@j0hanz/filesystem-mcp 2.7.0 → 2.7.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 +1 -1
- package/dist/core/concurrency.d.ts.map +1 -1
- package/dist/core/concurrency.js +53 -37
- package/dist/core/concurrency.js.map +1 -1
- package/dist/core/glob.d.ts.map +1 -1
- package/dist/core/glob.js +16 -5
- package/dist/core/glob.js.map +1 -1
- package/dist/core/input-required.d.ts +21 -16
- package/dist/core/input-required.d.ts.map +1 -1
- package/dist/core/input-required.js +12 -12
- package/dist/core/input-required.js.map +1 -1
- package/dist/core/path-completer.d.ts.map +1 -1
- package/dist/core/path-completer.js +7 -4
- package/dist/core/path-completer.js.map +1 -1
- package/dist/core/search.d.ts.map +1 -1
- package/dist/core/search.js +23 -15
- package/dist/core/search.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +7 -1
- package/dist/server.js.map +1 -1
- package/dist/tools/batch.d.ts +1 -0
- package/dist/tools/batch.d.ts.map +1 -1
- package/dist/tools/batch.js +5 -9
- package/dist/tools/batch.js.map +1 -1
- package/dist/tools/create.d.ts.map +1 -1
- package/dist/tools/create.js +2 -6
- package/dist/tools/create.js.map +1 -1
- package/dist/tools/define.d.ts.map +1 -1
- package/dist/tools/define.js +1 -5
- package/dist/tools/define.js.map +1 -1
- package/dist/tools/delete.d.ts.map +1 -1
- package/dist/tools/delete.js +4 -12
- package/dist/tools/delete.js.map +1 -1
- package/dist/tools/move.d.ts.map +1 -1
- package/dist/tools/move.js +2 -6
- package/dist/tools/move.js.map +1 -1
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +23 -75
- package/dist/tools/read.js.map +1 -1
- package/dist/transport/http.d.ts.map +1 -1
- package/dist/transport/http.js +2 -18
- package/dist/transport/http.js.map +1 -1
- package/dist/transport/shared.d.ts +3 -1
- package/dist/transport/shared.d.ts.map +1 -1
- package/dist/transport/shared.js +14 -1
- package/dist/transport/shared.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/transport/http.ts"],"names":[],"mappings":"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","sourcesContent":["// HTTP hosting: express app assembly, security middleware wiring, the modern\n// per-request server factory, and listen-filter watcher gating on POST /mcp.\nimport {\n hostHeaderValidation,\n localhostOriginValidation,\n originValidation,\n} from '@modelcontextprotocol/express';\nimport { toNodeHandler } from '@modelcontextprotocol/node';\nimport type { McpHttpHandler, ServerNotifier } from '@modelcontextprotocol/server';\nimport {\n createMcpHandler,\n DEFAULT_REQUEST_TIMEOUT_MSEC,\n isJsonContentType,\n ProtocolErrorCode,\n} from '@modelcontextprotocol/server';\n\nimport type { Server } from 'node:http';\nimport { createServer as createHttpServer } from 'node:http';\n\nimport express from 'express';\nimport type { Express, NextFunction, Request, Response } from 'express';\n\nimport { formatUnknownErrorMessage } from '../core/errors.ts';\nimport { Logger } from '../core/observability.ts';\nimport { parseTrueEnvFlag } from '../core/path-utils.ts';\nimport { PathGuard } from '../core/path.ts';\nimport type { ServerOptions } from '../core/path.ts';\nimport { PageSnapshotStore, ResourceStore } from '../core/store.ts';\nimport { getMaxInboundMessageBytes, parseEnvInt } from '../core/util.ts';\nimport {\n createWatcherRegistry,\n MAX_WATCHERS,\n type WatcherRegistry,\n} from '../core/watcher-registry.ts';\nimport { createServer } from '../server.ts';\nimport {\n assertHttpBindingPolicy,\n assertHttpHostPolicy,\n bearerAuthMiddleware,\n computeAllowedOriginHostnames,\n corsMiddleware,\n createRateLimiter,\n isLoopbackHttpHost,\n JSONRPC_SERVER_ERROR,\n protectedResourceUrl,\n resolveAllowedHosts,\n resolveTrustProxySetting,\n sendJsonRpcError,\n} from './http-policy.ts';\nimport type { RuntimeConfig } from './shared.ts';\nimport { jsonRpcRequestId, listenSubscriptionUris, prepareListenWatchers } from './shared.ts';\n\n// Must exceed the idle timeout of any proxy in front of this server, or the\n// proxy reuses connections the server already closed (intermittent 502s).\nconst KEEPALIVE_TIMEOUT_MS = 5_000;\n\n/** Body-parser rejections and anything else that reaches the end of the chain. */\nfunction errorHandlerMiddleware(\n err: Error & { status?: number },\n _req: Request,\n res: Response,\n next: NextFunction,\n): void {\n if (res.headersSent) {\n next(err);\n return;\n }\n if (err.status === 413) {\n sendJsonRpcError(\n res,\n 413,\n ProtocolErrorCode.InvalidRequest,\n `Request body too large: the limit is ${String(getMaxInboundMessageBytes())} bytes (3 × FS_MAX_FILE_SIZE + 1 MiB). Split the write into smaller calls.`,\n );\n return;\n }\n if (err.status === 400) {\n sendJsonRpcError(res, 400, ProtocolErrorCode.ParseError, 'Invalid JSON in request body');\n return;\n }\n Logger.error('[HTTP] Unhandled middleware error:', formatUnknownErrorMessage(err));\n sendJsonRpcError(res, 500, ProtocolErrorCode.InternalError, 'Internal Server Error');\n}\n\nfunction setupExpressApp(\n httpHost: string,\n apiKey: string | undefined,\n allowedHosts: string[],\n modernNodeHandler: (req: Request, res: Response, parsedBody?: unknown) => Promise<void>,\n sharedPathGuard: PathGuard,\n sharedRegistry: WatcherRegistry,\n notifier: ServerNotifier,\n): Express {\n const allowedOriginHostnames = computeAllowedOriginHostnames(process.env['FS_ALLOWED_ORIGINS']);\n // Read once here and passed down, like every other env-derived policy input:\n // http-policy holds no state and reads no env of its own.\n const publicUrl = process.env['FS_PUBLIC_URL'];\n\n // Assembled from the SDK's own gates rather than `createMcpExpressApp`,\n // which mounts `express.json()` for the whole app: that would parse every\n // body before the rate limiter and bearer auth below can refuse it. Host\n // and Origin validation still run first and app-wide, mounted by the same\n // rules `createMcpExpressApp` applies to these inputs.\n const app = express();\n if (allowedHosts.length > 0) app.use(hostHeaderValidation(allowedHosts));\n if (allowedOriginHostnames.length > 0) {\n app.use(originValidation(allowedOriginHostnames));\n } else if (isLoopbackHttpHost(httpHost)) {\n // An all-blank FS_ALLOWED_ORIGINS on a loopback bind keeps the localhost\n // default (see isOriginAllowed in http-policy.ts).\n app.use(localhostOriginValidation());\n }\n\n const trustProxy = resolveTrustProxySetting(process.env['FS_TRUST_PROXY']);\n if (trustProxy !== undefined) {\n app.set('trust proxy', trustProxy);\n }\n\n if (allowedHosts.length === 0) {\n Logger.warn(\n '[HTTP] FS_ALLOW_UNRESTRICTED_HOSTS is set: binding globally without Host validation.',\n );\n }\n\n app.use('/mcp', corsMiddleware(allowedOriginHostnames));\n\n // Unconditional: the spec's rate-limit MUST is not scoped to authenticated\n // binds. A keyless bind is loopback-only, so the cap is looser, not absent.\n const rpm = parseEnvInt('FS_RATE_LIMIT_RPM', apiKey ? 120 : 6_000, 1, 100_000);\n app.use('/mcp', createRateLimiter(rpm));\n\n if (apiKey && allowedHosts.length > 0) {\n const metadataHandler = (req: Request, res: Response): void => {\n const resource = protectedResourceUrl(req, true, publicUrl);\n if (!resource) {\n // Unreachable for the unrestricted case (the gate above excludes it),\n // but defend a FS_PUBLIC_URL parse failure with a bodyless\n // 404 — a generic OAuth client cannot parse a JSON-RPC error envelope.\n res.status(404).end();\n return;\n }\n res.header('Access-Control-Allow-Origin', '*');\n res.status(200).json({\n resource: resource.href,\n bearer_methods_supported: ['header'],\n resource_name: 'filesystem-mcp',\n });\n };\n app.get(\n ['/.well-known/oauth-protected-resource', '/.well-known/oauth-protected-resource/mcp'],\n metadataHandler,\n );\n }\n\n app.get('/healthz', (_req: Request, res: Response) => {\n res.status(200).json({\n status: 'ok',\n uptime: process.uptime(),\n });\n });\n\n app.use('/mcp', bearerAuthMiddleware(apiKey, allowedHosts.length > 0, publicUrl));\n\n const resourceUpdateSink = (uri: string): void => {\n notifier.resourceUpdated(uri);\n };\n\n // One bound for both transports, derived from the write tools' own limit\n // (getMaxInboundMessageBytes): a create the schema accepts must not be\n // refused here. Mounted after CORS, the rate limiter and auth, so a refused\n // request is answered without its body being read. The SDK's handler and\n // adapter get the same number below so their internal defaults (4 MiB)\n // cannot disagree with this parser, although both only read a body when no\n // parsedBody is supplied — which this route never does.\n const parseJson = express.json({ limit: `${String(getMaxInboundMessageBytes())}b` });\n\n app.post('/mcp', parseJson, (req: Request, res: Response, next: NextFunction) => {\n void (async () => {\n if (!isJsonContentType(req.headers['content-type'])) {\n sendJsonRpcError(\n res,\n 415,\n JSONRPC_SERVER_ERROR,\n 'Unsupported Media Type: Content-Type must be application/json',\n );\n return;\n }\n const parsedBody = req.body as unknown;\n if (parsedBody === undefined) {\n // Undefined tells the Node adapter to read the raw stream without our parser's limit.\n sendJsonRpcError(res, 400, ProtocolErrorCode.ParseError, 'Invalid JSON in request body');\n return;\n }\n // Only a structurally valid listen gets watchers, mirroring the stdio\n // gate. A malformed one cannot succeed downstream, so attaching handles\n // for it only gives the response-close release something to undo — and\n // everything that is not a listen takes no watchers either way.\n const requestedUris = listenSubscriptionUris(parsedBody);\n if (requestedUris === undefined) {\n await modernNodeHandler(req, res, parsedBody);\n return;\n }\n\n // Reject an over-cap listen before the ack so the client does not believe\n // every requested URI is watched when the watcher budget is exhausted.\n // The per-URI `capped` failure below would also reject, but only after\n // creating and tearing down every watcher up to the cap.\n // Only URIs without a live watcher consume a slot; a re-listen to an\n // already-watched URI just retains another lease.\n const newUris = requestedUris.filter((uri) => !sharedRegistry.hasWatcher(uri));\n const available = MAX_WATCHERS - sharedRegistry.size();\n if (newUris.length > available) {\n sendJsonRpcError(\n res,\n 400,\n ProtocolErrorCode.InvalidParams,\n `subscriptions/listen names ${newUris.length} not-yet-watched URIs but only ${available} watcher slots remain (cap ${MAX_WATCHERS}). Reduce the resourceSubscriptions list.`,\n jsonRpcRequestId(parsedBody),\n );\n return;\n }\n\n const prepared = await prepareListenWatchers(\n requestedUris,\n sharedPathGuard,\n sharedRegistry,\n resourceUpdateSink,\n );\n if (!prepared.ok) {\n sendJsonRpcError(\n res,\n 400,\n ProtocolErrorCode.InvalidParams,\n prepared.message,\n jsonRpcRequestId(parsedBody),\n );\n return;\n }\n\n let released = false;\n const release = (): void => {\n if (released) return;\n released = true;\n for (const uri of prepared.acquiredUris) sharedRegistry.release(uri);\n };\n if (prepared.acquiredUris.length > 0) {\n // The attach loop awaits per-URI path validation, so the client can be\n // gone by the time it returns. `close` fires once: a listener attached\n // after it would never run and every lease this batch took would leak\n // until process shutdown.\n if (res.closed || res.destroyed || res.writableEnded) release();\n else res.once('close', release);\n }\n\n try {\n await modernNodeHandler(req, res, parsedBody);\n } catch (error) {\n release();\n throw error;\n }\n })().catch(next);\n });\n\n // Answer non-POST here rather than handing it to `modernNodeHandler`. The\n // SDK's own 405 is byte-identical to this one, but it omits the `Allow`\n // header RFC 9110 §15.5.6 requires and routes every routine GET probe through\n // the handler's `onerror` — which this server logs at error level.\n app.all('/mcp', (_req: Request, res: Response) => {\n sendJsonRpcError(res, 405, JSONRPC_SERVER_ERROR, 'Method not allowed.', null, {\n Allow: 'POST, OPTIONS',\n });\n });\n\n app.use(errorHandlerMiddleware);\n\n return app;\n}\n\nexport async function startHttpServer(\n port: number,\n options: ServerOptions,\n config: RuntimeConfig = {},\n): Promise<Server> {\n const httpHost = config.httpHost ?? '127.0.0.1';\n const { apiKey } = config;\n assertHttpBindingPolicy(httpHost, apiKey);\n const allowedHosts = resolveAllowedHosts(httpHost, process.env['FS_ALLOWED_HOSTS']);\n assertHttpHostPolicy(\n httpHost,\n allowedHosts,\n parseTrueEnvFlag(process.env['FS_ALLOW_UNRESTRICTED_HOSTS'], 'FS_ALLOW_UNRESTRICTED_HOSTS'),\n );\n\n const sharedRegistry = createWatcherRegistry();\n // One store for the whole endpoint, same lifetime and same one-credential\n // trust argument as the shared guard below: a result a tool externalized in\n // one POST must survive to the follow-up resources/read, and the modern leg\n // builds a fresh McpServer per request so a per-request store would discard\n // it immediately.\n // Fires only from a tool call, which is long after `modernHandler` below is\n // constructed, so reading it from the closure is safe.\n const sharedStore = new ResourceStore(() => {\n modernHandler.notify.resourcesChanged();\n });\n const sharedPageStore = new PageSnapshotStore();\n // One guard for the whole endpoint. The modern leg builds a fresh McpServer\n // per request, so a per-instance guard would discard every accepted access\n // grant the moment the request ended — re-prompting on each subsequent call\n // and leaving the listen-watcher path validating against a stale allowed set.\n // Grant scope is therefore the endpoint, not the connection: with FS_API_KEY set\n // every caller presents the same key (one auth context by construction), and\n // without it the bind is loopback-only. Split into per-auth-context guards if\n // this ever serves more than one credential.\n const sharedPathGuard = new PathGuard(options);\n await sharedPathGuard.recomputeAllowedDirectories();\n\n const maxRequestBodySize = getMaxInboundMessageBytes();\n\n const modernHandler: McpHttpHandler = createMcpHandler(\n async ({ era }) => {\n const c = await createServer(options, {\n watcherRegistry: sharedRegistry,\n notifier: modernHandler.notify,\n pathGuard: sharedPathGuard,\n resourceStore: sharedStore,\n pageStore: sharedPageStore,\n era,\n ...(apiKey !== undefined ? { apiKey } : {}),\n });\n const previousOnClose = c.mcp.server.onclose;\n c.mcp.server.onclose = () => {\n previousOnClose?.();\n c.disposeRuntimeState();\n };\n return c.mcp;\n },\n {\n // The SDK's default: a 2025-era request is answered by a fresh instance\n // from the same factory, no session. What that leg cannot do — answer an\n // input_required confirmation, deliver a subscription — is refused with a\n // named workaround (define.ts, resources.ts). See docs/adr/003.\n legacy: 'stateless',\n maxRequestBodySize,\n onerror: (error: Error) => {\n Logger.error('[HTTP] modern leg error:', formatUnknownErrorMessage(error));\n },\n },\n );\n const modernNodeHandler = toNodeHandler(modernHandler, {\n maxRequestBodySize,\n // The adapter answers 500 when request conversion or handler.fetch\n // itself throws (e.g. a request racing handler.close()); the entry's\n // own onerror above never sees those. Log the adapter-level refusal so\n // the 500 is not silent.\n onerror: (error: Error) => {\n Logger.error('[HTTP] node adapter error:', formatUnknownErrorMessage(error));\n },\n });\n\n const app = setupExpressApp(\n httpHost,\n apiKey,\n allowedHosts,\n modernNodeHandler,\n sharedPathGuard,\n sharedRegistry,\n modernHandler.notify,\n );\n\n const httpServer = createHttpServer(app);\n httpServer.keepAliveTimeout = KEEPALIVE_TIMEOUT_MS;\n httpServer.headersTimeout = KEEPALIVE_TIMEOUT_MS + 5_000;\n httpServer.requestTimeout = DEFAULT_REQUEST_TIMEOUT_MSEC;\n\n const onHttpServerError = (error: Error): void => {\n Logger.error('[HTTP] runtime server error', {\n host: httpHost,\n port,\n error: formatUnknownErrorMessage(error),\n });\n };\n httpServer.on('error', onHttpServerError);\n\n const teardown = (): Promise<void> => {\n sharedRegistry.destroy();\n sharedPageStore.clear();\n return modernHandler.close().catch((err: unknown) => {\n Logger.error('[HTTP] Error closing handler:', formatUnknownErrorMessage(err));\n });\n };\n const originalClose = httpServer.close.bind(httpServer);\n httpServer.close = function (callback?: (error?: Error) => void) {\n void teardown().then(() => {\n originalClose(callback);\n });\n return httpServer;\n };\n\n return new Promise((resolve, reject) => {\n const onError = (err: Error) => {\n void teardown();\n reject(err);\n };\n httpServer.once('error', onError);\n\n httpServer.listen(port, httpHost, () => {\n httpServer.removeListener('error', onError);\n Logger.info(`[HTTP] Server listening on http://${httpHost}:${port}`);\n resolve(httpServer);\n });\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/transport/http.ts"],"names":[],"mappings":"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","sourcesContent":["// HTTP hosting: express app assembly, security middleware wiring, the modern\n// per-request server factory, and listen-filter watcher gating on POST /mcp.\nimport {\n hostHeaderValidation,\n localhostOriginValidation,\n originValidation,\n} from '@modelcontextprotocol/express';\nimport { toNodeHandler } from '@modelcontextprotocol/node';\nimport type { McpHttpHandler, ServerNotifier } from '@modelcontextprotocol/server';\nimport {\n createMcpHandler,\n DEFAULT_REQUEST_TIMEOUT_MSEC,\n isJsonContentType,\n ProtocolErrorCode,\n} from '@modelcontextprotocol/server';\n\nimport type { Server } from 'node:http';\nimport { createServer as createHttpServer } from 'node:http';\n\nimport express from 'express';\nimport type { Express, NextFunction, Request, Response } from 'express';\n\nimport { formatUnknownErrorMessage } from '../core/errors.ts';\nimport { Logger } from '../core/observability.ts';\nimport { parseTrueEnvFlag } from '../core/path-utils.ts';\nimport { PathGuard } from '../core/path.ts';\nimport type { ServerOptions } from '../core/path.ts';\nimport { PageSnapshotStore, ResourceStore } from '../core/store.ts';\nimport { getMaxInboundMessageBytes, parseEnvInt } from '../core/util.ts';\nimport { createWatcherRegistry, type WatcherRegistry } from '../core/watcher-registry.ts';\nimport { createServer } from '../server.ts';\nimport {\n assertHttpBindingPolicy,\n assertHttpHostPolicy,\n bearerAuthMiddleware,\n computeAllowedOriginHostnames,\n corsMiddleware,\n createRateLimiter,\n isLoopbackHttpHost,\n JSONRPC_SERVER_ERROR,\n protectedResourceUrl,\n resolveAllowedHosts,\n resolveTrustProxySetting,\n sendJsonRpcError,\n} from './http-policy.ts';\nimport type { RuntimeConfig } from './shared.ts';\nimport { jsonRpcRequestId, listenSubscriptionUris, prepareListenWatchers } from './shared.ts';\n\n// Must exceed the idle timeout of any proxy in front of this server, or the\n// proxy reuses connections the server already closed (intermittent 502s).\nconst KEEPALIVE_TIMEOUT_MS = 5_000;\n\n/** Body-parser rejections and anything else that reaches the end of the chain. */\nfunction errorHandlerMiddleware(\n err: Error & { status?: number },\n _req: Request,\n res: Response,\n next: NextFunction,\n): void {\n if (res.headersSent) {\n next(err);\n return;\n }\n if (err.status === 413) {\n sendJsonRpcError(\n res,\n 413,\n ProtocolErrorCode.InvalidRequest,\n `Request body too large: the limit is ${String(getMaxInboundMessageBytes())} bytes (3 × FS_MAX_FILE_SIZE + 1 MiB). Split the write into smaller calls.`,\n );\n return;\n }\n if (err.status === 400) {\n sendJsonRpcError(res, 400, ProtocolErrorCode.ParseError, 'Invalid JSON in request body');\n return;\n }\n Logger.error('[HTTP] Unhandled middleware error:', formatUnknownErrorMessage(err));\n sendJsonRpcError(res, 500, ProtocolErrorCode.InternalError, 'Internal Server Error');\n}\n\nfunction setupExpressApp(\n httpHost: string,\n apiKey: string | undefined,\n allowedHosts: string[],\n modernNodeHandler: (req: Request, res: Response, parsedBody?: unknown) => Promise<void>,\n sharedPathGuard: PathGuard,\n sharedRegistry: WatcherRegistry,\n notifier: ServerNotifier,\n): Express {\n const allowedOriginHostnames = computeAllowedOriginHostnames(process.env['FS_ALLOWED_ORIGINS']);\n // Read once here and passed down, like every other env-derived policy input:\n // http-policy holds no state and reads no env of its own.\n const publicUrl = process.env['FS_PUBLIC_URL'];\n\n // Assembled from the SDK's own gates rather than `createMcpExpressApp`,\n // which mounts `express.json()` for the whole app: that would parse every\n // body before the rate limiter and bearer auth below can refuse it. Host\n // and Origin validation still run first and app-wide, mounted by the same\n // rules `createMcpExpressApp` applies to these inputs.\n const app = express();\n if (allowedHosts.length > 0) app.use(hostHeaderValidation(allowedHosts));\n if (allowedOriginHostnames.length > 0) {\n app.use(originValidation(allowedOriginHostnames));\n } else if (isLoopbackHttpHost(httpHost)) {\n // An all-blank FS_ALLOWED_ORIGINS on a loopback bind keeps the localhost\n // default (see isOriginAllowed in http-policy.ts).\n app.use(localhostOriginValidation());\n }\n\n const trustProxy = resolveTrustProxySetting(process.env['FS_TRUST_PROXY']);\n if (trustProxy !== undefined) {\n app.set('trust proxy', trustProxy);\n }\n\n if (allowedHosts.length === 0) {\n Logger.warn(\n '[HTTP] FS_ALLOW_UNRESTRICTED_HOSTS is set: binding globally without Host validation.',\n );\n }\n\n app.use('/mcp', corsMiddleware(allowedOriginHostnames));\n\n // Unconditional: the spec's rate-limit MUST is not scoped to authenticated\n // binds. A keyless bind is loopback-only, so the cap is looser, not absent.\n const rpm = parseEnvInt('FS_RATE_LIMIT_RPM', apiKey ? 120 : 6_000, 1, 100_000);\n app.use('/mcp', createRateLimiter(rpm));\n\n if (apiKey && allowedHosts.length > 0) {\n const metadataHandler = (req: Request, res: Response): void => {\n const resource = protectedResourceUrl(req, true, publicUrl);\n if (!resource) {\n // Unreachable for the unrestricted case (the gate above excludes it),\n // but defend a FS_PUBLIC_URL parse failure with a bodyless\n // 404 — a generic OAuth client cannot parse a JSON-RPC error envelope.\n res.status(404).end();\n return;\n }\n res.header('Access-Control-Allow-Origin', '*');\n res.status(200).json({\n resource: resource.href,\n bearer_methods_supported: ['header'],\n resource_name: 'filesystem-mcp',\n });\n };\n app.get(\n ['/.well-known/oauth-protected-resource', '/.well-known/oauth-protected-resource/mcp'],\n metadataHandler,\n );\n }\n\n app.get('/healthz', (_req: Request, res: Response) => {\n res.status(200).json({\n status: 'ok',\n uptime: process.uptime(),\n });\n });\n\n app.use('/mcp', bearerAuthMiddleware(apiKey, allowedHosts.length > 0, publicUrl));\n\n const resourceUpdateSink = (uri: string): void => {\n notifier.resourceUpdated(uri);\n };\n\n // One bound for both transports, derived from the write tools' own limit\n // (getMaxInboundMessageBytes): a create the schema accepts must not be\n // refused here. Mounted after CORS, the rate limiter and auth, so a refused\n // request is answered without its body being read. The SDK's handler and\n // adapter get the same number below so their internal defaults (4 MiB)\n // cannot disagree with this parser, although both only read a body when no\n // parsedBody is supplied — which this route never does.\n const parseJson = express.json({ limit: `${String(getMaxInboundMessageBytes())}b` });\n\n app.post('/mcp', parseJson, (req: Request, res: Response, next: NextFunction) => {\n void (async () => {\n if (!isJsonContentType(req.headers['content-type'])) {\n sendJsonRpcError(\n res,\n 415,\n JSONRPC_SERVER_ERROR,\n 'Unsupported Media Type: Content-Type must be application/json',\n );\n return;\n }\n const parsedBody = req.body as unknown;\n if (parsedBody === undefined) {\n // Undefined tells the Node adapter to read the raw stream without our parser's limit.\n sendJsonRpcError(res, 400, ProtocolErrorCode.ParseError, 'Invalid JSON in request body');\n return;\n }\n // Only a structurally valid listen gets watchers, mirroring the stdio\n // gate. A malformed one cannot succeed downstream, so attaching handles\n // for it only gives the response-close release something to undo — and\n // everything that is not a listen takes no watchers either way.\n const requestedUris = listenSubscriptionUris(parsedBody);\n if (requestedUris === undefined) {\n await modernNodeHandler(req, res, parsedBody);\n return;\n }\n\n // Over-cap listens are rejected inside prepareListenWatchers, before the ack.\n const prepared = await prepareListenWatchers(\n requestedUris,\n sharedPathGuard,\n sharedRegistry,\n resourceUpdateSink,\n );\n if (!prepared.ok) {\n sendJsonRpcError(\n res,\n 400,\n ProtocolErrorCode.InvalidParams,\n prepared.message,\n jsonRpcRequestId(parsedBody),\n );\n return;\n }\n\n let released = false;\n const release = (): void => {\n if (released) return;\n released = true;\n for (const uri of prepared.acquiredUris) sharedRegistry.release(uri);\n };\n if (prepared.acquiredUris.length > 0) {\n // The attach loop awaits per-URI path validation, so the client can be\n // gone by the time it returns. `close` fires once: a listener attached\n // after it would never run and every lease this batch took would leak\n // until process shutdown.\n if (res.closed || res.destroyed || res.writableEnded) release();\n else res.once('close', release);\n }\n\n try {\n await modernNodeHandler(req, res, parsedBody);\n } catch (error) {\n release();\n throw error;\n }\n })().catch(next);\n });\n\n // Answer non-POST here rather than handing it to `modernNodeHandler`. The\n // SDK's own 405 is byte-identical to this one, but it omits the `Allow`\n // header RFC 9110 §15.5.6 requires and routes every routine GET probe through\n // the handler's `onerror` — which this server logs at error level.\n app.all('/mcp', (_req: Request, res: Response) => {\n sendJsonRpcError(res, 405, JSONRPC_SERVER_ERROR, 'Method not allowed.', null, {\n Allow: 'POST, OPTIONS',\n });\n });\n\n app.use(errorHandlerMiddleware);\n\n return app;\n}\n\nexport async function startHttpServer(\n port: number,\n options: ServerOptions,\n config: RuntimeConfig = {},\n): Promise<Server> {\n const httpHost = config.httpHost ?? '127.0.0.1';\n const { apiKey } = config;\n assertHttpBindingPolicy(httpHost, apiKey);\n const allowedHosts = resolveAllowedHosts(httpHost, process.env['FS_ALLOWED_HOSTS']);\n assertHttpHostPolicy(\n httpHost,\n allowedHosts,\n parseTrueEnvFlag(process.env['FS_ALLOW_UNRESTRICTED_HOSTS'], 'FS_ALLOW_UNRESTRICTED_HOSTS'),\n );\n\n const sharedRegistry = createWatcherRegistry();\n // One store for the whole endpoint, same lifetime and same one-credential\n // trust argument as the shared guard below: a result a tool externalized in\n // one POST must survive to the follow-up resources/read, and the modern leg\n // builds a fresh McpServer per request so a per-request store would discard\n // it immediately.\n // Fires only from a tool call, which is long after `modernHandler` below is\n // constructed, so reading it from the closure is safe.\n const sharedStore = new ResourceStore(() => {\n modernHandler.notify.resourcesChanged();\n });\n const sharedPageStore = new PageSnapshotStore();\n // One guard for the whole endpoint. The modern leg builds a fresh McpServer\n // per request, so a per-instance guard would discard every accepted access\n // grant the moment the request ended — re-prompting on each subsequent call\n // and leaving the listen-watcher path validating against a stale allowed set.\n // Grant scope is therefore the endpoint, not the connection: with FS_API_KEY set\n // every caller presents the same key (one auth context by construction), and\n // without it the bind is loopback-only. Split into per-auth-context guards if\n // this ever serves more than one credential.\n const sharedPathGuard = new PathGuard(options);\n await sharedPathGuard.recomputeAllowedDirectories();\n\n const maxRequestBodySize = getMaxInboundMessageBytes();\n\n const modernHandler: McpHttpHandler = createMcpHandler(\n async ({ era }) => {\n const c = await createServer(options, {\n watcherRegistry: sharedRegistry,\n notifier: modernHandler.notify,\n pathGuard: sharedPathGuard,\n resourceStore: sharedStore,\n pageStore: sharedPageStore,\n era,\n ...(apiKey !== undefined ? { apiKey } : {}),\n });\n return c.mcp;\n },\n {\n // The SDK's default: a 2025-era request is answered by a fresh instance\n // from the same factory, no session. What that leg cannot do — answer an\n // input_required confirmation, deliver a subscription — is refused with a\n // named workaround (define.ts, resources.ts). See docs/adr/003.\n legacy: 'stateless',\n maxRequestBodySize,\n onerror: (error: Error) => {\n Logger.error('[HTTP] modern leg error:', formatUnknownErrorMessage(error));\n },\n },\n );\n const modernNodeHandler = toNodeHandler(modernHandler, {\n maxRequestBodySize,\n // The adapter answers 500 when request conversion or handler.fetch\n // itself throws (e.g. a request racing handler.close()); the entry's\n // own onerror above never sees those. Log the adapter-level refusal so\n // the 500 is not silent.\n onerror: (error: Error) => {\n Logger.error('[HTTP] node adapter error:', formatUnknownErrorMessage(error));\n },\n });\n\n const app = setupExpressApp(\n httpHost,\n apiKey,\n allowedHosts,\n modernNodeHandler,\n sharedPathGuard,\n sharedRegistry,\n modernHandler.notify,\n );\n\n const httpServer = createHttpServer(app);\n httpServer.keepAliveTimeout = KEEPALIVE_TIMEOUT_MS;\n httpServer.headersTimeout = KEEPALIVE_TIMEOUT_MS + 5_000;\n httpServer.requestTimeout = DEFAULT_REQUEST_TIMEOUT_MSEC;\n\n const onHttpServerError = (error: Error): void => {\n Logger.error('[HTTP] runtime server error', {\n host: httpHost,\n port,\n error: formatUnknownErrorMessage(error),\n });\n };\n httpServer.on('error', onHttpServerError);\n\n const teardown = (): Promise<void> => {\n sharedRegistry.destroy();\n sharedPageStore.clear();\n return modernHandler.close().catch((err: unknown) => {\n Logger.error('[HTTP] Error closing handler:', formatUnknownErrorMessage(err));\n });\n };\n const originalClose = httpServer.close.bind(httpServer);\n httpServer.close = function (callback?: (error?: Error) => void) {\n void teardown().then(() => {\n originalClose(callback);\n });\n return httpServer;\n };\n\n return new Promise((resolve, reject) => {\n const onError = (err: Error) => {\n void teardown();\n reject(err);\n };\n httpServer.once('error', onError);\n\n httpServer.listen(port, httpHost, () => {\n httpServer.removeListener('error', onError);\n Logger.info(`[HTTP] Server listening on http://${httpHost}:${port}`);\n resolve(httpServer);\n });\n });\n}\n"]}
|
|
@@ -49,7 +49,9 @@ export type ListenPreparation = {
|
|
|
49
49
|
};
|
|
50
50
|
/**
|
|
51
51
|
* Prepare every filesystem watcher named by a `subscriptions/listen` filter.
|
|
52
|
-
* The batch is all-or-nothing: a failed URI releases each prior lease.
|
|
52
|
+
* The batch is all-or-nothing: a failed URI releases each prior lease. An
|
|
53
|
+
* over-cap listen is rejected up front, before the ack and before any watcher
|
|
54
|
+
* exists.
|
|
53
55
|
*/
|
|
54
56
|
export declare function prepareListenWatchers(uris: readonly string[], pathGuard: PathGuard, registry: WatcherRegistry, notify: (uri: string) => void): Promise<ListenPreparation>;
|
|
55
57
|
//# sourceMappingURL=shared.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/transport/shared.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,eAAe,EAAmB,MAAM,8BAA8B,CAAC;AAGhF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,6BAA6B,CAAC;AAErC;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,sFAAsF;IACtF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAG5E;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM,GAAG,IAAI,EAC5D,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,EAAE,EAAE,EAAE,GACL;IAAE,OAAO,EAAE,OAAO,eAAe,CAAC;IAAC,EAAE,EAAE,EAAE,CAAC;IAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAEvF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,EAAE,GAAG,SAAS,CAQ7E;AAED,MAAM,MAAM,iBAAiB,GACzB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAAE,GACtD;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAoBrD
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/transport/shared.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,eAAe,EAAmB,MAAM,8BAA8B,CAAC;AAGhF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,6BAA6B,CAAC;AAErC;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,sFAAsF;IACtF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAG5E;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM,GAAG,IAAI,EAC5D,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,EAAE,EAAE,EAAE,GACL;IAAE,OAAO,EAAE,OAAO,eAAe,CAAC;IAAC,EAAE,EAAE,EAAE,CAAC;IAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAEvF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,EAAE,GAAG,SAAS,CAQ7E;AAED,MAAM,MAAM,iBAAiB,GACzB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAAE,GACtD;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAoBrD;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,eAAe,EACzB,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAC5B,OAAO,CAAC,iBAAiB,CAAC,CAsB5B"}
|
package/dist/transport/shared.js
CHANGED
|
@@ -55,9 +55,22 @@ function watcherFailureMessage(uri, result) {
|
|
|
55
55
|
}
|
|
56
56
|
/**
|
|
57
57
|
* Prepare every filesystem watcher named by a `subscriptions/listen` filter.
|
|
58
|
-
* The batch is all-or-nothing: a failed URI releases each prior lease.
|
|
58
|
+
* The batch is all-or-nothing: a failed URI releases each prior lease. An
|
|
59
|
+
* over-cap listen is rejected up front, before the ack and before any watcher
|
|
60
|
+
* exists.
|
|
59
61
|
*/
|
|
60
62
|
export async function prepareListenWatchers(uris, pathGuard, registry, notify) {
|
|
63
|
+
// The per-URI `capped` failure below would also reject, but only after
|
|
64
|
+
// creating and tearing down watchers up to the cap. Only URIs without a live
|
|
65
|
+
// watcher consume a slot.
|
|
66
|
+
const newUris = uris.filter((uri) => !registry.hasWatcher(uri));
|
|
67
|
+
const available = MAX_WATCHERS - registry.size();
|
|
68
|
+
if (newUris.length > available) {
|
|
69
|
+
return {
|
|
70
|
+
ok: false,
|
|
71
|
+
message: `subscriptions/listen names ${newUris.length} not-yet-watched URIs but only ${available} watcher slots remain (cap ${MAX_WATCHERS}). Reduce the resourceSubscriptions list.`,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
61
74
|
const acquired = [];
|
|
62
75
|
for (const uri of uris) {
|
|
63
76
|
const result = await registry.acquire(pathGuard, uri, notify);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/transport/shared.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,6EAA6E;AAC7E,+EAA+E;AAC/E,gDAAgD;AAChD,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAEhF,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAE9D,OAAO,EACL,YAAY,GAGb,MAAM,6BAA6B,CAAC;AAerC,yEAAyE;AACzE,MAAM,UAAU,gBAAgB,CAAC,UAAmB;IAClD,MAAM,EAAE,GAAI,UAAkD,EAAE,EAAE,CAAC;IACnE,OAAO,OAAO,EAAE,KAAK,QAAQ,IAAI,OAAO,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACtE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAC1B,IAAY,EACZ,OAAe,EACf,EAAM;IAEN,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC;AACpE,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAAgB;IACrD,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACtE,IAAK,OAAgC,CAAC,MAAM,KAAK,sBAAsB;QAAE,OAAO,SAAS,CAAC;IAC1F,MAAM,MAAM,GAAG,eAAe,CAAC,0BAA0B,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACzF,IAAI,MAAM,YAAY,OAAO,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC/E,0EAA0E;IAC1E,wEAAwE;IACxE,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,qBAAqB,IAAI,EAAE,CAAC,CAAC,CAAC;AACrF,CAAC;AAMD,MAAM,uBAAuB,GAAG;IAC9B,SAAS,EAAE,0BAA0B;IACrC,MAAM,EAAE,iBAAiB,YAAY,UAAU;IAC/C,eAAe,EAAE,yCAAyC;IAC1D,KAAK,EAAE,yCAAyC;CACxC,CAAC;AAEX,SAAS,qBAAqB,CAC5B,GAAW,EACX,MAAkD;IAElD,MAAM,GAAG,GACP,MAAM,CAAC,MAAM,KAAK,cAAc;QAC9B,CAAC,CAAC,yBAAyB,CAAC,MAAM,CAAC,KAAK,CAAC;QACzC,CAAC,CAAC,uBAAuB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC7C,OAAO,uBAAuB,GAAG,KAAK,GAAG,EAAE,CAAC;AAC9C,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/transport/shared.ts"],"names":[],"mappings":"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","sourcesContent":["// Pieces both transport legs share: the runtime config contract, the JSON-RPC\n// error envelope, and the `subscriptions/listen` watcher-preparation ladder.\n// stdio and HTTP gate the same message shape; only where the lease is released\n// differs (connection close vs response close).\nimport { JSONRPC_VERSION, specTypeSchemas } from '@modelcontextprotocol/server';\n\nimport { formatUnknownErrorMessage } from '../core/errors.ts';\nimport type { PathGuard } from '../core/path.ts';\nimport {\n MAX_WATCHERS,\n type WatcherAttachResult,\n type WatcherRegistry,\n} from '../core/watcher-registry.ts';\n\n/**\n * Runtime inputs the CLI resolves once (flag, else the operator's env var) and\n * hands the transport. Separate from `ServerOptions` because that object is\n * `PathGuard`'s constructor argument: the filesystem guard has no use for a\n * bind address and no business holding a bearer secret.\n */\nexport interface RuntimeConfig {\n /** `--http-host` or `FS_HTTP_HOST`. The HTTP bind defaults to loopback without it. */\n httpHost?: string;\n /** `--api-key` or `FS_API_KEY`. Unset means open access (loopback dev mode). */\n apiKey?: string;\n}\n\n/** The request id of a parsed JSON-RPC body, for error-envelope echo. */\nexport function jsonRpcRequestId(parsedBody: unknown): string | number | null {\n const id = (parsedBody as { id?: unknown } | null | undefined)?.id;\n return typeof id === 'string' || typeof id === 'number' ? id : null;\n}\n\n/**\n * The one JSON-RPC error envelope both legs refuse in. HTTP wraps it in\n * `sendJsonRpcError` to add status and headers; stdio hands it straight to the\n * wire. A second hand-built literal is how the two drift apart.\n */\nexport function jsonRpcError<Id extends string | number | null>(\n code: number,\n message: string,\n id: Id,\n): { jsonrpc: typeof JSONRPC_VERSION; id: Id; error: { code: number; message: string } } {\n return { jsonrpc: JSONRPC_VERSION, id, error: { code, message } };\n}\n\n/**\n * The de-duplicated `resourceSubscriptions` URIs of a structurally valid\n * `subscriptions/listen` request — `[]` when it names none — or `undefined` for\n * anything else, including a listen the schema rejects. Both legs gate watcher\n * attachment on this: a malformed listen cannot succeed downstream, so creating\n * and tearing down `fs.watch` handles for it is pure waste — and on HTTP the\n * teardown depended on the response-close listener firing.\n *\n * The method check runs first because every inbound stdio message passes\n * through here; only a listen pays for the schema parse.\n */\nexport function listenSubscriptionUris(message: unknown): string[] | undefined {\n if (typeof message !== 'object' || message === null) return undefined;\n if ((message as { method?: unknown }).method !== 'subscriptions/listen') return undefined;\n const result = specTypeSchemas.SubscriptionsListenRequest['~standard'].validate(message);\n if (result instanceof Promise || result.issues !== undefined) return undefined;\n // De-duplicate: one attach must yield one ref-count, or the release below\n // decrements further than it incremented and tears down a live watcher.\n return [...new Set(result.value.params.notifications.resourceSubscriptions ?? [])];\n}\n\nexport type ListenPreparation =\n | { readonly ok: true; readonly acquiredUris: string[] }\n | { readonly ok: false; readonly message: string };\n\nconst WATCHER_FAILURE_REASONS = {\n 'bad-uri': 'unsupported resource URI',\n capped: `watcher limit ${MAX_WATCHERS} reached`,\n 'attach-failed': 'filesystem watcher could not be created',\n stale: 'subscription was cancelled during setup',\n} as const;\n\nfunction watcherFailureMessage(\n uri: string,\n result: Exclude<WatcherAttachResult, { ok: true }>,\n): string {\n const why =\n result.reason === 'invalid-path'\n ? formatUnknownErrorMessage(result.error)\n : WATCHER_FAILURE_REASONS[result.reason];\n return `Cannot subscribe to ${uri}: ${why}`;\n}\n\n/**\n * Prepare every filesystem watcher named by a `subscriptions/listen` filter.\n * The batch is all-or-nothing: a failed URI releases each prior lease. An\n * over-cap listen is rejected up front, before the ack and before any watcher\n * exists.\n */\nexport async function prepareListenWatchers(\n uris: readonly string[],\n pathGuard: PathGuard,\n registry: WatcherRegistry,\n notify: (uri: string) => void,\n): Promise<ListenPreparation> {\n // The per-URI `capped` failure below would also reject, but only after\n // creating and tearing down watchers up to the cap. Only URIs without a live\n // watcher consume a slot.\n const newUris = uris.filter((uri) => !registry.hasWatcher(uri));\n const available = MAX_WATCHERS - registry.size();\n if (newUris.length > available) {\n return {\n ok: false,\n message: `subscriptions/listen names ${newUris.length} not-yet-watched URIs but only ${available} watcher slots remain (cap ${MAX_WATCHERS}). Reduce the resourceSubscriptions list.`,\n };\n }\n const acquired: string[] = [];\n for (const uri of uris) {\n const result = await registry.acquire(pathGuard, uri, notify);\n if (!result.ok) {\n for (const prior of acquired) registry.release(prior);\n return { ok: false, message: watcherFailureMessage(uri, result) };\n }\n acquired.push(uri);\n }\n return { ok: true, acquiredUris: acquired };\n}\n"]}
|
package/package.json
CHANGED