@fdkey/mcp 0.2.0

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/dist/index.js ADDED
@@ -0,0 +1,409 @@
1
+ import { z } from 'zod';
2
+ import { jwtVerify, decodeProtectedHeader, decodeJwt } from 'jose';
3
+ import { InitializeRequestSchema } from '@modelcontextprotocol/sdk/types.js';
4
+ import { normalisePolicy } from './types.js';
5
+ import { canCall, markVerified, consumePolicy } from './guard.js';
6
+ import { VpsClient, VpsHttpError } from './vps-client.js';
7
+ import { StaticRouter } from './router-static.js';
8
+ import { createSessionStore } from './session-store.js';
9
+ import { WellKnownClient } from './well-known.js';
10
+ /** @internal Test-only export. Not part of the supported public API;
11
+ * may change or disappear without notice. Used by `index.test.ts` to
12
+ * verify the lazy-import contract directly. */
13
+ export { LazyVpsRouter as __LazyVpsRouterForTesting };
14
+ /** Hardcoded SDK version. Forwarded to the VPS as `integrator.sdk_version`
15
+ * on every challenge fetch so we can correlate failures with SDK releases.
16
+ * MUST be kept in sync with package.json version on every release — there's
17
+ * a smoke test that checks this match. */
18
+ const SDK_VERSION = '0.2.0';
19
+ /** Default VPS URL used when no `vpsUrl` and no `discoveryUrl` are provided. */
20
+ const DEFAULT_VPS_URL = 'https://api.fdkey.com';
21
+ /** Lazy wrapper around the multi-VPS `VpsRouter`. Defers `await import('./vps-router.js')`
22
+ * until the first `getTarget()` call so Workers/Bun/Deno builds — which never hit this
23
+ * path unless `discoveryUrl` is set — don't pull undici into the bundle.
24
+ *
25
+ * If `undici` is not installed (it's an `optionalDependency`), the dynamic
26
+ * import will fail. We catch that and rethrow with a clear, actionable
27
+ * error so the integrator doesn't have to debug a `Cannot find module
28
+ * 'undici'` stack trace. */
29
+ class LazyVpsRouter {
30
+ discoveryUrl;
31
+ inner = null;
32
+ constructor(discoveryUrl) {
33
+ this.discoveryUrl = discoveryUrl;
34
+ }
35
+ async getTarget() {
36
+ if (!this.inner) {
37
+ try {
38
+ const { VpsRouter } = await import('./vps-router.js');
39
+ this.inner = new VpsRouter(this.discoveryUrl);
40
+ }
41
+ catch (err) {
42
+ // We want to rethrow with a friendly message ONLY for the specific
43
+ // case "undici not installed" (the optionalDependency wasn't
44
+ // resolved). Match the Node module-resolution error code AND the
45
+ // module name so an unrelated bundler glitch on `vps-router.js`
46
+ // itself doesn't surface a misleading "install undici" error.
47
+ const code = err.code;
48
+ const isModuleNotFound = code === 'ERR_MODULE_NOT_FOUND' || code === 'MODULE_NOT_FOUND';
49
+ const msg = err instanceof Error ? err.message : String(err);
50
+ if (isModuleNotFound && msg.includes('undici')) {
51
+ throw new Error("@fdkey/mcp: `discoveryUrl` is set but `undici` is not installed. " +
52
+ "Multi-VPS routing requires undici (Node-only). Run " +
53
+ "`npm install undici` to enable it, or remove `discoveryUrl` to " +
54
+ "use the single-VPS StaticRouter path which works on Workers/Bun/Deno.");
55
+ }
56
+ throw err;
57
+ }
58
+ }
59
+ return this.inner.getTarget();
60
+ }
61
+ recordFailure(ip) {
62
+ if (this.inner)
63
+ this.inner.recordFailure(ip);
64
+ }
65
+ }
66
+ /** Maps the wrapped (and original) server objects to their bounded
67
+ * SessionStore. Use getFdkeyContext() rather than touching this directly. */
68
+ const FDKEY_REGISTRY = new WeakMap();
69
+ /** Read the current FDKEY context for a given session. Pass either the `extra`
70
+ * argument from a tool handler or a session ID string. Returns null if the server
71
+ * was not wrapped with withFdkey() or the session doesn't exist yet.
72
+ *
73
+ * Reads use `store.peek()` which does NOT slide the LRU position — querying
74
+ * the context shouldn't extend a session's lifetime; only actual tool calls
75
+ * do that. */
76
+ export function getFdkeyContext(server, extraOrSessionId) {
77
+ const store = FDKEY_REGISTRY.get(server);
78
+ if (!store)
79
+ return null;
80
+ const sid = typeof extraOrSessionId === 'string'
81
+ ? extraOrSessionId
82
+ : extraOrSessionId?.sessionId ?? 'stdio';
83
+ const s = store.peek(sid);
84
+ if (!s)
85
+ return { verified: false, verifiedAt: null, score: null, tier: null, claims: null };
86
+ return {
87
+ verified: s.verified,
88
+ verifiedAt: s.verifiedAt,
89
+ score: extractScore(s.lastClaims),
90
+ tier: extractTier(s.lastClaims),
91
+ claims: s.lastClaims,
92
+ };
93
+ }
94
+ function extractScore(claims) {
95
+ if (!claims)
96
+ return null;
97
+ const v = claims.score;
98
+ return typeof v === 'number' ? v : null;
99
+ }
100
+ function extractTier(claims) {
101
+ if (!claims)
102
+ return null;
103
+ const v = claims.tier;
104
+ return typeof v === 'string' ? v : null;
105
+ }
106
+ const GET_CHALLENGE_TOOL = 'fdkey_get_challenge';
107
+ const SUBMIT_CHALLENGE_TOOL = 'fdkey_submit_challenge';
108
+ const GET_CHALLENGE_DESC = 'Request an AI identity verification challenge. Call this when a tool returns fdkey_verification_required, when asked to verify, or to verify proactively. There is a time limit on your answers.';
109
+ const SUBMIT_CHALLENGE_DESC = 'Submit answers to the active FDKEY challenge. On verified:true, retry the tool that was blocked. On verified:false, call fdkey_get_challenge to try again.';
110
+ function mkError(text) {
111
+ return { content: [{ type: 'text', text }], isError: true };
112
+ }
113
+ function mkResult(obj) {
114
+ return { content: [{ type: 'text', text: JSON.stringify(obj) }] };
115
+ }
116
+ function expiresInSeconds(expiresAtIso) {
117
+ const ms = new Date(expiresAtIso).getTime() - Date.now();
118
+ return Math.max(0, Math.round(ms / 1000));
119
+ }
120
+ function challengePayload(c) {
121
+ return {
122
+ expires_in_seconds: c.expires_in_seconds ?? expiresInSeconds(c.expires_at),
123
+ difficulty: c.difficulty,
124
+ types_served: c.types_served,
125
+ header: c.header,
126
+ puzzles: c.puzzles,
127
+ footer: c.footer,
128
+ };
129
+ }
130
+ /**
131
+ * Wraps an MCP server with FDKEY verification middleware.
132
+ *
133
+ * @example
134
+ * const server = withFdkey(new McpServer({ name: 'my-server', version: '1.0.0' }), {
135
+ * apiKey: process.env.FDKEY_API_KEY!,
136
+ * protect: {
137
+ * login: { policy: 'each_call' },
138
+ * register: { policy: 'once_per_session' },
139
+ * },
140
+ * });
141
+ * server.registerTool('login', { inputSchema: { username: z.string() } }, async (args) => { ... });
142
+ */
143
+ export function withFdkey(server, config) {
144
+ const protect = config.protect ?? {};
145
+ const onFail = config.onFail ?? 'block';
146
+ // Default is 'allow' (fail-open) so an FDKEY service outage doesn't
147
+ // brick integrator workflows — protected tools fall through to their
148
+ // original handler. Override with `onVpsError: 'block'` if your threat
149
+ // model prefers fail-closed.
150
+ const onVpsError = config.onVpsError ?? 'allow';
151
+ const inlineChallenge = config.inlineChallenge ?? false;
152
+ // Router selection:
153
+ // discoveryUrl set → multi-VPS routing via undici-backed VpsRouter
154
+ // (Node-only; lazy-imported to keep undici out
155
+ // of Workers/Bun/Deno bundles)
156
+ // vpsUrl set → StaticRouter pinned to that URL (default fetch)
157
+ // neither set → StaticRouter at https://api.fdkey.com
158
+ const router = config.discoveryUrl
159
+ ? new LazyVpsRouter(config.discoveryUrl)
160
+ : new StaticRouter(config.vpsUrl ?? DEFAULT_VPS_URL);
161
+ const vpsClient = new VpsClient(router, config.apiKey, config.difficulty ?? 'medium');
162
+ const wellKnown = new WellKnownClient(router);
163
+ // Bounded per-server session store. See session-store.ts for the
164
+ // full TTL + LRU eviction contract; the wrapped server itself only
165
+ // sees `get()` and `peek()`.
166
+ const store = createSessionStore();
167
+ const getSession = (id) => store.get(id);
168
+ let latestClientInfo = null;
169
+ let latestProtocolVersion = null;
170
+ // Hook (1): wrap the existing initialize handler so we can read protocolVersion
171
+ // from the request params before delegating. The Server class registers its
172
+ // own _oninitialize via setRequestHandler in its constructor; calling
173
+ // setRequestHandler again replaces the entry. We retrieve the prior handler
174
+ // through the protocol's private _requestHandlers map and re-invoke it,
175
+ // preserving the official init behavior. Private-API access is bounded to
176
+ // these few lines and documented; if MCP SDK refactors this away we update.
177
+ const protocolPrivate = server.server;
178
+ const origInitialize = protocolPrivate._requestHandlers?.get('initialize');
179
+ if (origInitialize) {
180
+ server.server.setRequestHandler(InitializeRequestSchema, async (request, extra) => {
181
+ try {
182
+ // request.params.protocolVersion is what the client sent
183
+ const pv = request.params?.protocolVersion;
184
+ if (typeof pv === 'string')
185
+ latestProtocolVersion = pv;
186
+ }
187
+ catch {
188
+ // Reading the field must never break initialize; swallow errors.
189
+ }
190
+ return origInitialize(request, extra);
191
+ });
192
+ }
193
+ // Hook (2): post-init capture for clientInfo + capabilities.
194
+ const previousOnInitialized = server.server.oninitialized;
195
+ server.server.oninitialized = () => {
196
+ const ci = server.server.getClientVersion();
197
+ const caps = server.server.getClientCapabilities();
198
+ if (ci) {
199
+ latestClientInfo = {
200
+ name: ci.name,
201
+ version: ci.version,
202
+ title: ci.title,
203
+ capabilities: caps,
204
+ };
205
+ }
206
+ if (previousOnInitialized)
207
+ previousOnInitialized();
208
+ };
209
+ // Integrator-side block — same value every challenge call. Read once at
210
+ // wrap time. server_info comes from McpServer's ctor args; the underlying
211
+ // Server stores it as private `_serverInfo`. Accessing via type assertion
212
+ // because there's no public getter as of MCP TS SDK 1.x.
213
+ const serverPrivate = server.server;
214
+ const integratorMeta = {
215
+ server_name: serverPrivate._serverInfo?.name,
216
+ server_version: serverPrivate._serverInfo?.version,
217
+ sdk_version: SDK_VERSION,
218
+ };
219
+ // Tags: integrator-supplied free-form key/value dimensions. Forwarded as-is
220
+ // on every challenge fetch. VPS bounds them at 16 keys / 50 char keys / 200
221
+ // char values and rejects with HTTP 400 on overflow.
222
+ const configuredTags = config.tags;
223
+ /** Lazy-copy session-scoped agent metadata on first tool call. Idempotent —
224
+ * re-runs are no-ops because we use ??= guards. Returns a ChallengeMeta
225
+ * bundle ready to pass to vpsClient.fetchChallenge(). */
226
+ function captureChallengeMeta(session, extra) {
227
+ session.mcpSessionId ??= extra.sessionId ?? 'stdio';
228
+ if (session.transport === 'unknown') {
229
+ session.transport = extra.sessionId ? 'http' : 'stdio';
230
+ }
231
+ session.clientInfo ??= latestClientInfo;
232
+ session.protocolVersion ??= latestProtocolVersion;
233
+ const agent = {
234
+ transport: session.transport,
235
+ mcp_session_id: session.mcpSessionId ?? undefined,
236
+ };
237
+ if (session.clientInfo) {
238
+ agent.client_name = session.clientInfo.name;
239
+ agent.client_version = session.clientInfo.version;
240
+ if (session.clientInfo.title)
241
+ agent.client_title = session.clientInfo.title;
242
+ if (session.clientInfo.capabilities) {
243
+ agent.client_capabilities = session.clientInfo.capabilities;
244
+ }
245
+ }
246
+ if (session.protocolVersion)
247
+ agent.protocol_version = session.protocolVersion;
248
+ return {
249
+ agent,
250
+ integrator: integratorMeta,
251
+ tags: configuredTags,
252
+ };
253
+ }
254
+ // --- Injected tool: fdkey_get_challenge ---
255
+ server.registerTool(GET_CHALLENGE_TOOL, { description: GET_CHALLENGE_DESC }, async (extra) => {
256
+ const e = extra;
257
+ const session = getSession(e.sessionId ?? 'stdio');
258
+ const meta = captureChallengeMeta(session, e);
259
+ try {
260
+ const challenge = await vpsClient.fetchChallenge(meta);
261
+ session.pendingChallengeId = challenge.challenge_id;
262
+ return mkResult(challengePayload(challenge));
263
+ }
264
+ catch (err) {
265
+ return mkError(`fdkey_service_unavailable: ${String(err)}`);
266
+ }
267
+ });
268
+ // --- Injected tool: fdkey_submit_challenge ---
269
+ server.registerTool(SUBMIT_CHALLENGE_TOOL, {
270
+ description: SUBMIT_CHALLENGE_DESC,
271
+ inputSchema: { answers: z.record(z.string(), z.unknown()) },
272
+ }, async (args, extra) => {
273
+ const session = getSession(extra.sessionId ?? 'stdio');
274
+ if (!session.pendingChallengeId) {
275
+ return mkResult({
276
+ verified: false,
277
+ error: 'no_active_challenge',
278
+ message: `No active challenge. Call ${GET_CHALLENGE_TOOL} first.`,
279
+ });
280
+ }
281
+ let result;
282
+ try {
283
+ result = await vpsClient.submitAnswers(session.pendingChallengeId, args.answers);
284
+ }
285
+ catch (err) {
286
+ session.pendingChallengeId = null;
287
+ if (err instanceof VpsHttpError && err.body.error === 'challenge_expired') {
288
+ return mkResult({
289
+ verified: false,
290
+ error: 'challenge_expired',
291
+ message: `Challenge expired. Call ${GET_CHALLENGE_TOOL} to start a new one.`,
292
+ });
293
+ }
294
+ if (err instanceof VpsHttpError && err.status >= 400 && err.status < 500) {
295
+ // 4xx (e.g. wrong_user, already_submitted) — treat as a failed verification
296
+ if (onFail === 'allow') {
297
+ markVerified(session);
298
+ return mkResult({ verified: true, message: 'Verification skipped per server configuration.' });
299
+ }
300
+ return mkResult({ verified: false, error: err.body.error ?? 'verification_failed' });
301
+ }
302
+ // 5xx or transport error
303
+ if (onVpsError === 'allow') {
304
+ markVerified(session);
305
+ return mkResult({ verified: true, message: 'VPS unreachable — access allowed per server configuration.' });
306
+ }
307
+ return mkError(`fdkey_service_unavailable: verification service is temporarily unreachable. Retry in a few seconds or contact the server operator. (${String(err)})`);
308
+ }
309
+ session.pendingChallengeId = null;
310
+ if (result.verified && result.jwt) {
311
+ try {
312
+ const header = decodeProtectedHeader(result.jwt);
313
+ const kid = header.kid;
314
+ if (!kid)
315
+ throw new Error('JWT missing kid header');
316
+ const pubKey = await wellKnown.getKey(kid);
317
+ if (!pubKey)
318
+ throw new Error(`Unknown key id: ${kid}`);
319
+ // 30s clock tolerance — covers NTP drift between the VPS and the SDK host.
320
+ // Without this, JWTs with `nbf` slightly in the future (issuer clock ahead)
321
+ // get rejected by recipients with slightly slower clocks.
322
+ await jwtVerify(result.jwt, pubKey, { clockTolerance: 30 });
323
+ // Cache decoded claims so getFdkeyContext() can surface them to integrator handlers (G3)
324
+ session.lastClaims = decodeJwt(result.jwt);
325
+ }
326
+ catch (jwtErr) {
327
+ if (onFail === 'allow') {
328
+ markVerified(session);
329
+ return mkResult({ verified: true, message: 'Verification passed (JWT validation skipped).' });
330
+ }
331
+ return mkResult({
332
+ verified: false,
333
+ message: `Verification failed: invalid JWT — ${String(jwtErr)}`,
334
+ });
335
+ }
336
+ markVerified(session);
337
+ return mkResult({ verified: true, message: 'Verification passed. You can now access protected tools.' });
338
+ }
339
+ // VPS responded 200 with verified=false
340
+ if (onFail === 'allow') {
341
+ markVerified(session);
342
+ return mkResult({ verified: true, message: 'Verification failed but access allowed per server configuration.' });
343
+ }
344
+ return mkResult({ verified: false, message: 'Verification failed. Call fdkey_get_challenge to try again.' });
345
+ });
346
+ // --- Proxy: intercept registerTool() and deprecated tool() to gate protected tools ---
347
+ function makeInterceptor(targetFn) {
348
+ return function (...toolArgs) {
349
+ const name = toolArgs[0];
350
+ // Our injected tools are already registered — pass through unchanged
351
+ if (name === GET_CHALLENGE_TOOL || name === SUBMIT_CHALLENGE_TOOL) {
352
+ return Reflect.apply(targetFn, server, toolArgs);
353
+ }
354
+ const entry = protect[name];
355
+ if (!entry) {
356
+ return Reflect.apply(targetFn, server, toolArgs);
357
+ }
358
+ const idx = toolArgs.length - 1;
359
+ const original = toolArgs[idx];
360
+ const policy = normalisePolicy(entry.policy);
361
+ toolArgs[idx] = async (...cbArgs) => {
362
+ // extra is always the last cb arg: (args, extra) or (extra) for no-arg tools
363
+ const extra = cbArgs[cbArgs.length - 1];
364
+ const session = getSession(extra?.sessionId ?? 'stdio');
365
+ // Capture client info on every protected-tool call too — the agent
366
+ // may hit a gated tool before fdkey_get_challenge runs (the natural
367
+ // first-blocked-then-verify flow), and we still want metadata.
368
+ const meta = captureChallengeMeta(session, extra ?? {});
369
+ if (canCall(policy, name, session)) {
370
+ const result = await original(...cbArgs);
371
+ consumePolicy(policy, session);
372
+ return result;
373
+ }
374
+ if (inlineChallenge) {
375
+ try {
376
+ const challenge = await vpsClient.fetchChallenge(meta);
377
+ session.pendingChallengeId = challenge.challenge_id;
378
+ return mkError(`fdkey_verification_required. Solve the challenge below then call ${SUBMIT_CHALLENGE_TOOL} with your answers, then retry this tool.\n` +
379
+ JSON.stringify(challengePayload(challenge)));
380
+ }
381
+ catch {
382
+ if (onVpsError === 'allow')
383
+ return original(...cbArgs);
384
+ return mkError('fdkey_service_unavailable: verification service is temporarily unreachable. Retry in a few seconds or contact the server operator.');
385
+ }
386
+ }
387
+ return mkError(`fdkey_verification_required. Call ${GET_CHALLENGE_TOOL} to start verification, then ${SUBMIT_CHALLENGE_TOOL} with your answers, then retry this tool.`);
388
+ };
389
+ return Reflect.apply(targetFn, server, toolArgs);
390
+ };
391
+ }
392
+ const wrapped = new Proxy(server, {
393
+ get(target, prop, receiver) {
394
+ if (prop === 'registerTool') {
395
+ return makeInterceptor(target.registerTool);
396
+ }
397
+ if (prop === 'tool') {
398
+ return makeInterceptor(target.tool);
399
+ }
400
+ return Reflect.get(target, prop, receiver);
401
+ },
402
+ });
403
+ // Register both the original and the wrapped reference so getFdkeyContext()
404
+ // works regardless of which the integrator passes.
405
+ FDKEY_REGISTRY.set(server, store);
406
+ FDKEY_REGISTRY.set(wrapped, store);
407
+ return wrapped;
408
+ }
409
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { IVpsRouter, RoutingTarget } from './types.js';
2
+ /** Used when the SDK consumer passes an explicit `vpsUrl` (or accepts the
3
+ * default `https://api.fdkey.com`). Bypasses all discovery and probing —
4
+ * fetch uses its default DNS resolution against whatever hostname (or IP)
5
+ * is in the URL. No `dispatcher` is set, so this works on every runtime
6
+ * that has a global `fetch` (Node 18+, Cloudflare Workers, Bun, Deno). */
7
+ export declare class StaticRouter implements IVpsRouter {
8
+ private readonly url;
9
+ constructor(url: string);
10
+ getTarget(): Promise<RoutingTarget>;
11
+ recordFailure(_ip: string | undefined): void;
12
+ }
13
+ //# sourceMappingURL=router-static.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router-static.d.ts","sourceRoot":"","sources":["../src/router-static.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE5D;;;;2EAI2E;AAC3E,qBAAa,YAAa,YAAW,UAAU;IACjC,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,EAAE,MAAM;IAClC,SAAS,IAAI,OAAO,CAAC,aAAa,CAAC;IAGzC,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;CAC7C"}
@@ -0,0 +1,16 @@
1
+ /** Used when the SDK consumer passes an explicit `vpsUrl` (or accepts the
2
+ * default `https://api.fdkey.com`). Bypasses all discovery and probing —
3
+ * fetch uses its default DNS resolution against whatever hostname (or IP)
4
+ * is in the URL. No `dispatcher` is set, so this works on every runtime
5
+ * that has a global `fetch` (Node 18+, Cloudflare Workers, Bun, Deno). */
6
+ export class StaticRouter {
7
+ url;
8
+ constructor(url) {
9
+ this.url = url;
10
+ }
11
+ async getTarget() {
12
+ return { url: this.url };
13
+ }
14
+ recordFailure(_ip) { }
15
+ }
16
+ //# sourceMappingURL=router-static.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router-static.js","sourceRoot":"","sources":["../src/router-static.ts"],"names":[],"mappings":"AAEA;;;;2EAI2E;AAC3E,MAAM,OAAO,YAAY;IACM;IAA7B,YAA6B,GAAW;QAAX,QAAG,GAAH,GAAG,CAAQ;IAAG,CAAC;IAC5C,KAAK,CAAC,SAAS;QACb,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC;IAC3B,CAAC;IACD,aAAa,CAAC,GAAuB,IAAS,CAAC;CAChD"}
@@ -0,0 +1,45 @@
1
+ import type { SessionState } from './types.js';
2
+ /** Drop sessions that have been idle this long. One hour is comfortably
3
+ * longer than the default JWT lifetime (~5 min) plus typical agent
4
+ * retry/backoff windows, so an active session is never evicted while
5
+ * it could legitimately make another call. */
6
+ export declare const SESSION_IDLE_TTL_MS: number;
7
+ /** Hard cap on the per-server session map. Hit this only on pathological
8
+ * burst traffic — even at this size the eviction is O(1) thanks to the
9
+ * JS Map's preserved-insertion-order semantics. */
10
+ export declare const MAX_SESSIONS = 10000;
11
+ export interface SessionStore {
12
+ /** Get-or-create the session for `id`, sliding the LRU position to the
13
+ * tail. Opportunistically evicts one stale (TTL-expired) entry per
14
+ * miss, and force-evicts the LRU when at the size cap. */
15
+ get(id: string): SessionState;
16
+ /** Number of currently-live sessions. Test-only convenience. */
17
+ size(): number;
18
+ /** Read-only access to a session without sliding LRU position. Returns
19
+ * undefined if the session doesn't exist. Used by `getFdkeyContext` so
20
+ * reading verification context doesn't extend a session's lifetime. */
21
+ peek(id: string): SessionState | undefined;
22
+ }
23
+ /** Bounded session store. Memory is bounded by `maxSize` (the hard guarantee).
24
+ * Two eviction policies cooperate:
25
+ *
26
+ * 1. **TTL sweep on miss.** When a brand-new session arrives, the head
27
+ * of the map (always the LRU by insertion-order) is checked: if it
28
+ * has been idle longer than `idleTtlMs`, it's dropped. Sweep is
29
+ * O(1) per miss and only fires on misses — a busy session with
30
+ * many hits doesn't trigger it. This means stale entries can sit
31
+ * in the map longer than `idleTtlMs` if the server only ever sees
32
+ * hits; they're dropped opportunistically as new sessions arrive.
33
+ *
34
+ * 2. **LRU cap on insert.** When `sessions.size === maxSize`, the head
35
+ * (LRU) entry is force-dropped to make room — regardless of age.
36
+ * O(1) thanks to the JS Map's preserved-insertion-order semantics.
37
+ * This is the actual memory bound: maps NEVER exceed `maxSize`.
38
+ *
39
+ * Net guarantee: long-lived MCP servers stay capped at `maxSize` entries
40
+ * (default 10k × ~200 bytes ≈ 2 MB) regardless of transport-level
41
+ * disconnect signals. Idle entries below the cap may linger past their
42
+ * TTL until pressure forces them out — that's acceptable because the
43
+ * cap itself is the safety property. */
44
+ export declare function createSessionStore(maxSize?: number, idleTtlMs?: number, now?: () => number): SessionStore;
45
+ //# sourceMappingURL=session-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-store.d.ts","sourceRoot":"","sources":["../src/session-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C;;;+CAG+C;AAC/C,eAAO,MAAM,mBAAmB,QAAiB,CAAC;AAClD;;oDAEoD;AACpD,eAAO,MAAM,YAAY,QAAS,CAAC;AAEnC,MAAM,WAAW,YAAY;IAC3B;;+DAE2D;IAC3D,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,CAAC;IAC9B,gEAAgE;IAChE,IAAI,IAAI,MAAM,CAAC;IACf;;4EAEwE;IACxE,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAAC;CAC5C;AAED;;;;;;;;;;;;;;;;;;;;yCAoByC;AACzC,wBAAgB,kBAAkB,CAChC,OAAO,GAAE,MAAqB,EAC9B,SAAS,GAAE,MAA4B,EACvC,GAAG,GAAE,MAAM,MAAiB,GAC3B,YAAY,CAwCd"}
@@ -0,0 +1,71 @@
1
+ import { newSession } from './guard.js';
2
+ /** Drop sessions that have been idle this long. One hour is comfortably
3
+ * longer than the default JWT lifetime (~5 min) plus typical agent
4
+ * retry/backoff windows, so an active session is never evicted while
5
+ * it could legitimately make another call. */
6
+ export const SESSION_IDLE_TTL_MS = 60 * 60 * 1000;
7
+ /** Hard cap on the per-server session map. Hit this only on pathological
8
+ * burst traffic — even at this size the eviction is O(1) thanks to the
9
+ * JS Map's preserved-insertion-order semantics. */
10
+ export const MAX_SESSIONS = 10_000;
11
+ /** Bounded session store. Memory is bounded by `maxSize` (the hard guarantee).
12
+ * Two eviction policies cooperate:
13
+ *
14
+ * 1. **TTL sweep on miss.** When a brand-new session arrives, the head
15
+ * of the map (always the LRU by insertion-order) is checked: if it
16
+ * has been idle longer than `idleTtlMs`, it's dropped. Sweep is
17
+ * O(1) per miss and only fires on misses — a busy session with
18
+ * many hits doesn't trigger it. This means stale entries can sit
19
+ * in the map longer than `idleTtlMs` if the server only ever sees
20
+ * hits; they're dropped opportunistically as new sessions arrive.
21
+ *
22
+ * 2. **LRU cap on insert.** When `sessions.size === maxSize`, the head
23
+ * (LRU) entry is force-dropped to make room — regardless of age.
24
+ * O(1) thanks to the JS Map's preserved-insertion-order semantics.
25
+ * This is the actual memory bound: maps NEVER exceed `maxSize`.
26
+ *
27
+ * Net guarantee: long-lived MCP servers stay capped at `maxSize` entries
28
+ * (default 10k × ~200 bytes ≈ 2 MB) regardless of transport-level
29
+ * disconnect signals. Idle entries below the cap may linger past their
30
+ * TTL until pressure forces them out — that's acceptable because the
31
+ * cap itself is the safety property. */
32
+ export function createSessionStore(maxSize = MAX_SESSIONS, idleTtlMs = SESSION_IDLE_TTL_MS, now = Date.now) {
33
+ const sessions = new Map();
34
+ return {
35
+ get(id) {
36
+ const t = now();
37
+ const existing = sessions.get(id);
38
+ if (existing) {
39
+ // Slide to the tail of the LRU order: delete + re-insert is O(1).
40
+ sessions.delete(id);
41
+ existing.lastTouchedAt = t;
42
+ sessions.set(id, existing);
43
+ return existing;
44
+ }
45
+ // Miss → opportunistic TTL sweep of the head, then enforce the cap.
46
+ const head = sessions.keys().next().value;
47
+ if (head !== undefined) {
48
+ const headSession = sessions.get(head);
49
+ if (headSession && t - headSession.lastTouchedAt > idleTtlMs) {
50
+ sessions.delete(head);
51
+ }
52
+ }
53
+ if (sessions.size >= maxSize) {
54
+ const lruKey = sessions.keys().next().value;
55
+ if (lruKey !== undefined)
56
+ sessions.delete(lruKey);
57
+ }
58
+ const fresh = newSession();
59
+ fresh.lastTouchedAt = t;
60
+ sessions.set(id, fresh);
61
+ return fresh;
62
+ },
63
+ peek(id) {
64
+ return sessions.get(id);
65
+ },
66
+ size() {
67
+ return sessions.size;
68
+ },
69
+ };
70
+ }
71
+ //# sourceMappingURL=session-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-store.js","sourceRoot":"","sources":["../src/session-store.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC;;;+CAG+C;AAC/C,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAClD;;oDAEoD;AACpD,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC;AAenC;;;;;;;;;;;;;;;;;;;;yCAoByC;AACzC,MAAM,UAAU,kBAAkB,CAChC,UAAkB,YAAY,EAC9B,YAAoB,mBAAmB,EACvC,MAAoB,IAAI,CAAC,GAAG;IAE5B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAwB,CAAC;IAEjD,OAAO;QACL,GAAG,CAAC,EAAU;YACZ,MAAM,CAAC,GAAG,GAAG,EAAE,CAAC;YAChB,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAClC,IAAI,QAAQ,EAAE,CAAC;gBACb,kEAAkE;gBAClE,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACpB,QAAQ,CAAC,aAAa,GAAG,CAAC,CAAC;gBAC3B,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;gBAC3B,OAAO,QAAQ,CAAC;YAClB,CAAC;YACD,oEAAoE;YACpE,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;YAC1C,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACvB,MAAM,WAAW,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBACvC,IAAI,WAAW,IAAI,CAAC,GAAG,WAAW,CAAC,aAAa,GAAG,SAAS,EAAE,CAAC;oBAC7D,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBACxB,CAAC;YACH,CAAC;YACD,IAAI,QAAQ,CAAC,IAAI,IAAI,OAAO,EAAE,CAAC;gBAC7B,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;gBAC5C,IAAI,MAAM,KAAK,SAAS;oBAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACpD,CAAC;YACD,MAAM,KAAK,GAAG,UAAU,EAAE,CAAC;YAC3B,KAAK,CAAC,aAAa,GAAG,CAAC,CAAC;YACxB,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;YACxB,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,CAAC,EAAU;YACb,OAAO,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC1B,CAAC;QAED,IAAI;YACF,OAAO,QAAQ,CAAC,IAAI,CAAC;QACvB,CAAC;KACF,CAAC;AACJ,CAAC"}