@phake/mcp 0.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/LICENSE +21 -0
- package/README.md +187 -0
- package/dist/adapters/http-node/http/app.d.ts +5 -0
- package/dist/adapters/http-node/http/auth-app.d.ts +5 -0
- package/dist/adapters/http-node/http/middlewares/auth.d.ts +39 -0
- package/dist/adapters/http-node/http/middlewares/cors.d.ts +8 -0
- package/dist/adapters/http-node/http/routes/health.d.ts +5 -0
- package/dist/adapters/http-node/http/routes/mcp.d.ts +11 -0
- package/dist/adapters/http-node/middleware.security.d.ts +6 -0
- package/dist/adapters/http-node/routes.discovery.d.ts +6 -0
- package/dist/adapters/http-node/routes.oauth.d.ts +7 -0
- package/dist/adapters/http-worker/index.d.ts +48 -0
- package/dist/adapters/http-worker/mcp.handler.d.ts +24 -0
- package/dist/adapters/http-worker/routes.discovery.d.ts +7 -0
- package/dist/adapters/http-worker/routes.oauth.d.ts +8 -0
- package/dist/adapters/http-worker/security.d.ts +7 -0
- package/dist/index-1zyem3xr.js +14893 -0
- package/dist/index-4f4xvtt9.js +19552 -0
- package/dist/index-sbqy8kgq.js +3478 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +1083 -0
- package/dist/mcp-server.d.ts +18 -0
- package/dist/runtime/node/capabilities.d.ts +2 -0
- package/dist/runtime/node/context.d.ts +29 -0
- package/dist/runtime/node/index.d.ts +5 -0
- package/dist/runtime/node/index.js +27 -0
- package/dist/runtime/node/mcp.d.ts +28 -0
- package/dist/runtime/node/storage/file.d.ts +44 -0
- package/dist/runtime/node/storage/sqlite.d.ts +213 -0
- package/dist/runtime/worker/index.d.ts +1 -0
- package/dist/runtime/worker/index.js +12 -0
- package/dist/shared/auth/index.d.ts +1 -0
- package/dist/shared/auth/strategy.d.ts +71 -0
- package/dist/shared/config/env.d.ts +52 -0
- package/dist/shared/config/index.d.ts +2 -0
- package/dist/shared/config/metadata.d.ts +5 -0
- package/dist/shared/crypto/aes-gcm.d.ts +37 -0
- package/dist/shared/crypto/index.d.ts +1 -0
- package/dist/shared/http/cors.d.ts +20 -0
- package/dist/shared/http/index.d.ts +2 -0
- package/dist/shared/http/response.d.ts +52 -0
- package/dist/shared/mcp/dispatcher.d.ts +81 -0
- package/dist/shared/mcp/index.d.ts +3 -0
- package/dist/shared/mcp/security.d.ts +23 -0
- package/dist/shared/mcp/server-internals.d.ts +79 -0
- package/dist/shared/oauth/cimd.d.ts +43 -0
- package/dist/shared/oauth/discovery-handlers.d.ts +14 -0
- package/dist/shared/oauth/discovery.d.ts +26 -0
- package/dist/shared/oauth/endpoints.d.ts +11 -0
- package/dist/shared/oauth/flow.d.ts +31 -0
- package/dist/shared/oauth/index.d.ts +9 -0
- package/dist/shared/oauth/input-parsers.d.ts +43 -0
- package/dist/shared/oauth/refresh.d.ts +61 -0
- package/dist/shared/oauth/ssrf.d.ts +31 -0
- package/dist/shared/oauth/types.d.ts +78 -0
- package/dist/shared/schemas/prompts.d.ts +1 -0
- package/dist/shared/services/http-client.d.ts +16 -0
- package/dist/shared/services/index.d.ts +1 -0
- package/dist/shared/storage/index.d.ts +4 -0
- package/dist/shared/storage/interface.d.ts +99 -0
- package/dist/shared/storage/kv.d.ts +68 -0
- package/dist/shared/storage/memory.d.ts +91 -0
- package/dist/shared/storage/singleton.d.ts +4 -0
- package/dist/shared/tools/echo.d.ts +16 -0
- package/dist/shared/tools/health.d.ts +13 -0
- package/dist/shared/tools/index.d.ts +4 -0
- package/dist/shared/tools/registry.d.ts +64 -0
- package/dist/shared/tools/types.d.ts +161 -0
- package/dist/shared/types/auth.d.ts +35 -0
- package/dist/shared/types/context.d.ts +79 -0
- package/dist/shared/types/index.d.ts +8 -0
- package/dist/shared/types/provider.d.ts +28 -0
- package/dist/shared/utils/base64.d.ts +12 -0
- package/dist/shared/utils/cancellation.d.ts +13 -0
- package/dist/shared/utils/elicitation.d.ts +247 -0
- package/dist/shared/utils/formatting.d.ts +106 -0
- package/dist/shared/utils/index.d.ts +11 -0
- package/dist/shared/utils/limits.d.ts +6 -0
- package/dist/shared/utils/logger.d.ts +20 -0
- package/dist/shared/utils/pagination.d.ts +11 -0
- package/dist/shared/utils/progress.d.ts +56 -0
- package/dist/shared/utils/roots.d.ts +62 -0
- package/dist/shared/utils/sampling.d.ts +155 -0
- package/dist/shared/utils/security.d.ts +6 -0
- package/package.json +55 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1083 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
FileTokenStore,
|
|
4
|
+
JSON_RPC_METHOD_NOT_FOUND,
|
|
5
|
+
SqliteSessionStore,
|
|
6
|
+
buildServer,
|
|
7
|
+
getLowLevelServer,
|
|
8
|
+
getServerWithInternals,
|
|
9
|
+
isJsonRpcError,
|
|
10
|
+
sessions
|
|
11
|
+
} from "./index-1zyem3xr.js";
|
|
12
|
+
import {
|
|
13
|
+
ClientMetadataSchema,
|
|
14
|
+
JsonRpcErrorCode,
|
|
15
|
+
KvSessionStore,
|
|
16
|
+
KvTokenStore,
|
|
17
|
+
LATEST_PROTOCOL_VERSION,
|
|
18
|
+
SUPPORTED_PROTOCOL_VERSIONS,
|
|
19
|
+
assertSsrfSafe,
|
|
20
|
+
buildAuthorizationServerMetadata,
|
|
21
|
+
buildCorsHeaders,
|
|
22
|
+
buildFlowOptions,
|
|
23
|
+
buildOAuthConfig,
|
|
24
|
+
buildProtectedResourceMetadata,
|
|
25
|
+
buildProviderConfig,
|
|
26
|
+
buildProviderRefreshConfig,
|
|
27
|
+
buildTokenInput,
|
|
28
|
+
buildUnauthorizedChallenge,
|
|
29
|
+
checkSsrfSafe,
|
|
30
|
+
corsPreflightResponse,
|
|
31
|
+
createDiscoveryHandlers,
|
|
32
|
+
createWorkerRouter,
|
|
33
|
+
dispatchMcpMethod,
|
|
34
|
+
ensureFreshToken,
|
|
35
|
+
fetchClientMetadata,
|
|
36
|
+
generateOpaqueToken,
|
|
37
|
+
getLogLevel,
|
|
38
|
+
getSessionStore,
|
|
39
|
+
getTokenStore,
|
|
40
|
+
handleAuthorize,
|
|
41
|
+
handleMcpNotification,
|
|
42
|
+
handleProviderCallback,
|
|
43
|
+
handleRegister,
|
|
44
|
+
handleRevoke,
|
|
45
|
+
handleToken,
|
|
46
|
+
initializeStorage,
|
|
47
|
+
initializeWorkerStorage,
|
|
48
|
+
isClientIdUrl,
|
|
49
|
+
isSsrfSafe,
|
|
50
|
+
isTokenExpiredOrExpiring,
|
|
51
|
+
nodeDiscoveryStrategy,
|
|
52
|
+
parseAuthorizeInput,
|
|
53
|
+
parseCallbackInput,
|
|
54
|
+
parseTokenInput,
|
|
55
|
+
refreshProviderToken,
|
|
56
|
+
shimProcessEnv,
|
|
57
|
+
validateOrigin,
|
|
58
|
+
validateProtocolVersion,
|
|
59
|
+
validateRedirectUri,
|
|
60
|
+
withCors,
|
|
61
|
+
workerDiscoveryStrategy
|
|
62
|
+
} from "./index-sbqy8kgq.js";
|
|
63
|
+
import {
|
|
64
|
+
CancellationError,
|
|
65
|
+
CancellationToken,
|
|
66
|
+
MAX_SESSIONS_PER_API_KEY,
|
|
67
|
+
MemorySessionStore,
|
|
68
|
+
MemoryTokenStore,
|
|
69
|
+
assertProviderToken,
|
|
70
|
+
authContextStorage,
|
|
71
|
+
base64Decode,
|
|
72
|
+
base64Encode,
|
|
73
|
+
base64UrlDecode,
|
|
74
|
+
base64UrlDecodeJson,
|
|
75
|
+
base64UrlDecodeString,
|
|
76
|
+
base64UrlEncode,
|
|
77
|
+
base64UrlEncodeJson,
|
|
78
|
+
base64UrlEncodeString,
|
|
79
|
+
buildCapabilities,
|
|
80
|
+
contextRegistry,
|
|
81
|
+
createCancellationToken,
|
|
82
|
+
createEncryptor,
|
|
83
|
+
decrypt,
|
|
84
|
+
defineTool,
|
|
85
|
+
echoInputSchema,
|
|
86
|
+
echoTool,
|
|
87
|
+
encrypt,
|
|
88
|
+
executeSharedTool,
|
|
89
|
+
exports_external,
|
|
90
|
+
generateKey,
|
|
91
|
+
getCurrentAuthContext,
|
|
92
|
+
getSharedTool,
|
|
93
|
+
getSharedToolNames,
|
|
94
|
+
healthInputSchema,
|
|
95
|
+
healthTool,
|
|
96
|
+
logger,
|
|
97
|
+
registerTools,
|
|
98
|
+
sharedLogger,
|
|
99
|
+
sharedTools,
|
|
100
|
+
startContextCleanup,
|
|
101
|
+
stopContextCleanup,
|
|
102
|
+
toProviderInfo,
|
|
103
|
+
toProviderTokens,
|
|
104
|
+
withCancellation
|
|
105
|
+
} from "./index-4f4xvtt9.js";
|
|
106
|
+
// src/shared/config/env.ts
|
|
107
|
+
function parseBoolean(value) {
|
|
108
|
+
return String(value || "false").toLowerCase() === "true";
|
|
109
|
+
}
|
|
110
|
+
function parseNumber(value, defaultValue) {
|
|
111
|
+
const num = Number(value);
|
|
112
|
+
return Number.isFinite(num) ? num : defaultValue;
|
|
113
|
+
}
|
|
114
|
+
function parseStringArray(value) {
|
|
115
|
+
return String(value || "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
116
|
+
}
|
|
117
|
+
function parseAuthStrategy(env) {
|
|
118
|
+
const explicit = env.AUTH_STRATEGY?.toLowerCase();
|
|
119
|
+
if (explicit && ["oauth", "bearer", "api_key", "custom", "none"].includes(explicit)) {
|
|
120
|
+
return explicit;
|
|
121
|
+
}
|
|
122
|
+
if (parseBoolean(env.AUTH_ENABLED)) {
|
|
123
|
+
return "oauth";
|
|
124
|
+
}
|
|
125
|
+
if (env.API_KEY) {
|
|
126
|
+
return "api_key";
|
|
127
|
+
}
|
|
128
|
+
if (env.BEARER_TOKEN) {
|
|
129
|
+
return "bearer";
|
|
130
|
+
}
|
|
131
|
+
return "none";
|
|
132
|
+
}
|
|
133
|
+
function parseConfig(env) {
|
|
134
|
+
const authStrategy = parseAuthStrategy(env);
|
|
135
|
+
return {
|
|
136
|
+
HOST: String(env.HOST || "127.0.0.1"),
|
|
137
|
+
PORT: parseNumber(env.PORT, 3000),
|
|
138
|
+
NODE_ENV: env.NODE_ENV || "development",
|
|
139
|
+
MCP_TITLE: String(env.MCP_TITLE || "MCP Server Template"),
|
|
140
|
+
MCP_INSTRUCTIONS: String(env.MCP_INSTRUCTIONS || "Use these tools responsibly. Prefer minimal scopes and small page sizes."),
|
|
141
|
+
MCP_VERSION: String(env.MCP_VERSION || "0.1.0"),
|
|
142
|
+
MCP_PROTOCOL_VERSION: String(env.MCP_PROTOCOL_VERSION || "2025-06-18"),
|
|
143
|
+
MCP_ACCEPT_HEADERS: parseStringArray(env.MCP_ACCEPT_HEADERS),
|
|
144
|
+
AUTH_STRATEGY: authStrategy,
|
|
145
|
+
AUTH_ENABLED: authStrategy === "oauth" || parseBoolean(env.AUTH_ENABLED),
|
|
146
|
+
AUTH_REQUIRE_RS: parseBoolean(env.AUTH_REQUIRE_RS),
|
|
147
|
+
AUTH_ALLOW_DIRECT_BEARER: parseBoolean(env.AUTH_ALLOW_DIRECT_BEARER),
|
|
148
|
+
AUTH_RESOURCE_URI: env.AUTH_RESOURCE_URI,
|
|
149
|
+
AUTH_DISCOVERY_URL: env.AUTH_DISCOVERY_URL,
|
|
150
|
+
API_KEY: env.API_KEY,
|
|
151
|
+
API_KEY_HEADER: String(env.API_KEY_HEADER || "x-api-key"),
|
|
152
|
+
BEARER_TOKEN: env.BEARER_TOKEN,
|
|
153
|
+
CUSTOM_HEADERS: env.CUSTOM_HEADERS,
|
|
154
|
+
OAUTH_CLIENT_ID: env.OAUTH_CLIENT_ID,
|
|
155
|
+
OAUTH_CLIENT_SECRET: env.OAUTH_CLIENT_SECRET,
|
|
156
|
+
OAUTH_SCOPES: String(env.OAUTH_SCOPES || ""),
|
|
157
|
+
OAUTH_AUTHORIZATION_URL: env.OAUTH_AUTHORIZATION_URL,
|
|
158
|
+
OAUTH_TOKEN_URL: env.OAUTH_TOKEN_URL,
|
|
159
|
+
OAUTH_REVOCATION_URL: env.OAUTH_REVOCATION_URL,
|
|
160
|
+
OAUTH_REDIRECT_URI: String(env.OAUTH_REDIRECT_URI || "http://localhost:3000/callback"),
|
|
161
|
+
OAUTH_REDIRECT_ALLOWLIST: parseStringArray(env.OAUTH_REDIRECT_ALLOWLIST),
|
|
162
|
+
OAUTH_REDIRECT_ALLOW_ALL: parseBoolean(env.OAUTH_REDIRECT_ALLOW_ALL),
|
|
163
|
+
OAUTH_EXTRA_AUTH_PARAMS: env.OAUTH_EXTRA_AUTH_PARAMS,
|
|
164
|
+
CIMD_ENABLED: parseBoolean(env.CIMD_ENABLED ?? "true"),
|
|
165
|
+
CIMD_FETCH_TIMEOUT_MS: parseNumber(env.CIMD_FETCH_TIMEOUT_MS, 5000),
|
|
166
|
+
CIMD_MAX_RESPONSE_BYTES: parseNumber(env.CIMD_MAX_RESPONSE_BYTES, 65536),
|
|
167
|
+
CIMD_ALLOWED_DOMAINS: parseStringArray(env.CIMD_ALLOWED_DOMAINS),
|
|
168
|
+
PROVIDER_CLIENT_ID: env.PROVIDER_CLIENT_ID?.trim(),
|
|
169
|
+
PROVIDER_CLIENT_SECRET: env.PROVIDER_CLIENT_SECRET?.trim(),
|
|
170
|
+
PROVIDER_API_URL: env.PROVIDER_API_URL,
|
|
171
|
+
PROVIDER_ACCOUNTS_URL: env.PROVIDER_ACCOUNTS_URL,
|
|
172
|
+
BASE_URL: env.BASE_URL,
|
|
173
|
+
RS_TOKENS_FILE: env.RS_TOKENS_FILE,
|
|
174
|
+
RS_TOKENS_ENC_KEY: env.RS_TOKENS_ENC_KEY,
|
|
175
|
+
RPS_LIMIT: parseNumber(env.RPS_LIMIT, 10),
|
|
176
|
+
CONCURRENCY_LIMIT: parseNumber(env.CONCURRENCY_LIMIT, 5),
|
|
177
|
+
LOG_LEVEL: env.LOG_LEVEL || "info"
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// src/mcp-server.ts
|
|
182
|
+
function createMCPServer(options) {
|
|
183
|
+
if (options.adapter === "worker") {
|
|
184
|
+
return createWorkerServer(options.tools);
|
|
185
|
+
}
|
|
186
|
+
throw new Error(`Adapter '${options.adapter}' not supported yet`);
|
|
187
|
+
}
|
|
188
|
+
function createWorkerServer(tools) {
|
|
189
|
+
return {
|
|
190
|
+
async fetch(request, env) {
|
|
191
|
+
shimProcessEnv(env);
|
|
192
|
+
const config = parseConfig(env);
|
|
193
|
+
const storage = initializeWorkerStorage(env, config);
|
|
194
|
+
if (!storage) {
|
|
195
|
+
return withCors(new Response("Server misconfigured: Storage unavailable", {
|
|
196
|
+
status: 503
|
|
197
|
+
}));
|
|
198
|
+
}
|
|
199
|
+
const router = createWorkerRouter({
|
|
200
|
+
tokenStore: storage.tokenStore,
|
|
201
|
+
sessionStore: storage.sessionStore,
|
|
202
|
+
config,
|
|
203
|
+
tools
|
|
204
|
+
});
|
|
205
|
+
return router.fetch(request);
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
// src/shared/auth/strategy.ts
|
|
210
|
+
function parseCustomHeaders(value) {
|
|
211
|
+
if (!value)
|
|
212
|
+
return {};
|
|
213
|
+
const headers = {};
|
|
214
|
+
const pairs = value.split(",");
|
|
215
|
+
for (const pair of pairs) {
|
|
216
|
+
const colonIndex = pair.indexOf(":");
|
|
217
|
+
if (colonIndex === -1)
|
|
218
|
+
continue;
|
|
219
|
+
const key = pair.slice(0, colonIndex).trim();
|
|
220
|
+
const val = pair.slice(colonIndex + 1).trim();
|
|
221
|
+
if (key && val) {
|
|
222
|
+
headers[key] = val;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return headers;
|
|
226
|
+
}
|
|
227
|
+
function parseAuthStrategy2(env) {
|
|
228
|
+
const strategy = env.AUTH_STRATEGY?.toLowerCase();
|
|
229
|
+
switch (strategy) {
|
|
230
|
+
case "api_key":
|
|
231
|
+
return {
|
|
232
|
+
type: "api_key",
|
|
233
|
+
headerName: env.API_KEY_HEADER || "x-api-key",
|
|
234
|
+
value: env.API_KEY
|
|
235
|
+
};
|
|
236
|
+
case "bearer":
|
|
237
|
+
return {
|
|
238
|
+
type: "bearer",
|
|
239
|
+
value: env.BEARER_TOKEN
|
|
240
|
+
};
|
|
241
|
+
case "custom":
|
|
242
|
+
return {
|
|
243
|
+
type: "custom",
|
|
244
|
+
customHeaders: parseCustomHeaders(env.CUSTOM_HEADERS)
|
|
245
|
+
};
|
|
246
|
+
case "none":
|
|
247
|
+
return { type: "none" };
|
|
248
|
+
default:
|
|
249
|
+
return { type: "oauth" };
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
function buildAuthHeaders(strategyConfig) {
|
|
253
|
+
const headers = {};
|
|
254
|
+
switch (strategyConfig.type) {
|
|
255
|
+
case "api_key":
|
|
256
|
+
if (strategyConfig.value && strategyConfig.headerName) {
|
|
257
|
+
headers[strategyConfig.headerName] = strategyConfig.value;
|
|
258
|
+
}
|
|
259
|
+
break;
|
|
260
|
+
case "bearer":
|
|
261
|
+
if (strategyConfig.value) {
|
|
262
|
+
headers.Authorization = `Bearer ${strategyConfig.value}`;
|
|
263
|
+
}
|
|
264
|
+
break;
|
|
265
|
+
case "custom":
|
|
266
|
+
if (strategyConfig.customHeaders) {
|
|
267
|
+
Object.assign(headers, strategyConfig.customHeaders);
|
|
268
|
+
}
|
|
269
|
+
break;
|
|
270
|
+
case "oauth":
|
|
271
|
+
case "none":
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
return headers;
|
|
275
|
+
}
|
|
276
|
+
function resolveStaticAuth(strategyConfig) {
|
|
277
|
+
const headers = buildAuthHeaders(strategyConfig);
|
|
278
|
+
return {
|
|
279
|
+
strategy: strategyConfig.type,
|
|
280
|
+
headers,
|
|
281
|
+
accessToken: strategyConfig.type === "bearer" ? strategyConfig.value : undefined
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
function mergeAuthHeaders(incoming, strategy) {
|
|
285
|
+
return {
|
|
286
|
+
...incoming,
|
|
287
|
+
...strategy
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
function isOAuthStrategy(config) {
|
|
291
|
+
return config.type === "oauth";
|
|
292
|
+
}
|
|
293
|
+
function requiresAuth(config) {
|
|
294
|
+
return config.type !== "none";
|
|
295
|
+
}
|
|
296
|
+
function validateAuthConfig(config) {
|
|
297
|
+
const errors = [];
|
|
298
|
+
switch (config.type) {
|
|
299
|
+
case "api_key":
|
|
300
|
+
if (!config.value) {
|
|
301
|
+
errors.push("API_KEY is required when AUTH_STRATEGY=api_key");
|
|
302
|
+
}
|
|
303
|
+
break;
|
|
304
|
+
case "bearer":
|
|
305
|
+
if (!config.value) {
|
|
306
|
+
errors.push("BEARER_TOKEN is required when AUTH_STRATEGY=bearer");
|
|
307
|
+
}
|
|
308
|
+
break;
|
|
309
|
+
case "custom":
|
|
310
|
+
if (!config.customHeaders || Object.keys(config.customHeaders).length === 0) {
|
|
311
|
+
errors.push("CUSTOM_HEADERS is required when AUTH_STRATEGY=custom");
|
|
312
|
+
}
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
return errors;
|
|
316
|
+
}
|
|
317
|
+
// src/shared/utils/limits.ts
|
|
318
|
+
var makeTokenBucket = (capacity, refillPerSec) => {
|
|
319
|
+
let tokens = capacity;
|
|
320
|
+
let last = Date.now();
|
|
321
|
+
const refillLoop = () => {
|
|
322
|
+
const now = Date.now();
|
|
323
|
+
const delta = (now - last) / 1000;
|
|
324
|
+
last = now;
|
|
325
|
+
tokens = Math.min(capacity, tokens + delta * refillPerSec);
|
|
326
|
+
};
|
|
327
|
+
return {
|
|
328
|
+
take(n = 1) {
|
|
329
|
+
refillLoop();
|
|
330
|
+
if (tokens >= n) {
|
|
331
|
+
tokens -= n;
|
|
332
|
+
return true;
|
|
333
|
+
}
|
|
334
|
+
return false;
|
|
335
|
+
},
|
|
336
|
+
refill(n = capacity) {
|
|
337
|
+
tokens = Math.min(capacity, tokens + n);
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
};
|
|
341
|
+
var makeConcurrencyGate = (max) => {
|
|
342
|
+
let active = 0;
|
|
343
|
+
const queue = [];
|
|
344
|
+
return async (fn) => {
|
|
345
|
+
if (active >= max) {
|
|
346
|
+
await new Promise((resolve) => queue.push(resolve));
|
|
347
|
+
}
|
|
348
|
+
active++;
|
|
349
|
+
try {
|
|
350
|
+
return await fn();
|
|
351
|
+
} finally {
|
|
352
|
+
active--;
|
|
353
|
+
const next = queue.shift();
|
|
354
|
+
if (next)
|
|
355
|
+
next();
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
// src/shared/services/http-client.ts
|
|
361
|
+
var DEFAULT_RPS = 10;
|
|
362
|
+
var DEFAULT_CONCURRENCY = 5;
|
|
363
|
+
function createHttpClient(options = {}) {
|
|
364
|
+
const {
|
|
365
|
+
baseHeaders = {},
|
|
366
|
+
timeout = 30000,
|
|
367
|
+
retries = 3,
|
|
368
|
+
retryDelay = 1000,
|
|
369
|
+
rateLimit = { rps: DEFAULT_RPS, burst: DEFAULT_RPS * 2 },
|
|
370
|
+
concurrency = DEFAULT_CONCURRENCY
|
|
371
|
+
} = options;
|
|
372
|
+
const rateLimiter = makeTokenBucket(rateLimit.burst, rateLimit.rps);
|
|
373
|
+
const concurrencyGate = makeConcurrencyGate(concurrency);
|
|
374
|
+
return async (input, init) => {
|
|
375
|
+
return concurrencyGate(async () => {
|
|
376
|
+
if (!rateLimiter.take()) {
|
|
377
|
+
logger.warning("http_client", {
|
|
378
|
+
message: "Rate limit exceeded, request rejected"
|
|
379
|
+
});
|
|
380
|
+
throw new Error("Rate limit exceeded");
|
|
381
|
+
}
|
|
382
|
+
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input?.url ?? String(input);
|
|
383
|
+
const method = init?.method || "GET";
|
|
384
|
+
logger.debug("http_client", {
|
|
385
|
+
message: "HTTP request starting",
|
|
386
|
+
url,
|
|
387
|
+
method
|
|
388
|
+
});
|
|
389
|
+
for (let attempt = 1;attempt <= retries; attempt++) {
|
|
390
|
+
try {
|
|
391
|
+
const controller = new AbortController;
|
|
392
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
393
|
+
const response = await fetch(url, {
|
|
394
|
+
...init,
|
|
395
|
+
headers: {
|
|
396
|
+
...baseHeaders,
|
|
397
|
+
...init?.headers
|
|
398
|
+
},
|
|
399
|
+
signal: controller.signal
|
|
400
|
+
});
|
|
401
|
+
clearTimeout(timeoutId);
|
|
402
|
+
if (response.ok || attempt === retries) {
|
|
403
|
+
logger.info("http_client", {
|
|
404
|
+
message: "HTTP request completed",
|
|
405
|
+
url,
|
|
406
|
+
method,
|
|
407
|
+
status: response.status,
|
|
408
|
+
attempt
|
|
409
|
+
});
|
|
410
|
+
return response;
|
|
411
|
+
}
|
|
412
|
+
logger.warning("http_client", {
|
|
413
|
+
message: "HTTP request failed, retrying",
|
|
414
|
+
url,
|
|
415
|
+
method,
|
|
416
|
+
status: response.status,
|
|
417
|
+
attempt
|
|
418
|
+
});
|
|
419
|
+
const delay = retryDelay * 2 ** (attempt - 1) + Math.random() * 1000;
|
|
420
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
421
|
+
} catch (error) {
|
|
422
|
+
if (attempt === retries) {
|
|
423
|
+
logger.error("http_client", {
|
|
424
|
+
message: "HTTP request failed after all retries",
|
|
425
|
+
url,
|
|
426
|
+
method,
|
|
427
|
+
error: error.message,
|
|
428
|
+
attempts: retries
|
|
429
|
+
});
|
|
430
|
+
throw error;
|
|
431
|
+
}
|
|
432
|
+
logger.warning("http_client", {
|
|
433
|
+
message: "HTTP request error, retrying",
|
|
434
|
+
url,
|
|
435
|
+
method,
|
|
436
|
+
error: error.message,
|
|
437
|
+
attempt
|
|
438
|
+
});
|
|
439
|
+
const delay = retryDelay * 2 ** (attempt - 1) + Math.random() * 1000;
|
|
440
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
throw new Error("Unexpected end of retry loop");
|
|
444
|
+
});
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
// src/shared/utils/elicitation.ts
|
|
448
|
+
function getLowLevelServer2(server) {
|
|
449
|
+
return server.server;
|
|
450
|
+
}
|
|
451
|
+
var ElicitResultSchema = exports_external.object({
|
|
452
|
+
action: exports_external.enum(["accept", "decline", "cancel"]),
|
|
453
|
+
content: exports_external.record(exports_external.string(), exports_external.union([exports_external.string(), exports_external.number(), exports_external.boolean(), exports_external.array(exports_external.string())])).optional()
|
|
454
|
+
});
|
|
455
|
+
function validateElicitationSchema(schema) {
|
|
456
|
+
if (schema.type !== "object") {
|
|
457
|
+
throw new Error('Elicitation schema must have type: "object" at root');
|
|
458
|
+
}
|
|
459
|
+
if (!schema.properties || typeof schema.properties !== "object") {
|
|
460
|
+
throw new Error('Elicitation schema must have a "properties" object');
|
|
461
|
+
}
|
|
462
|
+
for (const [fieldName, fieldSchema] of Object.entries(schema.properties)) {
|
|
463
|
+
if ("properties" in fieldSchema) {
|
|
464
|
+
throw new Error(`Nested objects not allowed in elicitation schema (field: "${fieldName}"). ` + "Only primitive types (string, number, integer, boolean) and enums are supported.");
|
|
465
|
+
}
|
|
466
|
+
if (fieldSchema.type === "array" && "items" in fieldSchema) {
|
|
467
|
+
const items = fieldSchema.items;
|
|
468
|
+
if (items.type === "object" || "properties" in items) {
|
|
469
|
+
throw new Error(`Array of objects not allowed in elicitation schema (field: "${fieldName}"). ` + "Only arrays with string enum items are supported for multi-select.");
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
const allowedTypes = ["boolean", "string", "number", "integer", "array"];
|
|
473
|
+
if (!allowedTypes.includes(fieldSchema.type)) {
|
|
474
|
+
throw new Error(`Invalid field type "${fieldSchema.type}" in elicitation schema (field: "${fieldName}"). ` + `Allowed types: ${allowedTypes.join(", ")}`);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
function clientSupportsFormElicitation(server) {
|
|
479
|
+
try {
|
|
480
|
+
const lowLevel = getLowLevelServer2(server);
|
|
481
|
+
const clientCapabilities = lowLevel.getClientCapabilities?.() ?? {};
|
|
482
|
+
return Boolean(clientCapabilities.elicitation);
|
|
483
|
+
} catch {
|
|
484
|
+
return false;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
function clientSupportsUrlElicitation(server) {
|
|
488
|
+
try {
|
|
489
|
+
const lowLevel = getLowLevelServer2(server);
|
|
490
|
+
const clientCapabilities = lowLevel.getClientCapabilities?.() ?? {};
|
|
491
|
+
return Boolean(clientCapabilities.elicitation?.url);
|
|
492
|
+
} catch {
|
|
493
|
+
return false;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
async function elicitForm(server, request) {
|
|
497
|
+
if (!clientSupportsFormElicitation(server)) {
|
|
498
|
+
logger.warning("elicitation", {
|
|
499
|
+
message: "Client does not support form elicitation"
|
|
500
|
+
});
|
|
501
|
+
throw new Error("Client does not support form elicitation");
|
|
502
|
+
}
|
|
503
|
+
validateElicitationSchema(request.requestedSchema);
|
|
504
|
+
logger.debug("elicitation", {
|
|
505
|
+
message: "Requesting form elicitation",
|
|
506
|
+
fieldCount: Object.keys(request.requestedSchema.properties).length
|
|
507
|
+
});
|
|
508
|
+
try {
|
|
509
|
+
const lowLevel = getLowLevelServer2(server);
|
|
510
|
+
const response = await lowLevel.elicitInput({
|
|
511
|
+
mode: "form",
|
|
512
|
+
message: request.message,
|
|
513
|
+
requestedSchema: request.requestedSchema
|
|
514
|
+
});
|
|
515
|
+
logger.info("elicitation", {
|
|
516
|
+
message: "Form elicitation completed",
|
|
517
|
+
action: response.action
|
|
518
|
+
});
|
|
519
|
+
return response;
|
|
520
|
+
} catch (error) {
|
|
521
|
+
logger.error("elicitation", {
|
|
522
|
+
message: "Form elicitation failed",
|
|
523
|
+
error: error.message
|
|
524
|
+
});
|
|
525
|
+
throw error;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
async function elicitUrl(server, request) {
|
|
529
|
+
if (!clientSupportsUrlElicitation(server)) {
|
|
530
|
+
logger.warning("elicitation", {
|
|
531
|
+
message: "Client does not support URL elicitation"
|
|
532
|
+
});
|
|
533
|
+
throw new Error("Client does not support URL elicitation");
|
|
534
|
+
}
|
|
535
|
+
logger.debug("elicitation", {
|
|
536
|
+
message: "Requesting URL elicitation",
|
|
537
|
+
elicitationId: request.elicitationId,
|
|
538
|
+
url: request.url
|
|
539
|
+
});
|
|
540
|
+
try {
|
|
541
|
+
const lowLevel = getLowLevelServer2(server);
|
|
542
|
+
const response = await lowLevel.elicitInput({
|
|
543
|
+
mode: "url",
|
|
544
|
+
message: request.message,
|
|
545
|
+
elicitationId: request.elicitationId,
|
|
546
|
+
url: request.url
|
|
547
|
+
});
|
|
548
|
+
logger.info("elicitation", {
|
|
549
|
+
message: "URL elicitation completed",
|
|
550
|
+
action: response.action,
|
|
551
|
+
elicitationId: request.elicitationId
|
|
552
|
+
});
|
|
553
|
+
return response;
|
|
554
|
+
} catch (error) {
|
|
555
|
+
logger.error("elicitation", {
|
|
556
|
+
message: "URL elicitation failed",
|
|
557
|
+
error: error.message,
|
|
558
|
+
elicitationId: request.elicitationId
|
|
559
|
+
});
|
|
560
|
+
throw error;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
async function notifyElicitationComplete(server, elicitationId) {
|
|
564
|
+
if (!clientSupportsUrlElicitation(server)) {
|
|
565
|
+
throw new Error("Client does not support URL elicitation notifications");
|
|
566
|
+
}
|
|
567
|
+
logger.debug("elicitation", {
|
|
568
|
+
message: "Sending elicitation complete notification",
|
|
569
|
+
elicitationId
|
|
570
|
+
});
|
|
571
|
+
try {
|
|
572
|
+
const lowLevel = getLowLevelServer2(server);
|
|
573
|
+
const notifyComplete = lowLevel.createElicitationCompletionNotifier(elicitationId);
|
|
574
|
+
await notifyComplete();
|
|
575
|
+
logger.info("elicitation", {
|
|
576
|
+
message: "Elicitation complete notification sent",
|
|
577
|
+
elicitationId
|
|
578
|
+
});
|
|
579
|
+
} catch (error) {
|
|
580
|
+
logger.error("elicitation", {
|
|
581
|
+
message: "Failed to send elicitation complete notification",
|
|
582
|
+
error: error.message,
|
|
583
|
+
elicitationId
|
|
584
|
+
});
|
|
585
|
+
throw error;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
async function confirm(server, message, options) {
|
|
589
|
+
const result = await elicitForm(server, {
|
|
590
|
+
message,
|
|
591
|
+
requestedSchema: {
|
|
592
|
+
type: "object",
|
|
593
|
+
properties: {
|
|
594
|
+
confirmed: {
|
|
595
|
+
type: "boolean",
|
|
596
|
+
title: options?.confirmLabel ?? "Confirm",
|
|
597
|
+
default: false
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
});
|
|
602
|
+
return result.action === "accept" && result.content?.confirmed === true;
|
|
603
|
+
}
|
|
604
|
+
async function promptText(server, message, options) {
|
|
605
|
+
const result = await elicitForm(server, {
|
|
606
|
+
message,
|
|
607
|
+
requestedSchema: {
|
|
608
|
+
type: "object",
|
|
609
|
+
properties: {
|
|
610
|
+
value: {
|
|
611
|
+
type: "string",
|
|
612
|
+
title: options?.title ?? "Value",
|
|
613
|
+
description: options?.description,
|
|
614
|
+
default: options?.defaultValue,
|
|
615
|
+
minLength: options?.minLength,
|
|
616
|
+
maxLength: options?.maxLength
|
|
617
|
+
}
|
|
618
|
+
},
|
|
619
|
+
...options?.required && { required: ["value"] }
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
if (result.action === "accept") {
|
|
623
|
+
return result.content?.value;
|
|
624
|
+
}
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
async function promptSelect(server, message, options, config) {
|
|
628
|
+
const result = await elicitForm(server, {
|
|
629
|
+
message,
|
|
630
|
+
requestedSchema: {
|
|
631
|
+
type: "object",
|
|
632
|
+
properties: {
|
|
633
|
+
selection: {
|
|
634
|
+
type: "string",
|
|
635
|
+
title: config?.title ?? "Selection",
|
|
636
|
+
oneOf: options.map((opt) => ({ const: opt.value, title: opt.label })),
|
|
637
|
+
default: config?.defaultValue
|
|
638
|
+
}
|
|
639
|
+
},
|
|
640
|
+
...config?.required && { required: ["selection"] }
|
|
641
|
+
}
|
|
642
|
+
});
|
|
643
|
+
if (result.action === "accept") {
|
|
644
|
+
return result.content?.selection;
|
|
645
|
+
}
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
// src/shared/utils/formatting.ts
|
|
649
|
+
function summarizeList(items, formatPreview, options = {}) {
|
|
650
|
+
const {
|
|
651
|
+
title = "List",
|
|
652
|
+
maxPreview = 100,
|
|
653
|
+
detailsFormatter,
|
|
654
|
+
maxDetails = 5
|
|
655
|
+
} = options;
|
|
656
|
+
if (items.length === 0) {
|
|
657
|
+
return `## ${title} (0 items)
|
|
658
|
+
|
|
659
|
+
No items found.`;
|
|
660
|
+
}
|
|
661
|
+
const parts = [];
|
|
662
|
+
const previewItems = items.slice(0, maxPreview);
|
|
663
|
+
const hasMore = items.length > maxPreview;
|
|
664
|
+
parts.push(`## ${title} (${items.length} items)`);
|
|
665
|
+
parts.push("");
|
|
666
|
+
parts.push(...previewItems.map(formatPreview));
|
|
667
|
+
if (hasMore) {
|
|
668
|
+
parts.push(`... and ${items.length - maxPreview} more`);
|
|
669
|
+
}
|
|
670
|
+
if (detailsFormatter && items.length > 0) {
|
|
671
|
+
parts.push("");
|
|
672
|
+
parts.push("## Details");
|
|
673
|
+
parts.push("");
|
|
674
|
+
const detailItems = items.slice(0, maxDetails);
|
|
675
|
+
parts.push(...detailItems.map(detailsFormatter));
|
|
676
|
+
if (items.length > maxDetails) {
|
|
677
|
+
parts.push("");
|
|
678
|
+
parts.push(`_Showing ${maxDetails} of ${items.length} items. Use pagination to see more._`);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
return parts.join(`
|
|
682
|
+
`);
|
|
683
|
+
}
|
|
684
|
+
function summarizeBatch(results, options) {
|
|
685
|
+
const { operationName, successFormatter, errorFormatter } = options;
|
|
686
|
+
const successes = results.filter((r) => r.success);
|
|
687
|
+
const failures = results.filter((r) => !r.success);
|
|
688
|
+
const parts = [];
|
|
689
|
+
parts.push(`## ${operationName} Results`);
|
|
690
|
+
parts.push("");
|
|
691
|
+
parts.push(`**Summary**: ${successes.length} succeeded, ${failures.length} failed (${results.length} total)`);
|
|
692
|
+
if (successes.length > 0) {
|
|
693
|
+
parts.push("");
|
|
694
|
+
parts.push("### Successful Operations");
|
|
695
|
+
parts.push("");
|
|
696
|
+
parts.push(...successes.map(successFormatter));
|
|
697
|
+
}
|
|
698
|
+
if (failures.length > 0) {
|
|
699
|
+
parts.push("");
|
|
700
|
+
parts.push("### Failed Operations");
|
|
701
|
+
parts.push("");
|
|
702
|
+
parts.push(...failures.map(errorFormatter));
|
|
703
|
+
}
|
|
704
|
+
return parts.join(`
|
|
705
|
+
`);
|
|
706
|
+
}
|
|
707
|
+
function formatFieldChange(fieldName, before, after) {
|
|
708
|
+
const formatValue = (value) => {
|
|
709
|
+
if (value === null || value === undefined)
|
|
710
|
+
return "\u2014";
|
|
711
|
+
if (typeof value === "boolean")
|
|
712
|
+
return value ? "true" : "false";
|
|
713
|
+
return String(value);
|
|
714
|
+
};
|
|
715
|
+
return `${fieldName}: ${formatValue(before)} \u2192 ${formatValue(after)}`;
|
|
716
|
+
}
|
|
717
|
+
function createSection(content, options = {}) {
|
|
718
|
+
const { tag, indent = 0 } = options;
|
|
719
|
+
const indentation = " ".repeat(indent);
|
|
720
|
+
if (!tag) {
|
|
721
|
+
return content.split(`
|
|
722
|
+
`).map((line) => indentation + line).join(`
|
|
723
|
+
`);
|
|
724
|
+
}
|
|
725
|
+
const lines = [];
|
|
726
|
+
lines.push(`${indentation}<ove tag="${tag}">`);
|
|
727
|
+
lines.push(...content.split(`
|
|
728
|
+
`).map((line) => indentation + (line ? ` ${line}` : line)));
|
|
729
|
+
lines.push(`${indentation}</ove>`);
|
|
730
|
+
return lines.join(`
|
|
731
|
+
`);
|
|
732
|
+
}
|
|
733
|
+
function truncate(text, maxLength = 100) {
|
|
734
|
+
if (text.length <= maxLength)
|
|
735
|
+
return text;
|
|
736
|
+
return `${text.slice(0, maxLength - 3)}...`;
|
|
737
|
+
}
|
|
738
|
+
function formatKeyValueList(pairs) {
|
|
739
|
+
return Object.entries(pairs).filter(([_key, value]) => value !== null && value !== undefined).map(([key, value]) => {
|
|
740
|
+
const capitalizedKey = key.charAt(0).toUpperCase() + key.slice(1);
|
|
741
|
+
return `- **${capitalizedKey}**: ${value}`;
|
|
742
|
+
}).join(`
|
|
743
|
+
`);
|
|
744
|
+
}
|
|
745
|
+
// src/shared/utils/pagination.ts
|
|
746
|
+
function createCursor(offset) {
|
|
747
|
+
return base64Encode(JSON.stringify({ offset }));
|
|
748
|
+
}
|
|
749
|
+
function parseCursor(cursor) {
|
|
750
|
+
if (!cursor)
|
|
751
|
+
return 0;
|
|
752
|
+
try {
|
|
753
|
+
const decoded = base64Decode(cursor);
|
|
754
|
+
const parsed = JSON.parse(decoded);
|
|
755
|
+
return typeof parsed.offset === "number" ? parsed.offset : 0;
|
|
756
|
+
} catch {
|
|
757
|
+
return 0;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
function paginateArray(items, cursor, limit = 50) {
|
|
761
|
+
const offset = parseCursor(cursor);
|
|
762
|
+
const startIndex = Math.max(0, offset);
|
|
763
|
+
const endIndex = startIndex + limit;
|
|
764
|
+
const data = items.slice(startIndex, endIndex);
|
|
765
|
+
const nextCursor = endIndex < items.length ? createCursor(endIndex) : undefined;
|
|
766
|
+
return { data, nextCursor };
|
|
767
|
+
}
|
|
768
|
+
// src/shared/utils/progress.ts
|
|
769
|
+
class ProgressReporter {
|
|
770
|
+
server;
|
|
771
|
+
progressToken;
|
|
772
|
+
completed = false;
|
|
773
|
+
constructor(server, progressToken) {
|
|
774
|
+
this.server = server;
|
|
775
|
+
this.progressToken = progressToken;
|
|
776
|
+
}
|
|
777
|
+
async report(progress, total, message) {
|
|
778
|
+
if (this.completed) {
|
|
779
|
+
logger.warning("progress", {
|
|
780
|
+
message: "Attempted to send progress after completion - notification will be ignored",
|
|
781
|
+
progressToken: this.progressToken
|
|
782
|
+
});
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
try {
|
|
786
|
+
const lowLevel = getLowLevelServer(this.server);
|
|
787
|
+
await lowLevel.notification?.({
|
|
788
|
+
method: "notifications/progress",
|
|
789
|
+
params: {
|
|
790
|
+
progressToken: this.progressToken,
|
|
791
|
+
progress,
|
|
792
|
+
total,
|
|
793
|
+
...message ? { message } : {}
|
|
794
|
+
}
|
|
795
|
+
});
|
|
796
|
+
} catch (error) {
|
|
797
|
+
logger.warning("progress", {
|
|
798
|
+
message: "Failed to send progress notification",
|
|
799
|
+
error: error.message,
|
|
800
|
+
progressToken: this.progressToken
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
async complete(message) {
|
|
805
|
+
await this.report(1, 1, message ?? "Complete");
|
|
806
|
+
this.completed = true;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
function createProgressReporter(server, progressToken) {
|
|
810
|
+
if (!progressToken) {
|
|
811
|
+
return null;
|
|
812
|
+
}
|
|
813
|
+
return new ProgressReporter(server, progressToken);
|
|
814
|
+
}
|
|
815
|
+
// src/shared/utils/roots.ts
|
|
816
|
+
async function requestRoots(server) {
|
|
817
|
+
logger.debug("roots", {
|
|
818
|
+
message: "Requesting roots from client"
|
|
819
|
+
});
|
|
820
|
+
try {
|
|
821
|
+
const lowLevel = getLowLevelServer(server);
|
|
822
|
+
if (!lowLevel.request) {
|
|
823
|
+
throw new Error("Roots not supported: Server does not support client requests");
|
|
824
|
+
}
|
|
825
|
+
const clientCapabilities = lowLevel.getClientCapabilities?.() ?? {};
|
|
826
|
+
if (!clientCapabilities.roots) {
|
|
827
|
+
throw new Error("Client does not support roots capability. " + 'Client must declare "roots" capability to list filesystem roots.');
|
|
828
|
+
}
|
|
829
|
+
const response = await lowLevel.request({
|
|
830
|
+
method: "roots/list"
|
|
831
|
+
});
|
|
832
|
+
logger.info("roots", {
|
|
833
|
+
message: "Received roots from client",
|
|
834
|
+
rootCount: response.roots.length
|
|
835
|
+
});
|
|
836
|
+
return response.roots;
|
|
837
|
+
} catch (error) {
|
|
838
|
+
logger.error("roots", {
|
|
839
|
+
message: "Roots request failed",
|
|
840
|
+
error: error.message
|
|
841
|
+
});
|
|
842
|
+
if (isJsonRpcError(error, JSON_RPC_METHOD_NOT_FOUND)) {
|
|
843
|
+
throw new Error('Roots not supported by client. Client must declare "roots" capability.');
|
|
844
|
+
}
|
|
845
|
+
throw error;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
function clientSupportsRoots(server) {
|
|
849
|
+
try {
|
|
850
|
+
const lowLevel = getLowLevelServer(server);
|
|
851
|
+
const clientCapabilities = lowLevel.getClientCapabilities?.() ?? {};
|
|
852
|
+
return Boolean(clientCapabilities.roots);
|
|
853
|
+
} catch {
|
|
854
|
+
return false;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
function clientSupportsRootsListChanged(server) {
|
|
858
|
+
try {
|
|
859
|
+
const lowLevel = getLowLevelServer(server);
|
|
860
|
+
const clientCapabilities = lowLevel.getClientCapabilities?.() ?? {};
|
|
861
|
+
const roots = clientCapabilities.roots;
|
|
862
|
+
return Boolean(roots?.listChanged);
|
|
863
|
+
} catch {
|
|
864
|
+
return false;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
// src/shared/utils/sampling.ts
|
|
868
|
+
function getLowLevelServer3(server) {
|
|
869
|
+
return server.server;
|
|
870
|
+
}
|
|
871
|
+
async function requestSampling(server, request) {
|
|
872
|
+
logger.debug("sampling", {
|
|
873
|
+
message: "Requesting LLM sampling from client",
|
|
874
|
+
messageCount: request.messages.length,
|
|
875
|
+
modelHints: request.modelPreferences?.hints?.map((h) => h.name),
|
|
876
|
+
hasTools: !!request.tools,
|
|
877
|
+
hasToolChoice: !!request.toolChoice
|
|
878
|
+
});
|
|
879
|
+
try {
|
|
880
|
+
const lowLevel = getLowLevelServer3(server);
|
|
881
|
+
const params = {
|
|
882
|
+
messages: request.messages,
|
|
883
|
+
maxTokens: request.maxTokens,
|
|
884
|
+
modelPreferences: request.modelPreferences,
|
|
885
|
+
systemPrompt: request.systemPrompt,
|
|
886
|
+
temperature: request.temperature,
|
|
887
|
+
stopSequences: request.stopSequences,
|
|
888
|
+
metadata: request.metadata,
|
|
889
|
+
...request.tools && { tools: request.tools },
|
|
890
|
+
...request.toolChoice && { toolChoice: request.toolChoice }
|
|
891
|
+
};
|
|
892
|
+
const response = await lowLevel.createMessage(params);
|
|
893
|
+
logger.info("sampling", {
|
|
894
|
+
message: "Received LLM response from client",
|
|
895
|
+
model: response.model,
|
|
896
|
+
stopReason: response.stopReason
|
|
897
|
+
});
|
|
898
|
+
return response;
|
|
899
|
+
} catch (error) {
|
|
900
|
+
logger.error("sampling", {
|
|
901
|
+
message: "Sampling request failed",
|
|
902
|
+
error: error.message
|
|
903
|
+
});
|
|
904
|
+
throw error;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
function clientSupportsSampling(server) {
|
|
908
|
+
try {
|
|
909
|
+
const lowLevel = getLowLevelServer3(server);
|
|
910
|
+
const clientCapabilities = lowLevel.getClientCapabilities?.() ?? {};
|
|
911
|
+
return Boolean(clientCapabilities.sampling);
|
|
912
|
+
} catch {
|
|
913
|
+
return false;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
function clientSupportsSamplingTools(server) {
|
|
917
|
+
try {
|
|
918
|
+
const lowLevel = getLowLevelServer3(server);
|
|
919
|
+
const clientCapabilities = lowLevel.getClientCapabilities?.() ?? {};
|
|
920
|
+
const sampling = clientCapabilities.sampling;
|
|
921
|
+
return Boolean(sampling?.tools);
|
|
922
|
+
} catch {
|
|
923
|
+
return false;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
async function requestTextCompletion(server, prompt, maxTokens, options) {
|
|
927
|
+
const response = await requestSampling(server, {
|
|
928
|
+
messages: [
|
|
929
|
+
{
|
|
930
|
+
role: "user",
|
|
931
|
+
content: {
|
|
932
|
+
type: "text",
|
|
933
|
+
text: prompt
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
],
|
|
937
|
+
maxTokens,
|
|
938
|
+
...options
|
|
939
|
+
});
|
|
940
|
+
if (response.content.type !== "text") {
|
|
941
|
+
throw new Error(`Expected text response but got ${response.content.type}`);
|
|
942
|
+
}
|
|
943
|
+
return response.content.text;
|
|
944
|
+
}
|
|
945
|
+
export {
|
|
946
|
+
workerDiscoveryStrategy,
|
|
947
|
+
withCors,
|
|
948
|
+
withCancellation,
|
|
949
|
+
validateRedirectUri,
|
|
950
|
+
validateProtocolVersion,
|
|
951
|
+
validateOrigin,
|
|
952
|
+
validateElicitationSchema,
|
|
953
|
+
validateAuthConfig,
|
|
954
|
+
truncate,
|
|
955
|
+
toProviderTokens,
|
|
956
|
+
toProviderInfo,
|
|
957
|
+
summarizeList,
|
|
958
|
+
summarizeBatch,
|
|
959
|
+
stopContextCleanup,
|
|
960
|
+
startContextCleanup,
|
|
961
|
+
shimProcessEnv,
|
|
962
|
+
sharedTools,
|
|
963
|
+
sharedLogger,
|
|
964
|
+
sessions,
|
|
965
|
+
resolveStaticAuth,
|
|
966
|
+
requiresAuth,
|
|
967
|
+
requestTextCompletion,
|
|
968
|
+
requestSampling,
|
|
969
|
+
requestRoots,
|
|
970
|
+
registerTools,
|
|
971
|
+
refreshProviderToken,
|
|
972
|
+
promptText,
|
|
973
|
+
promptSelect,
|
|
974
|
+
parseTokenInput,
|
|
975
|
+
parseCursor,
|
|
976
|
+
parseConfig,
|
|
977
|
+
parseCallbackInput,
|
|
978
|
+
parseAuthorizeInput,
|
|
979
|
+
parseAuthStrategy2 as parseAuthStrategy,
|
|
980
|
+
paginateArray,
|
|
981
|
+
notifyElicitationComplete,
|
|
982
|
+
nodeDiscoveryStrategy,
|
|
983
|
+
mergeAuthHeaders,
|
|
984
|
+
makeTokenBucket,
|
|
985
|
+
makeConcurrencyGate,
|
|
986
|
+
logger,
|
|
987
|
+
isTokenExpiredOrExpiring,
|
|
988
|
+
isSsrfSafe,
|
|
989
|
+
isOAuthStrategy,
|
|
990
|
+
isJsonRpcError,
|
|
991
|
+
isClientIdUrl,
|
|
992
|
+
initializeWorkerStorage,
|
|
993
|
+
initializeStorage,
|
|
994
|
+
healthTool,
|
|
995
|
+
healthInputSchema,
|
|
996
|
+
handleToken,
|
|
997
|
+
handleRevoke,
|
|
998
|
+
handleRegister,
|
|
999
|
+
handleProviderCallback,
|
|
1000
|
+
handleMcpNotification,
|
|
1001
|
+
handleAuthorize,
|
|
1002
|
+
getTokenStore,
|
|
1003
|
+
getSharedToolNames,
|
|
1004
|
+
getSharedTool,
|
|
1005
|
+
getSessionStore,
|
|
1006
|
+
getServerWithInternals,
|
|
1007
|
+
getLowLevelServer,
|
|
1008
|
+
getLogLevel,
|
|
1009
|
+
getCurrentAuthContext,
|
|
1010
|
+
generateOpaqueToken,
|
|
1011
|
+
generateKey,
|
|
1012
|
+
formatKeyValueList,
|
|
1013
|
+
formatFieldChange,
|
|
1014
|
+
fetchClientMetadata,
|
|
1015
|
+
executeSharedTool,
|
|
1016
|
+
ensureFreshToken,
|
|
1017
|
+
encrypt,
|
|
1018
|
+
elicitUrl,
|
|
1019
|
+
elicitForm,
|
|
1020
|
+
echoTool,
|
|
1021
|
+
echoInputSchema,
|
|
1022
|
+
dispatchMcpMethod,
|
|
1023
|
+
defineTool,
|
|
1024
|
+
decrypt,
|
|
1025
|
+
createWorkerRouter,
|
|
1026
|
+
createSection,
|
|
1027
|
+
createProgressReporter,
|
|
1028
|
+
createMCPServer,
|
|
1029
|
+
createHttpClient,
|
|
1030
|
+
createEncryptor,
|
|
1031
|
+
createDiscoveryHandlers,
|
|
1032
|
+
createCursor,
|
|
1033
|
+
createCancellationToken,
|
|
1034
|
+
corsPreflightResponse,
|
|
1035
|
+
contextRegistry,
|
|
1036
|
+
confirm,
|
|
1037
|
+
clientSupportsUrlElicitation,
|
|
1038
|
+
clientSupportsSamplingTools,
|
|
1039
|
+
clientSupportsSampling,
|
|
1040
|
+
clientSupportsRootsListChanged,
|
|
1041
|
+
clientSupportsRoots,
|
|
1042
|
+
clientSupportsFormElicitation,
|
|
1043
|
+
checkSsrfSafe,
|
|
1044
|
+
buildUnauthorizedChallenge,
|
|
1045
|
+
buildTokenInput,
|
|
1046
|
+
buildServer,
|
|
1047
|
+
buildProviderRefreshConfig,
|
|
1048
|
+
buildProviderConfig,
|
|
1049
|
+
buildProtectedResourceMetadata,
|
|
1050
|
+
buildOAuthConfig,
|
|
1051
|
+
buildFlowOptions,
|
|
1052
|
+
buildCorsHeaders,
|
|
1053
|
+
buildCapabilities,
|
|
1054
|
+
buildAuthorizationServerMetadata,
|
|
1055
|
+
buildAuthHeaders,
|
|
1056
|
+
base64UrlEncodeString,
|
|
1057
|
+
base64UrlEncodeJson,
|
|
1058
|
+
base64UrlEncode,
|
|
1059
|
+
base64UrlDecodeString,
|
|
1060
|
+
base64UrlDecodeJson,
|
|
1061
|
+
base64UrlDecode,
|
|
1062
|
+
base64Encode,
|
|
1063
|
+
base64Decode,
|
|
1064
|
+
authContextStorage,
|
|
1065
|
+
assertSsrfSafe,
|
|
1066
|
+
assertProviderToken,
|
|
1067
|
+
SqliteSessionStore,
|
|
1068
|
+
SUPPORTED_PROTOCOL_VERSIONS,
|
|
1069
|
+
ProgressReporter,
|
|
1070
|
+
MemoryTokenStore,
|
|
1071
|
+
MemorySessionStore,
|
|
1072
|
+
MAX_SESSIONS_PER_API_KEY,
|
|
1073
|
+
LATEST_PROTOCOL_VERSION,
|
|
1074
|
+
KvTokenStore,
|
|
1075
|
+
KvSessionStore,
|
|
1076
|
+
JsonRpcErrorCode,
|
|
1077
|
+
JSON_RPC_METHOD_NOT_FOUND,
|
|
1078
|
+
FileTokenStore,
|
|
1079
|
+
ElicitResultSchema,
|
|
1080
|
+
ClientMetadataSchema,
|
|
1081
|
+
CancellationToken,
|
|
1082
|
+
CancellationError
|
|
1083
|
+
};
|