dbgate-api-premium 7.2.1 → 7.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dbgate-api-premium",
3
3
  "main": "src/index.js",
4
- "version": "7.2.1",
4
+ "version": "7.2.2",
5
5
  "homepage": "https://www.dbgate.io/",
6
6
  "repository": {
7
7
  "type": "git",
@@ -30,11 +30,11 @@
30
30
  "compare-versions": "^3.6.0",
31
31
  "cors": "^2.8.5",
32
32
  "cross-env": "^6.0.3",
33
- "dbgate-datalib": "7.2.1",
33
+ "dbgate-datalib": "7.2.2",
34
34
  "dbgate-query-splitter": "^4.12.0",
35
- "dbgate-rest": "7.2.1",
36
- "dbgate-sqltree": "7.2.1",
37
- "dbgate-tools": "7.2.1",
35
+ "dbgate-rest": "7.2.2",
36
+ "dbgate-sqltree": "7.2.2",
37
+ "dbgate-tools": "7.2.2",
38
38
  "debug": "^4.3.4",
39
39
  "diff": "^5.0.0",
40
40
  "diff2html": "^3.4.13",
@@ -96,7 +96,7 @@
96
96
  "@types/fs-extra": "^9.0.11",
97
97
  "@types/jest": "^30.0.0",
98
98
  "@types/lodash": "^4.14.149",
99
- "dbgate-types": "7.2.1",
99
+ "dbgate-types": "7.2.2",
100
100
  "env-cmd": "^10.1.0",
101
101
  "jest": "^30.4.2",
102
102
  "jsdoc-to-markdown": "^9.0.5",
@@ -348,6 +348,9 @@ function getDefaultAuthProvider() {
348
348
 
349
349
  function getAuthProviderFromReq(req) {
350
350
  const authProviderId = req?.auth?.amoid || req?.user?.amoid;
351
+ if (authProviderId == 'mcp') {
352
+ return require('./storageAuthProvider').getMcpAuthProvider();
353
+ }
351
354
  return getAuthProviderById(authProviderId) ?? getDefaultAuthProvider();
352
355
  }
353
356
 
@@ -90,6 +90,42 @@ class SuperadminAuthProvider extends AuthProviderBase {
90
90
  }
91
91
  }
92
92
 
93
+ class McpAuthProvider extends AuthProviderBase {
94
+ amoid = 'mcp';
95
+ skipInList = true;
96
+
97
+ async getCurrentPermissions() {
98
+ if (!process.env.STORAGE_DATABASE) return null;
99
+ return [...(getPredefinedPermissions('mcp') ?? []), ...(await storageReadRolePermissions(-4))];
100
+ }
101
+
102
+ async getCurrentDatabasePermissions() {
103
+ if (!process.env.STORAGE_DATABASE) return [];
104
+ return resolvePermissionConnectionIds(await readComplexRolePermissions(-4, 'role_databases'));
105
+ }
106
+
107
+ async getCurrentTablePermissions() {
108
+ if (!process.env.STORAGE_DATABASE) return [];
109
+ return resolvePermissionConnectionIds(await readComplexRolePermissions(-4, 'role_tables'));
110
+ }
111
+
112
+ async getCurrentFilePermissions() {
113
+ if (!process.env.STORAGE_DATABASE) return [];
114
+ return readComplexRolePermissions(-4, 'role_files');
115
+ }
116
+
117
+ async checkCurrentConnectionPermission(_req, conid) {
118
+ if (!process.env.STORAGE_DATABASE) return true;
119
+ return storageCheckRoleConnectionAccess(-4, conid);
120
+ }
121
+ }
122
+
123
+ const mcpAuthProvider = new McpAuthProvider();
124
+
125
+ function getMcpAuthProvider() {
126
+ return mcpAuthProvider;
127
+ }
128
+
93
129
  class StorageProviderBase extends AuthProviderBase {
94
130
  constructor(config) {
95
131
  super();
@@ -724,5 +760,6 @@ function createStorageAuthProvider(config) {
724
760
  module.exports = {
725
761
  createStorageAuthProvider,
726
762
  SuperadminAuthProvider,
763
+ getMcpAuthProvider,
727
764
  getBuiltinRoleIdFromRequest,
728
765
  };
@@ -28,10 +28,17 @@ const {
28
28
  markUserAsActive,
29
29
  markLoginAsLoggedOut,
30
30
  } = require('../utility/loginchecker');
31
+ const {
32
+ readMcpConfig,
33
+ getPublicBaseUrl,
34
+ getMcpResourceUrl,
35
+ setMcpIdentity,
36
+ safeTokenHashEquals,
37
+ } = require('../utility/mcpAuth');
31
38
 
32
39
  const logger = getLogger('auth');
33
40
 
34
- function unauthorizedResponse(req, res, text) {
41
+ function unauthorizedResponse(req, res, text, mcpAuthMode = null) {
35
42
  // if (req.path == getExpressPath('/config/get-settings')) {
36
43
  // return res.json({});
37
44
  // }
@@ -39,10 +46,70 @@ function unauthorizedResponse(req, res, text) {
39
46
  // return res.json([]);
40
47
  // }
41
48
 
49
+ if (req.path == getExpressPath('/mcp')) {
50
+ res.setHeader(
51
+ 'WWW-Authenticate',
52
+ mcpAuthMode == 'oauth'
53
+ ? `Bearer resource_metadata="${getPublicBaseUrl(req)}${getExpressPath('/.well-known/oauth-protected-resource')}"`
54
+ : 'Bearer'
55
+ );
56
+ }
57
+
42
58
  return res.status(401).send(text);
43
59
  }
44
60
 
45
- function authMiddleware(req, res, next) {
61
+ async function authenticateMcpRequest(req, res, next) {
62
+ const config = await readMcpConfig();
63
+ if (!config.enabled) {
64
+ return res.status(403).send('DBGM-00000 MCP server is disabled');
65
+ }
66
+
67
+ if (config.authMode == 'none') {
68
+ setMcpIdentity(req);
69
+ return next();
70
+ }
71
+
72
+ const authHeader = req.headers.authorization;
73
+ const [scheme, token] = authHeader?.split(' ') ?? [];
74
+ if (scheme != 'Bearer' || !token) {
75
+ return unauthorizedResponse(req, res, 'missing or invalid MCP authorization header', config.authMode);
76
+ }
77
+
78
+ if (config.authMode == 'token') {
79
+ if (!safeTokenHashEquals(token, config.tokenHash)) {
80
+ return unauthorizedResponse(req, res, 'invalid MCP token', config.authMode);
81
+ }
82
+ setMcpIdentity(req, { licenseUid: 'mcp-token', login: 'mcp-token' });
83
+ return next();
84
+ }
85
+
86
+ try {
87
+ const decoded = jwt.verify(token, getTokenSecret());
88
+ if (
89
+ decoded.tokenUse != 'mcp' ||
90
+ decoded.aud != getMcpResourceUrl(req) ||
91
+ decoded.oauthClientId != config.oauthClientId
92
+ ) {
93
+ throw new Error('Invalid MCP token claims');
94
+ }
95
+ setMcpIdentity(req, decoded);
96
+ markUserAsActive(decoded.licenseUid, token);
97
+ return next();
98
+ } catch (err) {
99
+ return unauthorizedResponse(req, res, 'invalid MCP OAuth token', config.authMode);
100
+ }
101
+ }
102
+
103
+ async function authMiddleware(req, res, next) {
104
+ if (req.path == getExpressPath('/mcp')) {
105
+ try {
106
+ return await authenticateMcpRequest(req, res, next);
107
+ } catch (err) {
108
+ logger.error(extractErrorLogData(err), 'DBGM-00000 Error loading MCP authentication configuration');
109
+ return res.status(500).send('DBGM-00000 Could not load MCP authentication configuration');
110
+ }
111
+ }
112
+
46
113
  const SKIP_AUTH_PATHS = [
47
114
  '/config/get',
48
115
  '/config/logout',
@@ -58,6 +125,14 @@ function authMiddleware(req, res, next) {
58
125
  '/storage/request-password-reset',
59
126
  '/storage/reset-password',
60
127
  '/auth/get-providers',
128
+ '/.well-known/oauth-protected-resource',
129
+ '/.well-known/oauth-protected-resource/mcp',
130
+ '/.well-known/oauth-authorization-server',
131
+ '/.well-known/openid-configuration',
132
+ '/authorize',
133
+ '/token',
134
+ '/mcp/oauth/authorize',
135
+ '/mcp/oauth/token',
61
136
  '/connections/dblogin-web',
62
137
  '/connections/dblogin-app',
63
138
  '/connections/dblogin-auth',
@@ -92,6 +167,9 @@ function authMiddleware(req, res, next) {
92
167
  const token = authHeader.split(' ')[1];
93
168
  try {
94
169
  const decoded = jwt.verify(token, getTokenSecret());
170
+ if (decoded.tokenUse == 'mcp') {
171
+ throw new Error('MCP token cannot be used for the DbGate API');
172
+ }
95
173
  req.user = decoded;
96
174
  markUserAsActive(decoded.licenseUid, token);
97
175
 
@@ -32,6 +32,12 @@ const {
32
32
  const lock = new AsyncLock();
33
33
  let cachedSettingsValue = null;
34
34
 
35
+ function coerceSettingsEnvValue(raw) {
36
+ if (raw === 'true') return true; // so booleans can be enabled AND disabled via env
37
+ if (raw === 'false') return false;
38
+ return raw; // leave everything else as a string (numeric settings are parsed on read)
39
+ }
40
+
35
41
  module.exports = {
36
42
  // settingsValue: {},
37
43
 
@@ -173,9 +179,12 @@ module.exports = {
173
179
  }
174
180
  for (const envVar in process.env) {
175
181
  if (envVar.startsWith('SETTINGS_')) {
176
- const key = envVar.substring('SETTINGS_'.length);
177
- if (!res[key]) {
178
- res[key] = process.env[envVar];
182
+ // dot-safe: allow `SETTINGS_tabGroup__showServerName` on platforms that forbid '.'
183
+ // in env-var names; `__` -> '.'. Existing dotted names are unaffected (no settings
184
+ // key contains '__').
185
+ const key = envVar.substring('SETTINGS_'.length).replace(/__/g, '.');
186
+ if (!(key in res)) {
187
+ res[key] = coerceSettingsEnvValue(process.env[envVar]);
179
188
  }
180
189
  }
181
190
  }
@@ -489,10 +489,17 @@ module.exports = {
489
489
 
490
490
  if (portalConnections) {
491
491
  const res = portalConnections.find(x => x._id == conid) || null;
492
- return mask && !platformInfo.allowShellConnection ? maskConnection(res) : encryptConnection(res);
492
+ if (res) {
493
+ return mask && !platformInfo.allowShellConnection ? maskConnection(res) : encryptConnection(res);
494
+ }
495
+ if (!(process.send && processArgs.processDisplayName === 'script')) {
496
+ return null;
497
+ }
498
+ }
499
+ if (!portalConnections) {
500
+ const res = await this.datastore.get(conid);
501
+ if (res) return res;
493
502
  }
494
- const res = await this.datastore.get(conid);
495
- if (res) return res;
496
503
 
497
504
  // In a forked runner-script child process, ask the parent for connections that may be
498
505
  // volatile (in-memory only, e.g. ask-for-password). We only do this when
@@ -247,6 +247,15 @@ module.exports = {
247
247
  return newOpened;
248
248
  },
249
249
 
250
+ async ensureStructureLoaded(conid, database) {
251
+ const conn = await this.ensureOpened(conid, database);
252
+ if (conn.isApiConnection || !conn.subprocess) {
253
+ return conn.structure ?? {};
254
+ }
255
+ const response = await this.sendRequest(conn, { msgtype: 'getStructure' });
256
+ return response.structure ?? conn.structure ?? {};
257
+ },
258
+
250
259
  /** @param {import('dbgate-types').OpenedDatabaseConnection} conn */
251
260
  sendRequest(conn, message, additionalData = {}) {
252
261
  const msgid = crypto.randomUUID();
@@ -0,0 +1,156 @@
1
+ const crypto = require('crypto');
2
+ const { isProApp } = require('../utility/checkLicense');
3
+ const { loadPermissionsFromRequest, hasPermission } = require('../utility/hasPermission');
4
+ const { sendToAuditLog } = require('../utility/auditlog');
5
+ const { encryptPasswordString, decryptPasswordString } = require('../utility/crypting');
6
+ const storage = require('./storage');
7
+ const {
8
+ MCP_AUTH_MODES,
9
+ hashMcpToken,
10
+ sanitizeMcpConfig,
11
+ getMcpResourceUrl,
12
+ } = require('../utility/mcpAuth');
13
+
14
+ async function requireMcpAdmin(req) {
15
+ if (!isProApp() || !process.env.STORAGE_DATABASE) {
16
+ throw new Error('DBGM-00000 MCP administration requires Team Premium');
17
+ }
18
+ const permissions = await loadPermissionsFromRequest(req);
19
+ if (!hasPermission('admin/settings', permissions)) {
20
+ throw new Error('DBGM-00000 Permission admin/settings not granted');
21
+ }
22
+ }
23
+
24
+ async function readStoredConfig() {
25
+ return sanitizeMcpConfig((await storage.readConfig({ group: 'mcp' })) || {});
26
+ }
27
+
28
+ function publicConfig(config, req) {
29
+ let mcpUrl = null;
30
+ let mcpUrlError = null;
31
+ let token = null;
32
+ let oauthClientSecret = null;
33
+ try {
34
+ mcpUrl = getMcpResourceUrl(req);
35
+ } catch (error) {
36
+ mcpUrlError = error.message;
37
+ }
38
+ try {
39
+ token = decryptPasswordString(config.tokenEncrypted) || null;
40
+ } catch (error) {
41
+ token = null;
42
+ }
43
+ try {
44
+ oauthClientSecret = decryptPasswordString(config.oauthClientSecretEncrypted) || null;
45
+ } catch (error) {
46
+ oauthClientSecret = null;
47
+ }
48
+ return {
49
+ enabled: config.enabled,
50
+ authMode: config.authMode,
51
+ hasToken: !!config.tokenHash,
52
+ token,
53
+ tokenSuffix: config.tokenSuffix,
54
+ tokenGeneratedAt: config.tokenGeneratedAt,
55
+ mcpUrl,
56
+ mcpUrlError,
57
+ oauthClientId: config.oauthClientId,
58
+ hasOAuthClientSecret: !!config.oauthClientSecretHash,
59
+ oauthClientSecret,
60
+ oauthClientSecretSuffix: config.oauthClientSecretSuffix,
61
+ oauthClientGeneratedAt: config.oauthClientGeneratedAt,
62
+ };
63
+ }
64
+
65
+ async function writeStoredConfig(config) {
66
+ await storage.writeConfig({ group: 'mcp', config });
67
+ }
68
+
69
+ module.exports = {
70
+ getConfig_meta: true,
71
+ async getConfig(_params, req) {
72
+ await requireMcpAdmin(req);
73
+ return publicConfig(await readStoredConfig(), req);
74
+ },
75
+
76
+ updateConfig_meta: true,
77
+ async updateConfig({ enabled, authMode }, req) {
78
+ await requireMcpAdmin(req);
79
+ if (typeof enabled !== 'boolean' || !MCP_AUTH_MODES.includes(authMode)) {
80
+ throw new Error('DBGM-00000 Invalid MCP configuration');
81
+ }
82
+
83
+ const current = await readStoredConfig();
84
+ const updated = { ...current, enabled, authMode };
85
+ await writeStoredConfig(updated);
86
+ sendToAuditLog(req, {
87
+ category: 'admin',
88
+ component: 'MCP',
89
+ action: 'configuration',
90
+ event: 'mcp.configurationChanged',
91
+ severity: 'info',
92
+ detail: { enabled, authMode },
93
+ message: 'MCP configuration changed',
94
+ });
95
+ return publicConfig(updated, req);
96
+ },
97
+
98
+ generateToken_meta: true,
99
+ async generateToken(_params, req) {
100
+ await requireMcpAdmin(req);
101
+ const token = `dbgate_mcp_${crypto.randomBytes(32).toString('base64url')}`;
102
+ const tokenSuffix = token.slice(-6);
103
+ const tokenGeneratedAt = new Date().toISOString();
104
+ const current = await readStoredConfig();
105
+ const updated = {
106
+ ...current,
107
+ tokenHash: hashMcpToken(token),
108
+ tokenEncrypted: encryptPasswordString(token),
109
+ tokenSuffix,
110
+ tokenGeneratedAt,
111
+ };
112
+ await writeStoredConfig(updated);
113
+ sendToAuditLog(req, {
114
+ category: 'admin',
115
+ component: 'MCP',
116
+ action: 'generateToken',
117
+ event: 'mcp.tokenGenerated',
118
+ severity: 'info',
119
+ message: 'MCP token generated',
120
+ });
121
+ return { token, tokenSuffix, tokenGeneratedAt };
122
+ },
123
+
124
+ generateOauthClient_meta: true,
125
+ async generateOauthClient(_params, req) {
126
+ await requireMcpAdmin(req);
127
+ const clientId = `dbgate_mcp_client_${crypto.randomBytes(24).toString('base64url')}`;
128
+ const clientSecret = `dbgate_mcp_secret_${crypto.randomBytes(32).toString('base64url')}`;
129
+ const clientSecretSuffix = clientSecret.slice(-6);
130
+ const clientGeneratedAt = new Date().toISOString();
131
+ const current = await readStoredConfig();
132
+ const updated = {
133
+ ...current,
134
+ oauthClientId: clientId,
135
+ oauthClientSecretHash: hashMcpToken(clientSecret),
136
+ oauthClientSecretEncrypted: encryptPasswordString(clientSecret),
137
+ oauthClientSecretSuffix: clientSecretSuffix,
138
+ oauthClientGeneratedAt: clientGeneratedAt,
139
+ };
140
+ await writeStoredConfig(updated);
141
+ sendToAuditLog(req, {
142
+ category: 'admin',
143
+ component: 'MCP',
144
+ action: 'generateOAuthClient',
145
+ event: 'mcp.oauthClientGenerated',
146
+ severity: 'info',
147
+ message: 'MCP OAuth client credentials generated',
148
+ });
149
+ return {
150
+ clientId,
151
+ clientSecret,
152
+ clientSecretSuffix,
153
+ clientGeneratedAt,
154
+ };
155
+ },
156
+ };
@@ -193,8 +193,12 @@ module.exports = {
193
193
  },
194
194
 
195
195
  readConfig_meta: true,
196
- async readConfig({ group }) {
197
- return await storageReadConfig(group);
196
+ async readConfig({ group }, req) {
197
+ const config = await storageReadConfig(group);
198
+ if (req && group == 'mcp') {
199
+ return _.omit(config, ['tokenHash']);
200
+ }
201
+ return config;
198
202
  },
199
203
 
200
204
  readAuthConfig_meta: true,
@@ -290,7 +294,10 @@ module.exports = {
290
294
  },
291
295
 
292
296
  writeConfig_meta: true,
293
- async writeConfig({ group, config }) {
297
+ async writeConfig({ group, config }, req) {
298
+ if (req && group == 'mcp') {
299
+ throw new Error('DBGM-00000 MCP configuration must be changed through MCP administration');
300
+ }
294
301
  await storageWriteConfig(group, config);
295
302
  await this._init();
296
303
  return null;
@@ -808,7 +815,7 @@ module.exports = {
808
815
  id
809
816
  );
810
817
 
811
- const basePermissions = getPredefinedPermissions(id < 0 && resp[0]?.name ? resp[0]?.name : 'logged-user');
818
+ const basePermissions = getPredefinedPermissions(id < 0 && resp[0]?.name ? resp[0]?.name : 'logged-user') ?? [];
812
819
  const permissions =
813
820
  id == 'new'
814
821
  ? []
@@ -931,18 +938,18 @@ module.exports = {
931
938
 
932
939
  const {
933
940
  name,
934
- usedConnections,
935
- usedUsers,
936
- usedTeamFilesRead,
937
- usedTeamFilesWrite,
938
- usedTeamFilesUse,
939
- permissions,
940
- databasePermissions,
941
- tablePermissions,
942
- filePermissions,
943
- usedTeamFoldersRead,
944
- usedTeamFoldersWrite,
945
- usedTeamFoldersUse,
941
+ usedConnections = [],
942
+ usedUsers = [],
943
+ usedTeamFilesRead = [],
944
+ usedTeamFilesWrite = [],
945
+ usedTeamFilesUse = [],
946
+ permissions = [],
947
+ databasePermissions = [],
948
+ tablePermissions = [],
949
+ filePermissions = [],
950
+ usedTeamFoldersRead = [],
951
+ usedTeamFoldersWrite = [],
952
+ usedTeamFoldersUse = [],
946
953
  } = role;
947
954
  let id = role.id;
948
955
 
@@ -1087,7 +1094,9 @@ module.exports = {
1087
1094
  const role_connections = await storageSelectFmt(`select * from ~role_connections`);
1088
1095
  const user_connections = await storageSelectFmt(`select * from ~user_connections`);
1089
1096
  const user_roles = await storageSelectFmt(`select * from ~user_roles`);
1090
- const config = await storageSelectFmt(`select * from ~config`);
1097
+ const config = (await storageSelectFmt(`select * from ~config`)).filter(
1098
+ item => item.group != 'mcp' || item.key != 'tokenHash'
1099
+ );
1091
1100
  const auth_methods_config = await storageSelectFmt(`select * from ~auth_methods_config`);
1092
1101
  const role_permissions = await storageSelectFmt(`select * from ~role_permissions`);
1093
1102
  const roles = await storageSelectFmt(`select * from ~roles`);
@@ -11,6 +11,7 @@ const {
11
11
  const _ = require('lodash');
12
12
  const logger = getLogger('storageDb');
13
13
  const { extractConnectionSslParams } = require('../utility/connectUtility');
14
+ const MCP_ROLE_ID = -4;
14
15
 
15
16
  async function getDbConnectionParams() {
16
17
  const server = process.env.STORAGE_SERVER;
@@ -553,6 +554,10 @@ async function storageCheckUserRoleConnectionAccess(userId, conid) {
553
554
  return respByUser.length > 0 || respByRole.length > 0 || respByLogged.length > 0;
554
555
  }
555
556
 
557
+ async function storageCheckMcpConnectionAccess(req, conid) {
558
+ return storageCheckRoleConnectionAccess(MCP_ROLE_ID, conid);
559
+ }
560
+
556
561
  async function storageCreateTeamFile({ fileType, file, data, ownerUserId, metadata, teamFolderId }) {
557
562
  const [conn, driver] = await getStorageConnection();
558
563
  if (!conn) {
@@ -1121,6 +1126,7 @@ module.exports = {
1121
1126
  resolvePermissionConnectionIds,
1122
1127
  storageCheckRoleConnectionAccess,
1123
1128
  storageCheckUserRoleConnectionAccess,
1129
+ storageCheckMcpConnectionAccess,
1124
1130
  storageCreateTeamFile,
1125
1131
  storageGetExistingFile,
1126
1132
  storageGetTeamFileRoleAccess,
@@ -1,5 +1,5 @@
1
1
 
2
2
  module.exports = {
3
- version: '7.2.1',
4
- buildTime: '2026-06-19T10:22:00.879Z'
3
+ version: '7.2.2',
4
+ buildTime: '2026-07-20T13:18:22.158Z'
5
5
  };
package/src/main.js CHANGED
@@ -31,6 +31,8 @@ const scheduler = require('./controllers/scheduler');
31
31
  const queryHistory = require('./controllers/queryHistory');
32
32
  const cloud = require('./controllers/cloud');
33
33
  const teamFiles = require('./controllers/teamFiles');
34
+ const mcpAdmin = require('./controllers/mcpAdmin');
35
+ const mcp = require('./mcp');
34
36
 
35
37
  const onFinished = require('on-finished');
36
38
  const processArgs = require('./utility/processArgs');
@@ -94,6 +96,7 @@ function start() {
94
96
  // console.log('process.argv', process.argv);
95
97
 
96
98
  const app = express();
99
+ app.set('trust proxy', 'loopback, linklocal, uniquelocal');
97
100
 
98
101
  const server = http.createServer(app);
99
102
 
@@ -175,6 +178,17 @@ function start() {
175
178
  });
176
179
 
177
180
  app.use(bodyParser.json({ limit: '50mb' }));
181
+ app.use(bodyParser.urlencoded({ extended: false }));
182
+
183
+ app.get(getExpressPath('/.well-known/oauth-protected-resource'), mcp.handleOAuthProtectedResourceMetadata);
184
+ app.get(getExpressPath('/.well-known/oauth-protected-resource/mcp'), mcp.handleOAuthProtectedResourceMetadata);
185
+ app.get(getExpressPath('/.well-known/oauth-authorization-server'), mcp.handleOAuthAuthorizationServerMetadata);
186
+ app.get(getExpressPath('/.well-known/openid-configuration'), mcp.handleOAuthAuthorizationServerMetadata);
187
+ app.get(getExpressPath('/authorize'), mcp.handleOAuthAuthorize);
188
+ app.post(getExpressPath('/token'), mcp.handleOAuthToken);
189
+ app.get(getExpressPath('/mcp/oauth/authorize'), mcp.handleOAuthAuthorize);
190
+ app.post(getExpressPath('/mcp/oauth/token'), mcp.handleOAuthToken);
191
+ app.post(getExpressPath('/mcp'), mcp.handleMcpRequest);
178
192
 
179
193
  app.use(
180
194
  getExpressPath('/uploads'),
@@ -269,6 +283,7 @@ function useAllControllers(app, electron) {
269
283
  useController(app, electron, '/cloud', cloud);
270
284
  useController(app, electron, '/team-files', teamFiles);
271
285
  useController(app, electron, '/rest-connections', restConnections);
286
+ useController(app, electron, '/mcp-admin', mcpAdmin);
272
287
  }
273
288
 
274
289
  function setElectronSender(electronSender) {