@termwright/mcp 0.2.0 → 0.3.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.
Files changed (55) hide show
  1. package/README.md +109 -28
  2. package/dist/bin.js +2 -1
  3. package/dist/bin.js.map +1 -1
  4. package/dist/chunk-R2N52YYH.js +629 -0
  5. package/dist/chunk-R2N52YYH.js.map +1 -0
  6. package/dist/{chunk-IPNUAUAN.js → chunk-ROMJP5D3.js} +595 -482
  7. package/dist/chunk-ROMJP5D3.js.map +1 -0
  8. package/dist/docs-85fGFORb.d.ts +13 -0
  9. package/dist/docs.d.ts +1 -0
  10. package/dist/docs.js +7 -0
  11. package/dist/docs.js.map +1 -0
  12. package/dist/index.d.ts +50 -28
  13. package/dist/index.js +9 -7
  14. package/package.json +11 -8
  15. package/dist/chunk-2J5WHI6X.js +0 -2000
  16. package/dist/chunk-2J5WHI6X.js.map +0 -1
  17. package/dist/chunk-36C7A7DW.js +0 -2685
  18. package/dist/chunk-36C7A7DW.js.map +0 -1
  19. package/dist/chunk-3PLOAM2C.js +0 -2427
  20. package/dist/chunk-3PLOAM2C.js.map +0 -1
  21. package/dist/chunk-57GYK2EF.js +0 -2991
  22. package/dist/chunk-57GYK2EF.js.map +0 -1
  23. package/dist/chunk-ABLJBL5P.js +0 -2687
  24. package/dist/chunk-ABLJBL5P.js.map +0 -1
  25. package/dist/chunk-BOOUADRN.js +0 -1938
  26. package/dist/chunk-BOOUADRN.js.map +0 -1
  27. package/dist/chunk-BPWIETN5.js +0 -2983
  28. package/dist/chunk-BPWIETN5.js.map +0 -1
  29. package/dist/chunk-CMQB5G7R.js +0 -2968
  30. package/dist/chunk-CMQB5G7R.js.map +0 -1
  31. package/dist/chunk-I4B53KZ7.js +0 -2955
  32. package/dist/chunk-I4B53KZ7.js.map +0 -1
  33. package/dist/chunk-IPNUAUAN.js.map +0 -1
  34. package/dist/chunk-KZWL2S6E.js +0 -2869
  35. package/dist/chunk-KZWL2S6E.js.map +0 -1
  36. package/dist/chunk-LB2QBYW4.js +0 -2686
  37. package/dist/chunk-LB2QBYW4.js.map +0 -1
  38. package/dist/chunk-MR3AXSXL.js +0 -1977
  39. package/dist/chunk-MR3AXSXL.js.map +0 -1
  40. package/dist/chunk-NVSZXEZU.js +0 -2688
  41. package/dist/chunk-NVSZXEZU.js.map +0 -1
  42. package/dist/chunk-PD2WKAFE.js +0 -2531
  43. package/dist/chunk-PD2WKAFE.js.map +0 -1
  44. package/dist/chunk-PGY4ZDLD.js +0 -1843
  45. package/dist/chunk-PGY4ZDLD.js.map +0 -1
  46. package/dist/chunk-QDIAASH7.js +0 -2982
  47. package/dist/chunk-QDIAASH7.js.map +0 -1
  48. package/dist/chunk-UZWFLJGG.js +0 -2873
  49. package/dist/chunk-UZWFLJGG.js.map +0 -1
  50. package/dist/chunk-VFYTROYG.js +0 -2825
  51. package/dist/chunk-VFYTROYG.js.map +0 -1
  52. package/dist/chunk-ZTHKAJKT.js +0 -2981
  53. package/dist/chunk-ZTHKAJKT.js.map +0 -1
  54. package/dist/chunk-ZZULGRRE.js +0 -2991
  55. package/dist/chunk-ZZULGRRE.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server.ts","../src/http-security.ts","../src/sdk-facade.ts","../src/cli.ts"],"sourcesContent":["/**\n * Server wiring: tools onto an `McpServer`, and the two transports.\n *\n * Both transports share one code path — `createTermwrightMcpServer(store)` — so\n * a tool behaves identically over stdio and over Streamable HTTP. What differs\n * is only who owns the session key: stdio has exactly one implicit session,\n * HTTP keys sessions by `Mcp-Session-Id` in our {@link SessionRegistry}.\n */\nimport { createServer } from 'node:http';\nimport type { IncomingMessage, Server, ServerResponse } from 'node:http';\nimport { randomBytes, randomUUID } from 'node:crypto';\nimport { CrashContextError, describeCrash } from './crash.js';\nimport { renderErrorPayload, toErrorPayload, usageError } from './errors.js';\nimport {\n BoundedRateLimiter,\n admitHttpRequest,\n isLoopbackHost,\n normalizeAllowedOrigins,\n} from './http-security.js';\nimport type { HttpRateLimitOptions } from './http-security.js';\nimport {\n InMemoryTransport,\n connectTransport,\n McpServer,\n StdioServerTransport,\n StreamableHTTPServerTransport,\n isInitializeRequest,\n} from './sdk-facade.js';\nimport type { CallToolResult, Transport } from './sdk-facade.js';\nimport { SessionRegistry, closeSessionStores, createSessionStores } from './sessions.js';\nimport type { SessionStores } from './sessions.js';\nimport { TOOLS } from './registry.js';\nimport type { ToolContext, ToolOutcome } from './tool-kit.js';\nimport { SERVER_NAME, SERVER_VERSION } from './version.js';\n\n/** Server-level instructions shown to hosts that surface them. */\nconst INSTRUCTIONS =\n 'Drive terminal programs the way a person would. terminal.launch starts a program and returns a ' +\n 'handle; terminal.snapshot gives compact refs plus visible text; act with terminal.click / press / ' +\n 'type; wait with terminal.wait_for; poll cheaply with terminal.capture_since using the revision a ' +\n 'snapshot returned. Refs like semantic:n8@42 are only valid at semantic revision 42 — re-snapshot after the ' +\n 'screen changes. Programs without a termwright adapter report semanticTree: unavailable; target ' +\n 'them by text instead of by role.';\n\n/** Builds the `CallToolResult` for a successful handler outcome. */\nfunction successResult(outcome: ToolOutcome<Record<string, unknown>>): CallToolResult {\n return {\n content: [\n { type: 'text', text: outcome.text },\n ...(outcome.images ?? []).map((image) => ({\n type: 'image' as const,\n data: image.data,\n mimeType: image.mimeType,\n })),\n ],\n structuredContent: outcome.data,\n };\n}\n\n/**\n * Attaches the crash report when the failure happened against a terminal whose\n * program died on its own.\n *\n * This lives here, once, rather than in every acting tool: the server is the\n * only place that sees both the raw arguments (which name the terminal) and\n * every thrown error. Without it a locator that never resolved because the\n * child was gone reports a bare `timeout`, which tells an agent to wait longer\n * — precisely the wrong move.\n */\nfunction withCrashContext(context: ToolContext, args: unknown, error: unknown): unknown {\n if (error instanceof CrashContextError) return error;\n const id = (args as { terminal?: unknown } | null)?.terminal;\n if (typeof id !== 'string') return error;\n const report = context.terminals.find(id)?.harness.crashReport();\n return report === undefined || report === null\n ? error\n : new CrashContextError(error, describeCrash(report));\n}\n\n/** `_meta` key carrying the structured error payload of a failed tool call. */\nexport const ERROR_META_KEY = 'io.termwright/error';\n\n/**\n * Builds the `CallToolResult` for a failure.\n *\n * The payload travels in `_meta`, not in `structuredContent`: a client that has\n * seen the tool list validates `structuredContent` against the success\n * `outputSchema`, and an error object would be rejected before the agent ever\n * saw it. The text content carries the same information in readable form —\n * typed kind, message, the driver's suggestion and its bounded candidates.\n */\nfunction errorResult(error: unknown): CallToolResult {\n const payload = toErrorPayload(error);\n return {\n isError: true,\n content: [{ type: 'text', text: renderErrorPayload(payload) }],\n _meta: { [ERROR_META_KEY]: payload },\n };\n}\n\n/** Registers every tool from {@link TOOLS} on a fresh `McpServer`. */\nexport function createTermwrightMcpServer(stores: SessionStores): McpServer {\n const server = new McpServer(\n { name: SERVER_NAME, version: SERVER_VERSION },\n { capabilities: { tools: {} }, instructions: INSTRUCTIONS },\n );\n const context: ToolContext = { terminals: stores.terminals, traces: stores.traces };\n\n for (const tool of TOOLS) {\n server.registerTool(\n tool.name,\n {\n title: tool.title,\n description: tool.description,\n inputSchema: tool.inputSchema,\n outputSchema: tool.outputSchema,\n annotations: tool.annotations,\n },\n async (args: unknown): Promise<CallToolResult> => {\n try {\n return successResult(await tool.handler(context, args as never));\n } catch (error) {\n return errorResult(withCrashContext(context, args, error));\n }\n },\n );\n }\n return server;\n}\n\n/** A running server plus the handle needed to shut it down. */\nexport interface RunningServer {\n readonly server: McpServer;\n readonly stores: SessionStores;\n close(): Promise<void>;\n}\n\n/** Options shared by the transports. */\nexport interface ServeOptions {\n /** Root for `variant: \"full\"` snapshot dumps. */\n readonly storageDir?: string;\n readonly maxSessions?: number;\n}\n\n/** Connects a server to a transport and returns its lifecycle handle. */\nasync function connect(stores: SessionStores, transport: Transport): Promise<RunningServer> {\n const server = createTermwrightMcpServer(stores);\n try {\n await connectTransport(server, transport);\n } catch (error) {\n const cleanup = await Promise.allSettled([closeSessionStores(stores), server.close()]);\n const failures = cleanup.flatMap((result) =>\n result.status === 'rejected' ? [result.reason] : [],\n );\n if (failures.length > 0)\n throw new AggregateError([error, ...failures], 'MCP transport startup and rollback failed');\n throw error;\n }\n return {\n server,\n stores,\n close: async (): Promise<void> => {\n const results = await Promise.allSettled([closeSessionStores(stores), server.close()]);\n const failures = results.flatMap((result) =>\n result.status === 'rejected' ? [result.reason] : [],\n );\n if (failures.length > 0)\n throw new AggregateError(failures, 'MCP transport failed to close cleanly');\n },\n };\n}\n\n/** Serves the tools over stdio — the transport an MCP host spawns. */\nexport async function serveStdio(options: ServeOptions = {}): Promise<RunningServer> {\n const stores = createSessionStores({ sessionKey: 'stdio', storageDir: options.storageDir });\n return connect(stores, new StdioServerTransport());\n}\n\n/**\n * An in-process client/server pair over `InMemoryTransport`. Used by this\n * package's tests, and by anything embedding the tools without a socket.\n */\nexport async function serveInMemory(\n options: ServeOptions & { readonly sessionKey?: string } = {},\n): Promise<RunningServer & { readonly clientTransport: Transport }> {\n const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();\n const stores = createSessionStores({\n sessionKey: options.sessionKey ?? 'in-memory',\n storageDir: options.storageDir,\n });\n const running = await connect(stores, serverTransport);\n return { ...running, clientTransport };\n}\n\n/** A listening Streamable HTTP server. */\nexport interface HttpServerHandle {\n readonly http: Server;\n readonly registry: SessionRegistry<{\n transport: StreamableHTTPServerTransport;\n server: McpServer;\n }>;\n readonly port: number;\n /** Per-launch bearer required by every HTTP request. Never put it in a URL. */\n readonly authToken: string;\n close(): Promise<void>;\n}\n\n/** Default idle ceiling for an HTTP session. */\nexport const DEFAULT_IDLE_TTL_MS = 10 * 60_000;\n\n/** Options for {@link serveHttp}. */\nexport interface HttpServeOptions extends ServeOptions {\n readonly port?: number;\n readonly host?: string;\n /**\n * Explicit acknowledgement that a non-loopback bind exposes process launch,\n * terminal input and filesystem-backed trace tools to the network.\n */\n readonly allowNonLoopback?: boolean;\n /**\n * Browser origins allowed to call the endpoint. Non-browser MCP clients omit\n * `Origin`; any presented origin is rejected unless it appears here exactly.\n */\n readonly allowedOrigins?: readonly string[];\n /** Per-peer request ceiling. The limiter's identity map is bounded too. */\n readonly rateLimit?: HttpRateLimitOptions;\n /** Path the MCP endpoint listens on. Defaults to `/mcp`. */\n readonly path?: string;\n /**\n * Milliseconds a session may sit idle before it is torn down. Defaults to\n * {@link DEFAULT_IDLE_TTL_MS}; `0` disables expiry.\n */\n readonly idleTtlMs?: number;\n /** Injectable clock, for tests. */\n readonly now?: () => number;\n /** Where an expiry is reported. Defaults to stderr. */\n readonly log?: (message: string) => void;\n}\n\nfunction sendJson(response: ServerResponse, status: number, body: unknown): void {\n const text = JSON.stringify(body);\n response.writeHead(status, { 'content-type': 'application/json' });\n response.end(text);\n}\n\nasync function readBody(request: IncomingMessage): Promise<unknown> {\n const chunks: Buffer[] = [];\n let size = 0;\n for await (const chunk of request) {\n const buffer = Buffer.from(chunk as Buffer);\n size += buffer.byteLength;\n if (size > 4 * 1024 * 1024) throw new Error('request body too large');\n chunks.push(buffer);\n }\n if (chunks.length === 0) return undefined;\n return JSON.parse(Buffer.concat(chunks).toString('utf8'));\n}\n\n/**\n * Serves the tools over Streamable HTTP with multi-session support.\n *\n * Sessions live in {@link SessionRegistry}: `initialize` mints a session id and\n * registers a store together with its transport, every later request is routed\n * by its `Mcp-Session-Id` header, and `DELETE` (or transport close) disposes the\n * session's terminals. The ceiling is enforced here, before a transport exists.\n */\nexport async function serveHttp(options: HttpServeOptions = {}): Promise<HttpServerHandle> {\n const path = options.path ?? '/mcp';\n const host = options.host ?? '127.0.0.1';\n if (!isLoopbackHost(host) && options.allowNonLoopback !== true) {\n throw usageError(\n `refusing non-loopback MCP HTTP bind ${JSON.stringify(host)} without allowNonLoopback`,\n 'keep the default loopback bind, or explicitly acknowledge the remote trust boundary',\n );\n }\n const authToken = randomBytes(32).toString('base64url');\n const allowedOrigins = normalizeAllowedOrigins(options.allowedOrigins);\n const authenticatedRateLimiter = new BoundedRateLimiter(options.rateLimit);\n const preflightRateLimiter = new BoundedRateLimiter(options.rateLimit);\n const now = options.now ?? Date.now;\n // stdout may be a protocol stream elsewhere; server-level notes go to stderr.\n const log = options.log ?? ((message: string): void => void process.stderr.write(`${message}\\n`));\n const registry = new SessionRegistry<{\n transport: StreamableHTTPServerTransport;\n server: McpServer;\n }>({\n ...(options.maxSessions === undefined ? {} : { maxSessions: options.maxSessions }),\n ...(options.storageDir === undefined ? {} : { storageDir: options.storageDir }),\n ...(options.now === undefined ? {} : { now: options.now }),\n idleTtlMs: options.idleTtlMs ?? DEFAULT_IDLE_TTL_MS,\n disposeAttachment: async (attachment) => {\n await attachment.transport.close();\n },\n onExpired: (key) => {\n log(`termwright: session ${key} expired after idling; terminals and traces released`);\n },\n onBackgroundError: (error) => {\n log(\n `termwright: idle session cleanup failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n },\n });\n\n const http = createServer((request, response) => {\n void (async () => {\n try {\n // Security admission is intentionally before URL routing, body reads,\n // session lookup/touch and initialize. A rejected peer cannot allocate\n // an MCP session, refresh one it guessed, or make us buffer its body.\n if (\n !admitHttpRequest(request, response, {\n token: authToken,\n allowedOrigins,\n authenticatedRateLimiter,\n preflightRateLimiter,\n now,\n })\n )\n return;\n const url = new URL(request.url ?? '/', 'http://localhost');\n if (url.pathname !== path) {\n sendJson(response, 404, { error: 'not found' });\n return;\n }\n const sessionId = request.headers['mcp-session-id'];\n const key = Array.isArray(sessionId) ? sessionId[0] : sessionId;\n\n if (request.method === 'DELETE') {\n if (key !== undefined) await registry.delete(key);\n response.writeHead(204).end();\n return;\n }\n\n const body = request.method === 'POST' ? await readBody(request) : undefined;\n\n if (key !== undefined) {\n const session = registry.get(key);\n if (session === undefined) {\n sendJson(response, 404, { error: 'unknown session', kind: 'no-session' });\n return;\n }\n // Every request that names a session is proof the client is alive.\n registry.touch(key);\n await session.attachment.transport.handleRequest(request, response, body);\n return;\n }\n\n if (request.method !== 'POST' || !isInitializeRequest(body)) {\n sendJson(response, 400, { error: 'missing Mcp-Session-Id', kind: 'usage' });\n return;\n }\n\n const newKey = randomUUID();\n const session = registry.create(newKey, (stores) => {\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: () => newKey,\n });\n const server = createTermwrightMcpServer(stores);\n transport.onclose = (): void => {\n void registry.delete(newKey).catch((error) => {\n log(\n `termwright: session ${newKey} transport cleanup failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n });\n };\n return { transport, server };\n });\n await connectTransport(session.attachment.server, session.attachment.transport);\n await session.attachment.transport.handleRequest(request, response, body);\n } catch (error) {\n const payload = toErrorPayload(error);\n if (!response.headersSent)\n sendJson(response, 500, { error: payload.message, kind: payload.kind });\n else response.end();\n }\n })();\n });\n\n try {\n await new Promise<void>((resolve, reject) => {\n const onError = (error: Error): void => {\n http.off('listening', onListening);\n reject(error);\n };\n const onListening = (): void => {\n http.off('error', onError);\n resolve();\n };\n http.once('error', onError);\n http.once('listening', onListening);\n http.listen(options.port ?? 0, host);\n });\n } catch (error) {\n await registry.closeAll().catch((cleanup) => {\n throw new AggregateError([error, cleanup], 'MCP HTTP bind and rollback both failed');\n });\n throw error;\n }\n registry.startIdleSweeper();\n const address = http.address();\n const port = typeof address === 'object' && address !== null ? address.port : (options.port ?? 0);\n\n return {\n http,\n registry,\n port,\n authToken,\n close: async (): Promise<void> => {\n registry.stopIdleSweeper();\n const results = await Promise.allSettled([\n registry.closeAll(),\n new Promise<void>((resolve, reject) => {\n http.close((error) => (error === undefined ? resolve() : reject(error)));\n }),\n ]);\n const failures = results.flatMap((result) =>\n result.status === 'rejected' ? [result.reason] : [],\n );\n if (failures.length > 0)\n throw new AggregateError(failures, 'MCP HTTP server failed to close cleanly');\n },\n };\n}\n","/**\n * Security policy for the Streamable HTTP transport.\n *\n * This module deliberately knows nothing about MCP messages or sessions. It is\n * the admission boundary in front of both: rate limit the peer, apply the\n * browser Origin policy, then authenticate the bearer. Only an admitted\n * request may reach routing, body parsing or the session registry.\n */\nimport { timingSafeEqual } from 'node:crypto';\nimport { isIP } from 'node:net';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\n\n/** Defaults are conservative for an interactive protocol and cheap to raise explicitly. */\nexport const DEFAULT_HTTP_RATE_LIMIT = Object.freeze({\n windowMs: 60_000,\n maxRequests: 120,\n maxClients: 1_024,\n});\n\n/** Bounded fixed-window admission policy, keyed by the TCP peer address. */\nexport interface HttpRateLimitOptions {\n readonly windowMs?: number;\n readonly maxRequests?: number;\n readonly maxClients?: number;\n}\n\ninterface RateBucket {\n readonly startedAt: number;\n requests: number;\n}\n\nexport interface RateLimitDecision {\n readonly allowed: boolean;\n readonly retryAfterSeconds: number;\n}\n\n/**\n * A rate limiter whose own memory is bounded. When all client slots are active,\n * a new identity is refused instead of evicting an active bucket and thereby\n * letting an address-rotation attack reset counters.\n */\nexport class BoundedRateLimiter {\n readonly #windowMs: number;\n readonly #maxRequests: number;\n readonly #maxClients: number;\n readonly #buckets = new Map<string, RateBucket>();\n\n constructor(options: HttpRateLimitOptions = {}) {\n this.#windowMs = positiveInteger(\n options.windowMs ?? DEFAULT_HTTP_RATE_LIMIT.windowMs,\n 'rateLimit.windowMs',\n );\n this.#maxRequests = positiveInteger(\n options.maxRequests ?? DEFAULT_HTTP_RATE_LIMIT.maxRequests,\n 'rateLimit.maxRequests',\n );\n this.#maxClients = positiveInteger(\n options.maxClients ?? DEFAULT_HTTP_RATE_LIMIT.maxClients,\n 'rateLimit.maxClients',\n );\n }\n\n /** Visible for deterministic tests and operational diagnostics. */\n get size(): number {\n return this.#buckets.size;\n }\n\n admit(identity: string, now: number): RateLimitDecision {\n if (!Number.isFinite(now)) throw new TypeError('rate-limit clock must return a finite number');\n let bucket = this.#buckets.get(identity);\n if (bucket !== undefined && now - bucket.startedAt >= this.#windowMs) {\n this.#buckets.delete(identity);\n bucket = undefined;\n }\n\n if (bucket === undefined) {\n if (this.#buckets.size >= this.#maxClients) this.#purgeExpired(now);\n if (this.#buckets.size >= this.#maxClients) {\n return { allowed: false, retryAfterSeconds: this.#retryForOldest(now) };\n }\n this.#buckets.set(identity, { startedAt: now, requests: 1 });\n return { allowed: true, retryAfterSeconds: 0 };\n }\n\n if (bucket.requests >= this.#maxRequests) {\n return {\n allowed: false,\n retryAfterSeconds: secondsUntil(bucket.startedAt + this.#windowMs, now),\n };\n }\n bucket.requests += 1;\n return { allowed: true, retryAfterSeconds: 0 };\n }\n\n #purgeExpired(now: number): void {\n for (const [identity, bucket] of this.#buckets) {\n if (now - bucket.startedAt >= this.#windowMs) this.#buckets.delete(identity);\n }\n }\n\n #retryForOldest(now: number): number {\n let oldestExpiry = Number.POSITIVE_INFINITY;\n for (const bucket of this.#buckets.values()) {\n oldestExpiry = Math.min(oldestExpiry, bucket.startedAt + this.#windowMs);\n }\n return Number.isFinite(oldestExpiry) ? secondsUntil(oldestExpiry, now) : 1;\n }\n}\n\n/** Exact origins accepted from browser-like clients. Missing Origin is allowed. */\nexport function normalizeAllowedOrigins(origins: readonly string[] = []): ReadonlySet<string> {\n const normalized = new Set<string>();\n for (const candidate of origins) {\n let url: URL;\n try {\n url = new URL(candidate);\n } catch {\n throw new TypeError(`allowed origin is not a URL: ${JSON.stringify(candidate)}`);\n }\n if (\n (url.protocol !== 'http:' && url.protocol !== 'https:') ||\n url.username !== '' ||\n url.password !== '' ||\n url.pathname !== '/' ||\n url.search !== '' ||\n url.hash !== ''\n ) {\n throw new TypeError(\n `allowed origin must be an HTTP(S) origin without path, query or credentials: ${JSON.stringify(candidate)}`,\n );\n }\n normalized.add(url.origin);\n }\n return normalized;\n}\n\n/** True only for an explicit loopback address or the conventional localhost name. */\nexport function isLoopbackHost(host: string): boolean {\n const lower = host.toLowerCase().replace(/^\\[|\\]$/gu, '');\n if (lower === 'localhost' || lower === '::1') return true;\n if (isIP(lower) === 4) return lower.split('.')[0] === '127';\n return /^::ffff:127(?:\\.\\d{1,3}){3}$/u.test(lower);\n}\n\nexport interface HttpAdmissionOptions {\n readonly token: string;\n readonly allowedOrigins: ReadonlySet<string>;\n readonly authenticatedRateLimiter: BoundedRateLimiter;\n readonly preflightRateLimiter: BoundedRateLimiter;\n readonly now: () => number;\n}\n\n/**\n * Applies the complete admission policy and writes the rejection response.\n * Returns only after headers have been decided; it never consumes request data.\n */\nexport function admitHttpRequest(\n request: IncomingMessage,\n response: ServerResponse,\n options: HttpAdmissionOptions,\n): boolean {\n const identity = request.socket.remoteAddress ?? '<unknown-peer>';\n const origin = request.headers.origin;\n if (origin !== undefined && (Array.isArray(origin) || !options.allowedOrigins.has(origin))) {\n sendSecurityError(response, 403, 'origin is not allowed');\n return false;\n }\n if (typeof origin === 'string') {\n response.setHeader('access-control-allow-origin', origin);\n response.setHeader('access-control-expose-headers', 'Mcp-Session-Id');\n response.setHeader('vary', 'Origin');\n if (request.method === 'OPTIONS') {\n if (!admitRate(identity, response, options.preflightRateLimiter, options.now)) return false;\n return admitPreflight(request, response);\n }\n }\n\n if (!bearerMatches(request.headers.authorization, options.token)) {\n sendSecurityError(response, 401, 'missing or invalid bearer token', {\n 'www-authenticate': 'Bearer realm=\"termwright-mcp\"',\n });\n return false;\n }\n if (!admitRate(identity, response, options.authenticatedRateLimiter, options.now)) return false;\n return true;\n}\n\nfunction admitRate(\n identity: string,\n response: ServerResponse,\n limiter: BoundedRateLimiter,\n now: () => number,\n): boolean {\n const rate = limiter.admit(identity, now());\n if (rate.allowed) return true;\n sendSecurityError(response, 429, 'rate limit exceeded', {\n 'retry-after': String(rate.retryAfterSeconds),\n });\n return false;\n}\n\nconst CORS_METHODS = new Set(['GET', 'POST', 'DELETE']);\nconst CORS_HEADERS = new Set([\n 'accept',\n 'authorization',\n 'content-type',\n 'last-event-id',\n 'mcp-protocol-version',\n 'mcp-session-id',\n]);\n\n/** A preflight authorizes no MCP operation, but remains origin- and rate-limited. */\nfunction admitPreflight(request: IncomingMessage, response: ServerResponse): false {\n const method = request.headers['access-control-request-method'];\n const requestedHeaders = (request.headers['access-control-request-headers'] ?? '')\n .split(',')\n .map((header) => header.trim().toLowerCase())\n .filter((header) => header !== '');\n if (\n typeof method !== 'string' ||\n !CORS_METHODS.has(method.toUpperCase()) ||\n requestedHeaders.some((header) => !CORS_HEADERS.has(header))\n ) {\n sendSecurityError(response, 403, 'CORS preflight is not allowed');\n return false;\n }\n response.writeHead(204, {\n 'access-control-allow-methods': [...CORS_METHODS].join(', '),\n 'access-control-allow-headers': [...CORS_HEADERS].join(', '),\n 'access-control-max-age': '600',\n });\n response.end();\n return false;\n}\n\nfunction bearerMatches(header: string | undefined, expected: string): boolean {\n if (header === undefined || !header.startsWith('Bearer ')) return false;\n const provided = header.slice('Bearer '.length);\n const left = Buffer.from(provided, 'utf8');\n const right = Buffer.from(expected, 'utf8');\n return left.length === right.length && timingSafeEqual(left, right);\n}\n\nfunction sendSecurityError(\n response: ServerResponse,\n status: number,\n error: string,\n headers: Readonly<Record<string, string>> = {},\n): void {\n const text = JSON.stringify({ error });\n // Rejections never reuse the connection: an unauthenticated peer that\n // declared a large body cannot keep streaming it after admission failed.\n response.writeHead(status, {\n 'cache-control': 'no-store',\n connection: 'close',\n 'content-type': 'application/json',\n ...headers,\n });\n response.end(text);\n}\n\nfunction positiveInteger(value: number, name: string): number {\n if (!Number.isSafeInteger(value) || value <= 0)\n throw new TypeError(`${name} must be a positive safe integer`);\n return value;\n}\n\nfunction secondsUntil(deadline: number, now: number): number {\n return Math.max(1, Math.ceil((deadline - now) / 1_000));\n}\n","/**\n * The single module in this package that is allowed to import\n * `@modelcontextprotocol/sdk`.\n *\n * Everything else — tools, server wiring, CLI, tests — imports from here. The\n * SDK v2 package split is therefore a change to this one file: re-point the\n * specifiers, keep the exported names, and no tool handler moves.\n *\n * Nothing SDK-shaped leaks into the package's own public surface\n * (`src/index.ts`) beyond what a host needs to connect a transport.\n */\nimport type { Client as ClientType } from '@modelcontextprotocol/sdk/client/index.js';\nimport type { McpServer as McpServerType } from '@modelcontextprotocol/sdk/server/mcp.js';\n\nexport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nexport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nexport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nexport { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js';\nexport { Client } from '@modelcontextprotocol/sdk/client/index.js';\nexport { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';\nexport { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';\n\nexport type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\n\n/**\n * Connects a server to a transport.\n *\n * The SDK is compiled without `exactOptionalPropertyTypes`, so its transport\n * classes declare `onclose?: (() => void) | undefined` while the `Transport`\n * interface declares `onclose?: () => void`. The two are the same thing at\n * runtime; the single cast that reconciles them lives here, in the SDK\n * boundary, rather than in every call site.\n */\nexport async function connectTransport(server: McpServerType, transport: unknown): Promise<void> {\n await server.connect(transport as Parameters<McpServerType['connect']>[0]);\n}\n\n/** The client-side counterpart of {@link connectTransport}, with the same rationale. */\nexport async function connectClient(client: ClientType, transport: unknown): Promise<void> {\n await client.connect(transport as Parameters<ClientType['connect']>[0]);\n}\n\nexport type { CallToolResult, ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';\n","/**\n * `termwright-mcp` — the standalone binary for this package.\n *\n * Commands: serve over stdio (the default), serve Streamable HTTP, print\n * `agent-context`, print `usage`. Exit codes follow the taxonomy in\n * CONTRACTS.md §MCP: 0 ok / 1 assertion / 2 usage / 3 no-session / 4 ipc /\n * 5 internal. With `--json`, failures are one JSON object carrying `kind`.\n *\n * The umbrella `termwright` CLI (task #10) reuses {@link runCli} and the\n * generators it calls rather than spawning this binary.\n */\nimport { buildAgentContext, buildUsage } from './agent-context.js';\nimport { buildAgentSkill, writeAgentSkill } from './agent-skill.js';\nimport { EXIT_CODES, exitCodeFor, toErrorPayload, usageError } from './errors.js';\nimport { serveHttp, serveStdio } from './server.js';\nimport { SERVER_NAME, SERVER_VERSION } from './version.js';\n\n/** Where the CLI writes. Injectable so tests never touch the real streams. */\nexport interface CliIo {\n readonly out: (text: string) => void;\n readonly err: (text: string) => void;\n}\n\nconst defaultIo: CliIo = {\n out: (text) => process.stdout.write(`${text}\\n`),\n err: (text) => process.stderr.write(`${text}\\n`),\n};\n\nexport interface ParsedArgs {\n readonly command: 'serve' | 'agent-context' | 'usage' | 'skill' | 'help' | 'version';\n readonly json: boolean;\n readonly http: boolean;\n readonly port: number | undefined;\n readonly host: string | undefined;\n readonly allowNonLoopback: boolean;\n readonly showAuthToken: boolean;\n /** Destination directory for `skill`; without it the package goes to stdout. */\n readonly out: string | undefined;\n}\n\nexport function parseArgs(argv: readonly string[]): ParsedArgs {\n let command: ParsedArgs['command'] = 'serve';\n let json = false;\n let http = false;\n let port: number | undefined;\n let host: string | undefined;\n let allowNonLoopback = false;\n let showAuthToken = false;\n let out: string | undefined;\n\n for (let index = 0; index < argv.length; index += 1) {\n const arg = argv[index] ?? '';\n switch (arg) {\n case '--json':\n json = true;\n break;\n case '--http':\n http = true;\n break;\n case '--port': {\n const value = Number(argv[index + 1]);\n if (!Number.isInteger(value) || value < 0 || value > 65_535) {\n throw usageError('--port needs an integer between 0 and 65535');\n }\n port = value;\n index += 1;\n break;\n }\n case '--host':\n host = argv[index + 1];\n if (host === undefined) throw usageError('--host needs a value');\n index += 1;\n break;\n case '--allow-non-loopback':\n allowNonLoopback = true;\n break;\n case '--show-auth-token':\n showAuthToken = true;\n break;\n case '--out':\n out = argv[index + 1];\n if (out === undefined) throw usageError('--out needs a directory');\n index += 1;\n break;\n case '--help':\n case '-h':\n command = 'help';\n break;\n case '--version':\n case '-v':\n command = 'version';\n break;\n case 'serve':\n case 'stdio':\n command = 'serve';\n break;\n case 'agent-context':\n command = 'agent-context';\n break;\n case 'usage':\n command = 'usage';\n break;\n case 'skill':\n command = 'skill';\n break;\n default:\n throw usageError(\n `unknown argument ${JSON.stringify(arg)}`,\n 'run `termwright-mcp usage` for the one-screen cheat sheet',\n );\n }\n }\n return { command, json, http, port, host, allowNonLoopback, showAuthToken, out };\n}\n\n/** Startup lines are pure so the secret-disclosure policy is regression tested without opening a server. */\nexport function httpStartupMessages(\n args: Pick<ParsedArgs, 'host' | 'showAuthToken'>,\n handle: { readonly port: number; readonly authToken: string },\n): readonly string[] {\n return [\n `${SERVER_NAME} MCP listening on http://${args.host ?? '127.0.0.1'}:${handle.port}/mcp`,\n args.showAuthToken\n ? `${SERVER_NAME} MCP bearer token: ${handle.authToken}`\n : `${SERVER_NAME} MCP bearer token hidden; restart with --show-auth-token to disclose it explicitly`,\n ];\n}\n\n/**\n * Runs the CLI and resolves with the process exit code. Serving blocks until\n * the transport closes, so `serve` only resolves on shutdown.\n */\nexport async function runCli(argv: readonly string[], io: CliIo = defaultIo): Promise<number> {\n // Read before parsing: a failure on a later argument must still honour --json.\n let json = argv.includes('--json');\n try {\n const args = parseArgs(argv);\n json = args.json;\n switch (args.command) {\n case 'version':\n io.out(\n json ? JSON.stringify({ name: SERVER_NAME, version: SERVER_VERSION }) : SERVER_VERSION,\n );\n return EXIT_CODES.ok;\n case 'help':\n case 'usage':\n io.out(json ? JSON.stringify(buildAgentContext()) : buildUsage());\n return EXIT_CODES.ok;\n case 'agent-context':\n io.out(JSON.stringify(buildAgentContext(), null, json ? 0 : 2));\n return EXIT_CODES.ok;\n case 'skill': {\n if (args.out === undefined) {\n const files = buildAgentSkill();\n io.out(\n json\n ? JSON.stringify(Object.fromEntries(files.map((file) => [file.path, file.contents])))\n : files.map((file) => `=== ${file.path}\\n${file.contents}`).join('\\n'),\n );\n return EXIT_CODES.ok;\n }\n const written = await writeAgentSkill(args.out);\n io.out(json ? JSON.stringify({ written }) : written.join('\\n'));\n return EXIT_CODES.ok;\n }\n case 'serve': {\n if (args.http) {\n const handle = await serveHttp({\n ...(args.port === undefined ? {} : { port: args.port }),\n ...(args.host === undefined ? {} : { host: args.host }),\n ...(args.allowNonLoopback ? { allowNonLoopback: true } : {}),\n });\n // stderr, never stdout: stdout may be a protocol stream.\n for (const line of httpStartupMessages(args, handle)) io.err(line);\n await new Promise<void>((resolve) => {\n handle.http.on('close', resolve);\n });\n return EXIT_CODES.ok;\n }\n const running = await serveStdio();\n await new Promise<void>((resolve) => {\n const shutdown = (): void => {\n void running.close().then(resolve, resolve);\n };\n process.once('SIGINT', shutdown);\n process.once('SIGTERM', shutdown);\n running.server.server.onclose = shutdown;\n });\n return EXIT_CODES.ok;\n }\n }\n } catch (error) {\n const payload = toErrorPayload(error);\n io.err(json ? JSON.stringify(payload) : `${payload.kind}: ${payload.message}`);\n if (!json && payload.suggestion !== undefined) io.err(`suggestion: ${payload.suggestion}`);\n return exitCodeFor(payload.kind);\n }\n}\n\n/** Entry point for the `termwright-mcp` bin. */\nexport async function main(): Promise<void> {\n process.exitCode = await runCli(process.argv.slice(2));\n}\n"],"mappings":"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","names":["session"]}