@structured-world/gitlab-mcp 6.62.2 → 7.0.1
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/README.md +22 -1
- package/README.md.in +21 -0
- package/dist/generated/prisma/internal/class.js +2 -2
- package/dist/generated/prisma/internal/prismaNamespace.js +2 -2
- package/dist/generated/prisma/models/AuthCodeFlowState.d.ts +1 -2
- package/dist/generated/prisma/models/AuthorizationCode.d.ts +1 -2
- package/dist/generated/prisma/models/DeviceFlowState.d.ts +1 -2
- package/dist/generated/prisma/models/McpSessionMapping.d.ts +1 -2
- package/dist/generated/prisma/models/OAuthSession.d.ts +1 -2
- package/dist/src/cli/init/connection.js +11 -11
- package/dist/src/cli/init/connection.js.map +1 -1
- package/dist/src/cli/instances/instances-command.js +5 -1
- package/dist/src/cli/instances/instances-command.js.map +1 -1
- package/dist/src/config.d.ts +7 -0
- package/dist/src/config.js +37 -36
- package/dist/src/config.js.map +1 -1
- package/dist/src/entities/pipelines/schema-readonly.d.ts +1 -1
- package/dist/src/handlers.d.ts +1 -0
- package/dist/src/handlers.js +210 -38
- package/dist/src/handlers.js.map +1 -1
- package/dist/src/logging/types.d.ts +1 -1
- package/dist/src/logging/types.js.map +1 -1
- package/dist/src/middleware/index.d.ts +1 -0
- package/dist/src/middleware/index.js +3 -1
- package/dist/src/middleware/index.js.map +1 -1
- package/dist/src/middleware/response-write-timeout.d.ts +2 -0
- package/dist/src/middleware/response-write-timeout.js +62 -0
- package/dist/src/middleware/response-write-timeout.js.map +1 -0
- package/dist/src/oauth/gitlab-device-flow.js +31 -26
- package/dist/src/oauth/gitlab-device-flow.js.map +1 -1
- package/dist/src/registry-manager.d.ts +18 -10
- package/dist/src/registry-manager.js +198 -113
- package/dist/src/registry-manager.js.map +1 -1
- package/dist/src/server.js +37 -21
- package/dist/src/server.js.map +1 -1
- package/dist/src/services/ConnectionManager.d.ts +29 -20
- package/dist/src/services/ConnectionManager.js +265 -140
- package/dist/src/services/ConnectionManager.js.map +1 -1
- package/dist/src/services/HealthMonitor.d.ts +42 -0
- package/dist/src/services/HealthMonitor.js +544 -0
- package/dist/src/services/HealthMonitor.js.map +1 -0
- package/dist/src/services/InstanceRegistry.d.ts +0 -1
- package/dist/src/services/InstanceRegistry.js +9 -21
- package/dist/src/services/InstanceRegistry.js.map +1 -1
- package/dist/src/services/SchemaIntrospector.d.ts +1 -0
- package/dist/src/services/SchemaIntrospector.js +3 -0
- package/dist/src/services/SchemaIntrospector.js.map +1 -1
- package/dist/src/services/TokenScopeDetector.d.ts +2 -2
- package/dist/src/services/TokenScopeDetector.js +59 -36
- package/dist/src/services/TokenScopeDetector.js.map +1 -1
- package/dist/src/services/ToolAvailability.d.ts +9 -5
- package/dist/src/services/ToolAvailability.js +30 -10
- package/dist/src/services/ToolAvailability.js.map +1 -1
- package/dist/src/services/WidgetAvailability.d.ts +3 -3
- package/dist/src/services/WidgetAvailability.js +7 -6
- package/dist/src/services/WidgetAvailability.js.map +1 -1
- package/dist/src/utils/error-handler.d.ts +10 -1
- package/dist/src/utils/error-handler.js +85 -0
- package/dist/src/utils/error-handler.js.map +1 -1
- package/dist/src/utils/fetch.d.ts +7 -0
- package/dist/src/utils/fetch.js +53 -39
- package/dist/src/utils/fetch.js.map +1 -1
- package/dist/src/utils/gitlab-api.js +5 -2
- package/dist/src/utils/gitlab-api.js.map +1 -1
- package/dist/src/utils/url.d.ts +1 -0
- package/dist/src/utils/url.js +38 -0
- package/dist/src/utils/url.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +19 -25
- package/dist/structured-world-gitlab-mcp-6.62.2.tgz +0 -0
|
@@ -10,11 +10,26 @@ exports.exchangeGitLabAuthCode = exchangeGitLabAuthCode;
|
|
|
10
10
|
exports.buildGitLabAuthUrl = buildGitLabAuthUrl;
|
|
11
11
|
const config_1 = require("../config");
|
|
12
12
|
const logger_1 = require("../logger");
|
|
13
|
+
const fetch_1 = require("../utils/fetch");
|
|
14
|
+
async function throwOnHttpError(response, operation) {
|
|
15
|
+
if (!response.ok) {
|
|
16
|
+
const rawText = await response.text();
|
|
17
|
+
const details = rawText.trim().slice(0, 500) || response.statusText;
|
|
18
|
+
(0, logger_1.logError)(`Failed to ${operation}`, { status: response.status, error: details });
|
|
19
|
+
throw new Error(`Failed to ${operation}: ${response.status} ${details}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const OAUTH_FETCH_OPTS = {
|
|
23
|
+
retry: false,
|
|
24
|
+
rateLimit: false,
|
|
25
|
+
rateLimitBaseUrl: config_1.GITLAB_BASE_URL,
|
|
26
|
+
skipAuth: true,
|
|
27
|
+
};
|
|
13
28
|
async function initiateDeviceFlow(config) {
|
|
14
29
|
const url = `${config_1.GITLAB_BASE_URL}/oauth/authorize_device`;
|
|
15
30
|
(0, logger_1.logDebug)('Initiating GitLab device flow', { url, clientId: config.gitlabClientId });
|
|
16
31
|
const scopes = config.gitlabScopes.replace(/,/g, ' ');
|
|
17
|
-
const response = await
|
|
32
|
+
const response = await (0, fetch_1.enhancedFetch)(url, {
|
|
18
33
|
method: 'POST',
|
|
19
34
|
headers: {
|
|
20
35
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
@@ -24,12 +39,9 @@ async function initiateDeviceFlow(config) {
|
|
|
24
39
|
client_id: config.gitlabClientId,
|
|
25
40
|
scope: scopes,
|
|
26
41
|
}),
|
|
42
|
+
...OAUTH_FETCH_OPTS,
|
|
27
43
|
});
|
|
28
|
-
|
|
29
|
-
const errorText = await response.text();
|
|
30
|
-
(0, logger_1.logError)('Failed to initiate device flow', { status: response.status, error: errorText });
|
|
31
|
-
throw new Error(`Failed to initiate device flow: ${response.status} ${errorText}`);
|
|
32
|
-
}
|
|
44
|
+
await throwOnHttpError(response, 'initiate device flow');
|
|
33
45
|
const data = (await response.json());
|
|
34
46
|
(0, logger_1.logInfo)('Device flow initiated', {
|
|
35
47
|
userCode: data.user_code,
|
|
@@ -48,13 +60,14 @@ async function pollDeviceFlowOnce(deviceCode, config) {
|
|
|
48
60
|
if (config.gitlabClientSecret) {
|
|
49
61
|
params.client_secret = config.gitlabClientSecret;
|
|
50
62
|
}
|
|
51
|
-
const response = await
|
|
63
|
+
const response = await (0, fetch_1.enhancedFetch)(url, {
|
|
52
64
|
method: 'POST',
|
|
53
65
|
headers: {
|
|
54
66
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
55
67
|
Accept: 'application/json',
|
|
56
68
|
},
|
|
57
69
|
body: new URLSearchParams(params),
|
|
70
|
+
...OAUTH_FETCH_OPTS,
|
|
58
71
|
});
|
|
59
72
|
if (response.ok) {
|
|
60
73
|
const data = (await response.json());
|
|
@@ -115,36 +128,30 @@ async function refreshGitLabToken(refreshToken, config) {
|
|
|
115
128
|
params.client_secret = config.gitlabClientSecret;
|
|
116
129
|
}
|
|
117
130
|
(0, logger_1.logDebug)('Refreshing GitLab token');
|
|
118
|
-
const response = await
|
|
131
|
+
const response = await (0, fetch_1.enhancedFetch)(url, {
|
|
119
132
|
method: 'POST',
|
|
120
133
|
headers: {
|
|
121
134
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
122
135
|
Accept: 'application/json',
|
|
123
136
|
},
|
|
124
137
|
body: new URLSearchParams(params),
|
|
138
|
+
...OAUTH_FETCH_OPTS,
|
|
125
139
|
});
|
|
126
|
-
|
|
127
|
-
const errorText = await response.text();
|
|
128
|
-
(0, logger_1.logError)('Failed to refresh GitLab token', { status: response.status, error: errorText });
|
|
129
|
-
throw new Error(`Failed to refresh token: ${response.status} ${errorText}`);
|
|
130
|
-
}
|
|
140
|
+
await throwOnHttpError(response, 'refresh token');
|
|
131
141
|
const data = (await response.json());
|
|
132
142
|
(0, logger_1.logInfo)('GitLab token refreshed successfully');
|
|
133
143
|
return data;
|
|
134
144
|
}
|
|
135
145
|
async function getGitLabUser(accessToken) {
|
|
136
146
|
const url = `${config_1.GITLAB_BASE_URL}/api/v4/user`;
|
|
137
|
-
const response = await
|
|
147
|
+
const response = await (0, fetch_1.enhancedFetch)(url, {
|
|
138
148
|
headers: {
|
|
139
149
|
Authorization: `Bearer ${accessToken}`,
|
|
140
150
|
Accept: 'application/json',
|
|
141
151
|
},
|
|
152
|
+
...OAUTH_FETCH_OPTS,
|
|
142
153
|
});
|
|
143
|
-
|
|
144
|
-
const errorText = await response.text();
|
|
145
|
-
(0, logger_1.logError)('Failed to get GitLab user info', { status: response.status, error: errorText });
|
|
146
|
-
throw new Error(`Failed to get GitLab user info: ${response.status}`);
|
|
147
|
-
}
|
|
154
|
+
await throwOnHttpError(response, 'get GitLab user info');
|
|
148
155
|
const user = (await response.json());
|
|
149
156
|
(0, logger_1.logDebug)('Retrieved GitLab user info', { userId: user.id, username: user.username });
|
|
150
157
|
return {
|
|
@@ -157,11 +164,12 @@ async function getGitLabUser(accessToken) {
|
|
|
157
164
|
async function validateGitLabToken(accessToken) {
|
|
158
165
|
try {
|
|
159
166
|
const url = `${config_1.GITLAB_BASE_URL}/api/v4/user`;
|
|
160
|
-
const response = await
|
|
167
|
+
const response = await (0, fetch_1.enhancedFetch)(url, {
|
|
161
168
|
method: 'HEAD',
|
|
162
169
|
headers: {
|
|
163
170
|
Authorization: `Bearer ${accessToken}`,
|
|
164
171
|
},
|
|
172
|
+
...OAUTH_FETCH_OPTS,
|
|
165
173
|
});
|
|
166
174
|
return response.ok;
|
|
167
175
|
}
|
|
@@ -181,19 +189,16 @@ async function exchangeGitLabAuthCode(code, redirectUri, config) {
|
|
|
181
189
|
params.client_secret = config.gitlabClientSecret;
|
|
182
190
|
}
|
|
183
191
|
(0, logger_1.logDebug)('Exchanging GitLab authorization code for tokens', { redirectUri });
|
|
184
|
-
const response = await
|
|
192
|
+
const response = await (0, fetch_1.enhancedFetch)(url, {
|
|
185
193
|
method: 'POST',
|
|
186
194
|
headers: {
|
|
187
195
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
188
196
|
Accept: 'application/json',
|
|
189
197
|
},
|
|
190
198
|
body: new URLSearchParams(params),
|
|
199
|
+
...OAUTH_FETCH_OPTS,
|
|
191
200
|
});
|
|
192
|
-
|
|
193
|
-
const errorText = await response.text();
|
|
194
|
-
(0, logger_1.logError)('Failed to exchange GitLab auth code', { status: response.status, error: errorText });
|
|
195
|
-
throw new Error(`Failed to exchange authorization code: ${response.status} ${errorText}`);
|
|
196
|
-
}
|
|
201
|
+
await throwOnHttpError(response, 'exchange authorization code');
|
|
197
202
|
const data = (await response.json());
|
|
198
203
|
(0, logger_1.logInfo)('GitLab authorization code exchanged successfully');
|
|
199
204
|
return data;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gitlab-device-flow.js","sourceRoot":"","sources":["../../../src/oauth/gitlab-device-flow.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"gitlab-device-flow.js","sourceRoot":"","sources":["../../../src/oauth/gitlab-device-flow.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,9 +2,9 @@ import { EnhancedToolDefinition, ToolDefinition } from './types';
|
|
|
2
2
|
declare class RegistryManager {
|
|
3
3
|
private static instance;
|
|
4
4
|
private registries;
|
|
5
|
-
private
|
|
6
|
-
private
|
|
7
|
-
private
|
|
5
|
+
private readonly toolLookupCaches;
|
|
6
|
+
private readonly toolDefinitionsCaches;
|
|
7
|
+
private readonly toolNamesCaches;
|
|
8
8
|
private descriptionOverrides;
|
|
9
9
|
private readOnlyToolsCache;
|
|
10
10
|
private constructor();
|
|
@@ -13,17 +13,25 @@ declare class RegistryManager {
|
|
|
13
13
|
private loadDescriptionOverrides;
|
|
14
14
|
private buildReadOnlyToolsList;
|
|
15
15
|
private getReadOnlyTools;
|
|
16
|
+
private loadInstanceContext;
|
|
17
|
+
private getToolExclusionReason;
|
|
18
|
+
private buildFilteredTools;
|
|
19
|
+
private postProcessRelatedReferences;
|
|
20
|
+
private resolveCacheUrl;
|
|
21
|
+
private resolveCache;
|
|
16
22
|
private buildToolLookupCache;
|
|
17
23
|
private invalidateCaches;
|
|
18
|
-
getTool(toolName: string): EnhancedToolDefinition | null;
|
|
19
|
-
executeTool(toolName: string, args: unknown): Promise<unknown>;
|
|
20
|
-
refreshCache(): void;
|
|
21
|
-
getAllToolDefinitions(): ToolDefinition[];
|
|
24
|
+
getTool(toolName: string, instanceUrl?: string): EnhancedToolDefinition | null;
|
|
25
|
+
executeTool(toolName: string, args: unknown, instanceUrl?: string): Promise<unknown>;
|
|
26
|
+
refreshCache(instanceUrl?: string): void;
|
|
27
|
+
getAllToolDefinitions(instanceUrl?: string): ToolDefinition[];
|
|
22
28
|
getAllToolDefinitionsTierless(): EnhancedToolDefinition[];
|
|
23
29
|
getAllToolDefinitionsUnfiltered(): EnhancedToolDefinition[];
|
|
24
|
-
hasToolHandler(toolName: string): boolean;
|
|
25
|
-
getAvailableToolNames(): string[];
|
|
26
|
-
|
|
30
|
+
hasToolHandler(toolName: string, instanceUrl?: string): boolean;
|
|
31
|
+
getAvailableToolNames(instanceUrl?: string): string[];
|
|
32
|
+
private isUnreachableFor;
|
|
33
|
+
private aggregateFilterCounters;
|
|
34
|
+
getFilterStats(instanceUrl?: string): FilterStats;
|
|
27
35
|
}
|
|
28
36
|
export interface FilterStats {
|
|
29
37
|
available: number;
|
|
@@ -22,16 +22,19 @@ const registry_18 = require("./entities/iterations/registry");
|
|
|
22
22
|
const config_1 = require("./config");
|
|
23
23
|
const ToolAvailability_1 = require("./services/ToolAvailability");
|
|
24
24
|
const ConnectionManager_1 = require("./services/ConnectionManager");
|
|
25
|
+
const HealthMonitor_1 = require("./services/HealthMonitor");
|
|
25
26
|
const TokenScopeDetector_1 = require("./services/TokenScopeDetector");
|
|
26
27
|
const logger_1 = require("./logger");
|
|
27
28
|
const schema_utils_1 = require("./utils/schema-utils");
|
|
28
29
|
const description_utils_1 = require("./utils/description-utils");
|
|
30
|
+
const url_1 = require("./utils/url");
|
|
31
|
+
const token_context_1 = require("./oauth/token-context");
|
|
29
32
|
class RegistryManager {
|
|
30
33
|
static instance;
|
|
31
34
|
registries = new Map();
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
toolLookupCaches = new Map();
|
|
36
|
+
toolDefinitionsCaches = new Map();
|
|
37
|
+
toolNamesCaches = new Map();
|
|
35
38
|
descriptionOverrides = new Map();
|
|
36
39
|
readOnlyToolsCache = null;
|
|
37
40
|
constructor() {
|
|
@@ -164,51 +167,73 @@ class RegistryManager {
|
|
|
164
167
|
this.readOnlyToolsCache ??= this.buildReadOnlyToolsList();
|
|
165
168
|
return this.readOnlyToolsCache;
|
|
166
169
|
}
|
|
167
|
-
|
|
168
|
-
this.toolLookupCache.clear();
|
|
170
|
+
loadInstanceContext(instanceUrl) {
|
|
169
171
|
let instanceInfo;
|
|
170
172
|
try {
|
|
171
|
-
const info = ConnectionManager_1.ConnectionManager.getInstance().getInstanceInfo();
|
|
173
|
+
const info = ConnectionManager_1.ConnectionManager.getInstance().getInstanceInfo(instanceUrl);
|
|
172
174
|
instanceInfo = { tier: info.tier, version: info.version };
|
|
173
175
|
}
|
|
174
|
-
catch {
|
|
176
|
+
catch (err) {
|
|
177
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
178
|
+
if (!msg.includes('not initialized') &&
|
|
179
|
+
!msg.includes('not available') &&
|
|
180
|
+
!msg.includes('No connection')) {
|
|
181
|
+
(0, logger_1.logWarn)('Unexpected error loading instance info for tool cache', {
|
|
182
|
+
error: msg,
|
|
183
|
+
instanceUrl,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
175
186
|
}
|
|
176
187
|
let tokenScopes;
|
|
177
188
|
try {
|
|
178
|
-
const scopeInfo = ConnectionManager_1.ConnectionManager.getInstance().getTokenScopeInfo();
|
|
189
|
+
const scopeInfo = ConnectionManager_1.ConnectionManager.getInstance().getTokenScopeInfo(instanceUrl);
|
|
179
190
|
if (scopeInfo) {
|
|
180
191
|
tokenScopes = scopeInfo.scopes;
|
|
181
192
|
}
|
|
182
193
|
}
|
|
183
|
-
catch {
|
|
194
|
+
catch (err) {
|
|
195
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
196
|
+
if (!msg.includes('not initialized') &&
|
|
197
|
+
!msg.includes('not available') &&
|
|
198
|
+
!msg.includes('No connection')) {
|
|
199
|
+
(0, logger_1.logWarn)('Unexpected error loading token scopes for tool cache', {
|
|
200
|
+
error: msg,
|
|
201
|
+
instanceUrl,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
184
204
|
}
|
|
185
|
-
|
|
205
|
+
return { instanceInfo, tokenScopes };
|
|
206
|
+
}
|
|
207
|
+
getToolExclusionReason(toolName, tool, ctx) {
|
|
208
|
+
if (config_1.GITLAB_READ_ONLY_MODE && !this.getReadOnlyTools().includes(toolName))
|
|
209
|
+
return 'readOnly';
|
|
210
|
+
if (config_1.GITLAB_DENIED_TOOLS_REGEX?.test(toolName))
|
|
211
|
+
return 'deniedRegex';
|
|
212
|
+
if (ctx.tokenScopes && !(0, TokenScopeDetector_1.isToolAvailableForScopes)(toolName, ctx.tokenScopes))
|
|
213
|
+
return 'scopes';
|
|
214
|
+
const isContextTool = this.registries.get('context')?.has(toolName) ?? false;
|
|
215
|
+
if (!isContextTool &&
|
|
216
|
+
ctx.instanceInfo &&
|
|
217
|
+
ctx.instanceInfo.version !== 'unknown' &&
|
|
218
|
+
!ToolAvailability_1.ToolAvailability.isToolAvailableForInstance(toolName, ctx.instanceInfo))
|
|
219
|
+
return 'tier';
|
|
220
|
+
const allActions = (0, schema_utils_1.extractActionsFromSchema)(tool.inputSchema);
|
|
221
|
+
if (allActions.length > 0 && (0, schema_utils_1.shouldRemoveTool)(toolName, allActions))
|
|
222
|
+
return 'actionDenial';
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
buildFilteredTools(ctx) {
|
|
226
|
+
const result = new Map();
|
|
227
|
+
for (const [, registry] of this.registries) {
|
|
186
228
|
for (const [toolName, tool] of registry) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
if (config_1.GITLAB_DENIED_TOOLS_REGEX?.test(toolName)) {
|
|
192
|
-
(0, logger_1.logDebug)('Tool filtered out: matches denied regex', { toolName });
|
|
193
|
-
continue;
|
|
194
|
-
}
|
|
195
|
-
if (tokenScopes && !(0, TokenScopeDetector_1.isToolAvailableForScopes)(toolName, tokenScopes)) {
|
|
196
|
-
(0, logger_1.logDebug)('Tool filtered out: insufficient token scopes', { toolName });
|
|
197
|
-
continue;
|
|
198
|
-
}
|
|
199
|
-
if (!ToolAvailability_1.ToolAvailability.isToolAvailable(toolName)) {
|
|
200
|
-
const reason = ToolAvailability_1.ToolAvailability.getUnavailableReason(toolName);
|
|
201
|
-
(0, logger_1.logDebug)('Tool filtered out', { toolName, reason });
|
|
202
|
-
continue;
|
|
203
|
-
}
|
|
204
|
-
const allActions = (0, schema_utils_1.extractActionsFromSchema)(tool.inputSchema);
|
|
205
|
-
if (allActions.length > 0 && (0, schema_utils_1.shouldRemoveTool)(toolName, allActions)) {
|
|
206
|
-
(0, logger_1.logDebug)('Tool filtered out: all actions denied', { toolName });
|
|
229
|
+
const exclusion = this.getToolExclusionReason(toolName, tool, ctx);
|
|
230
|
+
if (exclusion) {
|
|
231
|
+
(0, logger_1.logDebug)('Tool filtered out', { toolName, reason: exclusion });
|
|
207
232
|
continue;
|
|
208
233
|
}
|
|
209
234
|
let transformedSchema = (0, schema_utils_1.transformToolSchema)(toolName, tool.inputSchema);
|
|
210
|
-
if (instanceInfo) {
|
|
211
|
-
const restrictedParams = ToolAvailability_1.ToolAvailability.getRestrictedParameters(toolName, instanceInfo);
|
|
235
|
+
if (ctx.instanceInfo && ctx.instanceInfo.version !== 'unknown') {
|
|
236
|
+
const restrictedParams = ToolAvailability_1.ToolAvailability.getRestrictedParameters(toolName, ctx.instanceInfo);
|
|
212
237
|
if (restrictedParams.length > 0) {
|
|
213
238
|
transformedSchema = (0, schema_utils_1.stripTierRestrictedParameters)(transformedSchema, restrictedParams);
|
|
214
239
|
}
|
|
@@ -222,65 +247,102 @@ class RegistryManager {
|
|
|
222
247
|
if (customDescription) {
|
|
223
248
|
(0, logger_1.logDebug)('Applied description override', { toolName, customDescription });
|
|
224
249
|
}
|
|
225
|
-
|
|
250
|
+
result.set(toolName, finalTool);
|
|
226
251
|
}
|
|
227
252
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
253
|
+
return result;
|
|
254
|
+
}
|
|
255
|
+
postProcessRelatedReferences(cache) {
|
|
256
|
+
const availableToolNames = config_1.GITLAB_CROSS_REFS ? new Set(cache.keys()) : undefined;
|
|
257
|
+
for (const [toolName, tool] of cache) {
|
|
258
|
+
if (this.descriptionOverrides.has(toolName))
|
|
259
|
+
continue;
|
|
260
|
+
const nextDescription = availableToolNames
|
|
261
|
+
? (0, description_utils_1.resolveRelatedReferences)(tool.description, availableToolNames)
|
|
262
|
+
: (0, description_utils_1.stripRelatedSection)(tool.description);
|
|
263
|
+
if (nextDescription !== tool.description) {
|
|
264
|
+
cache.set(toolName, { ...tool, description: nextDescription });
|
|
237
265
|
}
|
|
238
266
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
267
|
+
}
|
|
268
|
+
resolveCacheUrl(instanceUrl) {
|
|
269
|
+
if (instanceUrl)
|
|
270
|
+
return (0, url_1.normalizeInstanceUrl)(instanceUrl);
|
|
271
|
+
const contextUrl = (0, token_context_1.getGitLabApiUrlFromContext)();
|
|
272
|
+
if (contextUrl)
|
|
273
|
+
return (0, url_1.normalizeInstanceUrl)(contextUrl);
|
|
274
|
+
try {
|
|
275
|
+
const current = ConnectionManager_1.ConnectionManager.getInstance().getCurrentInstanceUrl();
|
|
276
|
+
if (current)
|
|
277
|
+
return (0, url_1.normalizeInstanceUrl)(current);
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
248
280
|
}
|
|
281
|
+
return (0, url_1.normalizeInstanceUrl)(config_1.GITLAB_BASE_URL);
|
|
282
|
+
}
|
|
283
|
+
resolveCache(instanceUrl) {
|
|
284
|
+
const url = this.resolveCacheUrl(instanceUrl);
|
|
285
|
+
let cache = this.toolLookupCaches.get(url);
|
|
286
|
+
if (!cache) {
|
|
287
|
+
this.buildToolLookupCache(url);
|
|
288
|
+
cache = this.toolLookupCaches.get(url);
|
|
289
|
+
}
|
|
290
|
+
return cache ?? new Map();
|
|
291
|
+
}
|
|
292
|
+
buildToolLookupCache(instanceUrl) {
|
|
293
|
+
const url = this.resolveCacheUrl(instanceUrl);
|
|
294
|
+
const ctx = this.loadInstanceContext(url);
|
|
295
|
+
const newCache = this.buildFilteredTools(ctx);
|
|
296
|
+
this.postProcessRelatedReferences(newCache);
|
|
297
|
+
this.toolLookupCaches.set(url, newCache);
|
|
249
298
|
(0, logger_1.logDebug)('Registry manager built cache after filtering', {
|
|
250
|
-
toolCount:
|
|
299
|
+
toolCount: newCache.size,
|
|
300
|
+
instanceUrl: url,
|
|
251
301
|
});
|
|
252
302
|
}
|
|
253
|
-
invalidateCaches() {
|
|
254
|
-
|
|
255
|
-
this.
|
|
256
|
-
this.
|
|
257
|
-
this.buildToolLookupCache();
|
|
303
|
+
invalidateCaches(instanceUrl) {
|
|
304
|
+
const url = this.resolveCacheUrl(instanceUrl);
|
|
305
|
+
this.toolDefinitionsCaches.delete(url);
|
|
306
|
+
this.toolNamesCaches.delete(url);
|
|
307
|
+
this.buildToolLookupCache(url);
|
|
258
308
|
}
|
|
259
|
-
getTool(toolName) {
|
|
260
|
-
return this.
|
|
309
|
+
getTool(toolName, instanceUrl) {
|
|
310
|
+
return this.resolveCache(instanceUrl).get(toolName) ?? null;
|
|
261
311
|
}
|
|
262
|
-
async executeTool(toolName, args) {
|
|
263
|
-
const tool = this.getTool(toolName);
|
|
312
|
+
async executeTool(toolName, args, instanceUrl) {
|
|
313
|
+
const tool = this.getTool(toolName, instanceUrl);
|
|
264
314
|
if (!tool) {
|
|
265
315
|
throw new Error(`Tool '${toolName}' not found in any registry`);
|
|
266
316
|
}
|
|
267
317
|
return await tool.handler(args);
|
|
268
318
|
}
|
|
269
|
-
refreshCache() {
|
|
270
|
-
this.invalidateCaches();
|
|
319
|
+
refreshCache(instanceUrl) {
|
|
320
|
+
this.invalidateCaches(instanceUrl);
|
|
271
321
|
}
|
|
272
|
-
getAllToolDefinitions() {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
322
|
+
getAllToolDefinitions(instanceUrl) {
|
|
323
|
+
const url = this.resolveCacheUrl(instanceUrl);
|
|
324
|
+
const cache = this.resolveCache(instanceUrl);
|
|
325
|
+
const unreachableMode = this.isUnreachableFor(instanceUrl);
|
|
326
|
+
const cachedDefs = this.toolDefinitionsCaches.get(url);
|
|
327
|
+
if (cachedDefs === undefined || unreachableMode) {
|
|
328
|
+
const contextTools = unreachableMode ? this.registries.get('context') : null;
|
|
329
|
+
const defs = [];
|
|
330
|
+
for (const tool of cache.values()) {
|
|
331
|
+
if (contextTools && !contextTools.has(tool.name))
|
|
332
|
+
continue;
|
|
333
|
+
defs.push({
|
|
277
334
|
name: tool.name,
|
|
278
335
|
description: tool.description,
|
|
279
336
|
inputSchema: tool.inputSchema,
|
|
280
337
|
});
|
|
281
338
|
}
|
|
339
|
+
if (unreachableMode) {
|
|
340
|
+
return defs;
|
|
341
|
+
}
|
|
342
|
+
this.toolDefinitionsCaches.set(url, defs);
|
|
343
|
+
return defs;
|
|
282
344
|
}
|
|
283
|
-
return
|
|
345
|
+
return cachedDefs;
|
|
284
346
|
}
|
|
285
347
|
getAllToolDefinitionsTierless() {
|
|
286
348
|
const allTools = [];
|
|
@@ -417,62 +479,85 @@ class RegistryManager {
|
|
|
417
479
|
}
|
|
418
480
|
return allTools;
|
|
419
481
|
}
|
|
420
|
-
hasToolHandler(toolName) {
|
|
421
|
-
return this.
|
|
482
|
+
hasToolHandler(toolName, instanceUrl) {
|
|
483
|
+
return this.resolveCache(instanceUrl).has(toolName);
|
|
422
484
|
}
|
|
423
|
-
getAvailableToolNames() {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
let filteredByReadOnly = 0;
|
|
434
|
-
let filteredByDeniedRegex = 0;
|
|
435
|
-
let filteredByScopes = 0;
|
|
436
|
-
let filteredByTier = 0;
|
|
437
|
-
let filteredByActionDenial = 0;
|
|
438
|
-
let tokenScopes;
|
|
439
|
-
try {
|
|
440
|
-
const scopeInfo = ConnectionManager_1.ConnectionManager.getInstance().getTokenScopeInfo();
|
|
441
|
-
if (scopeInfo) {
|
|
442
|
-
tokenScopes = scopeInfo.scopes;
|
|
485
|
+
getAvailableToolNames(instanceUrl) {
|
|
486
|
+
const url = this.resolveCacheUrl(instanceUrl);
|
|
487
|
+
const cache = this.resolveCache(instanceUrl);
|
|
488
|
+
const unreachableMode = this.isUnreachableFor(instanceUrl);
|
|
489
|
+
const cachedNames = this.toolNamesCaches.get(url);
|
|
490
|
+
if (cachedNames === undefined || unreachableMode) {
|
|
491
|
+
const contextTools = unreachableMode ? this.registries.get('context') : null;
|
|
492
|
+
const names = Array.from(cache.keys()).filter((name) => !contextTools || contextTools.has(name));
|
|
493
|
+
if (unreachableMode) {
|
|
494
|
+
return names;
|
|
443
495
|
}
|
|
496
|
+
this.toolNamesCaches.set(url, names);
|
|
497
|
+
return names;
|
|
444
498
|
}
|
|
445
|
-
|
|
499
|
+
return cachedNames;
|
|
500
|
+
}
|
|
501
|
+
isUnreachableFor(instanceUrl) {
|
|
502
|
+
const healthMonitor = HealthMonitor_1.HealthMonitor.getInstance();
|
|
503
|
+
if (instanceUrl) {
|
|
504
|
+
try {
|
|
505
|
+
return (!healthMonitor.isInstanceReachable(instanceUrl) &&
|
|
506
|
+
healthMonitor.getState(instanceUrl) !== 'connecting');
|
|
507
|
+
}
|
|
508
|
+
catch {
|
|
509
|
+
return false;
|
|
510
|
+
}
|
|
446
511
|
}
|
|
512
|
+
return (healthMonitor.getMonitoredInstances().length > 0 && !healthMonitor.isAnyInstanceHealthy());
|
|
513
|
+
}
|
|
514
|
+
aggregateFilterCounters(ctx, contextTools) {
|
|
515
|
+
const counts = {
|
|
516
|
+
available: 0,
|
|
517
|
+
byReadOnly: 0,
|
|
518
|
+
byDeniedRegex: 0,
|
|
519
|
+
byScopes: 0,
|
|
520
|
+
byTier: 0,
|
|
521
|
+
byActionDenial: 0,
|
|
522
|
+
};
|
|
523
|
+
const counterByReason = {
|
|
524
|
+
readOnly: 'byReadOnly',
|
|
525
|
+
deniedRegex: 'byDeniedRegex',
|
|
526
|
+
scopes: 'byScopes',
|
|
527
|
+
tier: 'byTier',
|
|
528
|
+
actionDenial: 'byActionDenial',
|
|
529
|
+
};
|
|
447
530
|
for (const registry of this.registries.values()) {
|
|
448
531
|
for (const [toolName, tool] of registry) {
|
|
449
|
-
if (
|
|
532
|
+
if (contextTools && !contextTools.has(toolName))
|
|
450
533
|
continue;
|
|
534
|
+
const reason = this.getToolExclusionReason(toolName, tool, ctx);
|
|
535
|
+
if (!reason) {
|
|
536
|
+
counts.available++;
|
|
451
537
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
if (config_1.GITLAB_DENIED_TOOLS_REGEX?.test(toolName)) {
|
|
457
|
-
filteredByDeniedRegex++;
|
|
458
|
-
continue;
|
|
459
|
-
}
|
|
460
|
-
if (tokenScopes && !(0, TokenScopeDetector_1.isToolAvailableForScopes)(toolName, tokenScopes)) {
|
|
461
|
-
filteredByScopes++;
|
|
462
|
-
continue;
|
|
463
|
-
}
|
|
464
|
-
if (!ToolAvailability_1.ToolAvailability.isToolAvailable(toolName)) {
|
|
465
|
-
filteredByTier++;
|
|
466
|
-
continue;
|
|
538
|
+
else {
|
|
539
|
+
const key = counterByReason[reason];
|
|
540
|
+
if (key)
|
|
541
|
+
counts[key]++;
|
|
467
542
|
}
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
return counts;
|
|
546
|
+
}
|
|
547
|
+
getFilterStats(instanceUrl) {
|
|
548
|
+
const url = this.resolveCacheUrl(instanceUrl);
|
|
549
|
+
const unreachableMode = this.isUnreachableFor(instanceUrl);
|
|
550
|
+
const contextTools = unreachableMode ? this.registries.get('context') : null;
|
|
551
|
+
let totalTools = 0;
|
|
552
|
+
for (const registry of this.registries.values()) {
|
|
553
|
+
for (const [toolName] of registry) {
|
|
554
|
+
if (contextTools && !contextTools.has(toolName))
|
|
471
555
|
continue;
|
|
472
|
-
|
|
473
|
-
filteredByTier++;
|
|
556
|
+
totalTools++;
|
|
474
557
|
}
|
|
475
558
|
}
|
|
559
|
+
const ctx = this.loadInstanceContext(url);
|
|
560
|
+
const { available: availableTools, byReadOnly: filteredByReadOnly, byDeniedRegex: filteredByDeniedRegex, byScopes: filteredByScopes, byTier: filteredByTier, byActionDenial: filteredByActionDenial, } = this.aggregateFilterCounters(ctx, contextTools);
|
|
476
561
|
return {
|
|
477
562
|
available: availableTools,
|
|
478
563
|
total: totalTools,
|