@silkweave/mcp 3.2.0 → 3.2.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/build/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as smartToolResult, c as registerTools, d as FilterRequest, f as filterErrorResponse, i as parseResourceMessage, l as requestFromExtra, n as handleToolError, o as structuredToolResult, p as rpcInfo, r as jsonToolResult, s as RegisterToolsOptions, t as errorToolResult, u as FilterActions } from "./result-BN0lxM6Q.mjs";
1
+ import { a as smartToolResult, c as registerTools, d as FilterRequest, f as filterErrorResponse, i as parseResourceMessage, l as requestFromExtra, n as handleToolError, o as structuredToolResult, p as rpcInfo, r as jsonToolResult, s as RegisterToolsOptions, t as errorToolResult, u as FilterActions } from "./result-I04HIJR5.mjs";
2
2
  import { CreateMcpExpressAppOptions } from "@modelcontextprotocol/sdk/server/express.js";
3
3
  import { Action, AdapterFactory, OnToolCall, SilkweaveContext, SilkweaveOptions } from "@silkweave/core";
4
4
  import { Express, RequestHandler } from "express";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/adapter/http.ts","../src/adapter/stdio.ts","../src/handlers/auth.ts","../src/handlers/cors.ts","../src/handlers/metadata.ts","../src/handlers/oauth.ts","../src/handlers/sideload.ts","../src/handlers/transport.ts","../src/util/sideload.ts"],"mappings":";;;;;;;;;;UAciB,mBAAA,SAA4B,0BAAA;EAC3C,IAAA;EACA,IAAA;EACA,IAAA,GAAO,UAAA;EAH4B;EAKnC,IAAA,GAAO,aAAA;EAFA;EAIP,iBAAA;EAWgB;EAThB,WAAA;EAT2C;;;;;;;;EAkB3C,aAAA,GAAgB,aAAA;EAbT;EAeP,UAAA,GAAa,UAAA;AAAA;;;;;;;AAUf;iBAAgB,kBAAA,CACd,gBAAA,EAAkB,gBAAA,EAClB,OAAA,EAAS,gBAAA,EACT,OAAA,EAAS,MAAA,IACT,OAAA,EAAS,mBAAA,GACR,OAAA;;;;;;;iBA8CmB,cAAA,CACpB,gBAAA,EAAkB,gBAAA,EAClB,OAAA,EAAS,MAAA,IACT,OAAA,EAAS,mBAAA,EACT,OAAA,GAAU,gBAAA,GACT,OAAA,CAAQ,MAAA;;;;;cAgBE,IAAA,EAAM,cAAA,CAAe,mBAAA;;;UC/GjB,mBAAA;;EAEf,UAAA,GAAa,UAAA;AAAA;AAAA,cAGF,KAAA,EAAO,cAAA,CAAe,mBAAA;;;;;;;cCDtB,WAAA,EAAW,iBAAA,CAAA,QAAA;;AFKxB;;;;;;;;;iBEOgB,cAAA,CAAe,IAAA,EAAM,UAAA,EAAY,OAAA,EAAS,gBAAA,GAAmB,cAAA;;;;cCjBhE,oBAAA;;;;;;;AHUb;;iBGAgB,OAAA,CAAQ,UAAA,GAAY,aAAA,aAA+B,cAAA;;;;;;;;iBCNnD,yBAAA,CAA0B,IAAA,EAAM,UAAA,GAAa,cAAA;;;UCa5C,kBAAA;;EAEf,mBAAA,EAAqB,cAAA;;EAErB,SAAA,EAAW,cAAA;;EAEX,QAAA,EAAU,cAAA;;EAEV,YAAA;ELfmC;EKiBnC,KAAA,EAAO,cAAA;ELdA;EKgBP,QAAA,EAAU,cAAA;AAAA;;;;;;;;;;;iBAaI,WAAA,CAAY,IAAA,EAAM,UAAA,GAAa,kBAAA;;;UC1C9B,uBAAA;;EAEf,WAAA;AAAA;;;;;;ANQF;iBMCgB,gBAAA,CAAiB,OAAA,GAAS,uBAAA,GAA+B,cAAA;;;UCIxD,oBAAA;;EAEf,IAAA,EAAM,cAAA;AAAA;AAAA,UAGS,mBAAA;;;APVjB;;EOeE,aAAA,GAAgB,aAAA;EPZT;EOcP,UAAA,GAAa,UAAA;AAAA;;;;;;;;;;iBAYC,YAAA,CACd,gBAAA,EAAkB,gBAAA,EAClB,OAAA,EAAS,gBAAA,EACT,OAAA,EAAS,MAAA,IACT,OAAA,GAAS,mBAAA,GACR,oBAAA;;;UC7Cc,gBAAA;EACf,EAAA;EACA,IAAA;EACA,WAAA;EACA,IAAA;AAAA;AAAA,iBAGoB,sBAAA,CAAuB,MAAA,EAAQ,MAAA;EAAU,IAAA;EAAM;AAAA,GAAe,IAAA,CAAK,gBAAA,4BAAyC,OAAA,CAAA,gBAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/adapter/http.ts","../src/adapter/stdio.ts","../src/handlers/auth.ts","../src/handlers/cors.ts","../src/handlers/metadata.ts","../src/handlers/oauth.ts","../src/handlers/sideload.ts","../src/handlers/transport.ts","../src/util/sideload.ts"],"mappings":";;;;;;;;;;UAciB,mBAAA,SAA4B,0BAAA;EAC3C,IAAA;EACA,IAAA;EACA,IAAA,GAAO,UAAA;EAH4B;EAKnC,IAAA,GAAO,aAAA;EAFA;EAIP,iBAAA;EAWgB;EAThB,WAAA;EAT2C;;;;;;;;EAkB3C,aAAA,GAAgB,aAAA;EAbT;EAeP,UAAA,GAAa,UAAA;AAAA;;;;;;;AAUf;iBAAgB,kBAAA,CACd,gBAAA,EAAkB,gBAAA,EAClB,OAAA,EAAS,gBAAA,EACT,OAAA,EAAS,MAAA,IACT,OAAA,EAAS,mBAAA,GACR,OAAA;;;;;;;iBA8CmB,cAAA,CACpB,gBAAA,EAAkB,gBAAA,EAClB,OAAA,EAAS,MAAA,IACT,OAAA,EAAS,mBAAA,EACT,OAAA,GAAU,gBAAA,GACT,OAAA,CAAQ,MAAA;;;;;cAgBE,IAAA,EAAM,cAAA,CAAe,mBAAA;;;UC/GjB,mBAAA;;EAEf,UAAA,GAAa,UAAA;AAAA;AAAA,cAGF,KAAA,EAAO,cAAA,CAAe,mBAAA;;;;;;;cCDtB,WAAA,EAAW,iBAAA,CAAA,QAAA;;AFKxB;;;;;;;;;iBEOgB,cAAA,CAAe,IAAA,EAAM,UAAA,EAAY,OAAA,EAAS,gBAAA,GAAmB,cAAA;;;;cCjBhE,oBAAA;;;;;;;AHUb;;iBGAgB,OAAA,CAAQ,UAAA,GAAY,aAAA,aAA+B,cAAA;;;;;;;;iBCNnD,yBAAA,CAA0B,IAAA,EAAM,UAAA,GAAa,cAAA;;;UCa5C,kBAAA;;EAEf,mBAAA,EAAqB,cAAA;;EAErB,SAAA,EAAW,cAAA;;EAEX,QAAA,EAAU,cAAA;;EAEV,YAAA;ELfmC;EKiBnC,KAAA,EAAO,cAAA;ELdA;EKgBP,QAAA,EAAU,cAAA;AAAA;;;;;;;;;;;iBAaI,WAAA,CAAY,IAAA,EAAM,UAAA,GAAa,kBAAA;;;UCzC9B,uBAAA;;EAEf,WAAA;AAAA;;;;;;ANOF;iBMEgB,gBAAA,CAAiB,OAAA,GAAS,uBAAA,GAA+B,cAAA;;;UCGxD,oBAAA;;EAEf,IAAA,EAAM,cAAA;AAAA;AAAA,UAGS,mBAAA;;;APVjB;;EOeE,aAAA,GAAgB,aAAA;EPZT;EOcP,UAAA,GAAa,UAAA;AAAA;;;;;;;;;;iBAYC,YAAA,CACd,gBAAA,EAAkB,gBAAA,EAClB,OAAA,EAAS,gBAAA,EACT,OAAA,EAAS,MAAA,IACT,OAAA,GAAS,mBAAA,GACR,oBAAA;;;UC7Cc,gBAAA;EACf,EAAA;EACA,IAAA;EACA,WAAA;EACA,IAAA;AAAA;AAAA,iBAGoB,sBAAA,CAAuB,MAAA,EAAQ,MAAA;EAAU,IAAA;EAAM;AAAA,GAAe,IAAA,CAAK,gBAAA,4BAAyC,OAAA,CAAA,gBAAA"}
package/build/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as authMiddleware, i as rpcInfo, n as requestFromExtra, o as authStorage, r as filterErrorResponse, t as registerTools } from "./registerTools-DkaraOyx.mjs";
1
+ import { a as authMiddleware, i as rpcInfo, n as requestFromExtra, o as authStorage, r as filterErrorResponse, t as registerTools } from "./registerTools-B_vjxOrs.mjs";
2
2
  import { a as smartToolResult, i as parseResourceMessage, n as handleToolError, o as structuredToolResult, r as jsonToolResult, t as errorToolResult } from "./result-CXgeE8ca.mjs";
3
3
  import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
4
4
  import { createContext, validateActionDisposition } from "@silkweave/core";
@@ -6,6 +6,7 @@ import express from "express";
6
6
  import { generateProtectedResourceMetadata } from "@silkweave/auth";
7
7
  import cors from "cors";
8
8
  import { readFile, writeFile } from "fs/promises";
9
+ import { basename, resolve, sep } from "path";
9
10
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10
11
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
11
12
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -107,11 +108,18 @@ function oauthRoutes(auth) {
107
108
  */
108
109
  function sideloadResource(options = {}) {
109
110
  const { resourceDir = "resources" } = options;
111
+ const baseDir = resolve(resourceDir);
110
112
  return async (req, res) => {
111
113
  const id = req.params["id"];
112
114
  if (!id || typeof id !== "string") throw new Error("Invalid ID");
113
- const resourceMeta = JSON.parse(await readFile(`${resourceDir}/${id}.json`, "utf-8"));
114
- const buffer = await readFile(`${resourceDir}/${id}`);
115
+ const safeId = basename(id);
116
+ const target = resolve(baseDir, safeId);
117
+ if (safeId !== id || target !== baseDir && !target.startsWith(baseDir + sep)) {
118
+ res.status(400).json({ error: "invalid_resource_id" });
119
+ return;
120
+ }
121
+ const resourceMeta = JSON.parse(await readFile(`${target}.json`, "utf-8"));
122
+ const buffer = await readFile(target);
115
123
  res.status(200);
116
124
  res.header("Content-Type", resourceMeta.contentType);
117
125
  res.send(buffer);
@@ -143,6 +151,17 @@ function createMcpServer(options, actions, context, onToolCall) {
143
151
  function mcpTransport(silkweaveOptions, context, actions, options = {}) {
144
152
  actions.forEach(validateActionDisposition);
145
153
  const post = async (req, res) => {
154
+ if (Array.isArray(req.body)) {
155
+ res.status(400).json({
156
+ jsonrpc: "2.0",
157
+ error: {
158
+ code: -32600,
159
+ message: "JSON-RPC batch requests are not supported"
160
+ },
161
+ id: null
162
+ });
163
+ return;
164
+ }
146
165
  let active = actions;
147
166
  if (options.filterActions) try {
148
167
  active = await options.filterActions(actions, {
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/handlers/cors.ts","../src/handlers/metadata.ts","../src/handlers/oauth.ts","../src/handlers/sideload.ts","../src/handlers/transport.ts","../src/adapter/http.ts","../src/adapter/stdio.ts","../src/util/sideload.ts"],"sourcesContent":["import cors, { CorsOptions } from 'cors'\nimport { type RequestHandler } from 'express'\n\n/** Headers required by the MCP protocol that must always be exposed when CORS is in use. */\nexport const MCP_REQUIRED_HEADERS = ['WWW-Authenticate', 'Last-Event-Id', 'Mcp-Protocol-Version']\n\n/**\n * CORS middleware preconfigured to expose the headers MCP clients need\n * (`Last-Event-Id`, `Mcp-Protocol-Version`, `WWW-Authenticate`) on top of any\n * user-supplied options. (Stateless transport - no `Mcp-Session-Id`.)\n *\n * Pass `false` to disable, omit / pass `true` for permissive defaults, or pass\n * a `CorsOptions` object to override.\n */\nexport function mcpCors(corsConfig: CorsOptions | boolean = true): RequestHandler | null {\n if (corsConfig === false) { return null }\n const userConfig = corsConfig === true ? {} : corsConfig\n const userExposed = userConfig.exposedHeaders\n const exposedHeaders = [\n ...MCP_REQUIRED_HEADERS,\n ...(Array.isArray(userExposed) ? userExposed : userExposed ? [userExposed] : [])\n ]\n return cors({ origin: '*', ...userConfig, exposedHeaders })\n}\n","import { AuthConfig, generateProtectedResourceMetadata } from '@silkweave/auth'\nimport { type RequestHandler } from 'express'\n\n/**\n * Handler for `GET /.well-known/oauth-protected-resource` (RFC 9728). Returns\n * the resource server's metadata pointing at the configured authorization\n * servers. Requires `auth.resourceUrl` and a non-empty `auth.authorizationServers`.\n */\nexport function protectedResourceMetadata(auth: AuthConfig): RequestHandler {\n if (!auth.resourceUrl || !auth.authorizationServers?.length) {\n throw new Error('@silkweave/mcp protectedResourceMetadata(): auth.resourceUrl and auth.authorizationServers are required')\n }\n const metadata = generateProtectedResourceMetadata(auth.resourceUrl, auth.authorizationServers, auth.requiredScopes)\n return (_req, res) => { res.json(metadata) }\n}\n","import { AuthConfig, OAuthRequest, OAuthResponse } from '@silkweave/auth'\nimport express, { type Request, type RequestHandler, type Response } from 'express'\n\nfunction toOAuthReq(req: Request): OAuthRequest {\n return {\n method: req.method,\n url: new URL(req.url, `${req.protocol}://${req.get('host')}`),\n headers: Object.fromEntries(Object.entries(req.headers).map(([k, v]) => [k, Array.isArray(v) ? v[0] : v])),\n body: req.body as Record<string, string> | undefined\n }\n}\n\nfunction sendOAuth(res: Response, oauthRes: OAuthResponse) {\n for (const [key, value] of Object.entries(oauthRes.headers)) { res.header(key, value) }\n if (oauthRes.body) {\n res.status(oauthRes.status).send(typeof oauthRes.body === 'string' ? oauthRes.body : JSON.stringify(oauthRes.body))\n } else {\n res.status(oauthRes.status).end()\n }\n}\n\nexport interface OAuthRouteHandlers {\n /** `GET /.well-known/oauth-authorization-server` - RFC 8414 discovery. */\n wellKnownAuthServer: RequestHandler\n /** `GET /authorize` - start the OAuth flow. */\n authorize: RequestHandler\n /** `GET {callbackPath}` - provider callback. */\n callback: RequestHandler\n /** Path the provider should redirect to (defaults to `/auth/callback`). */\n callbackPath: string\n /** `POST /token` - exchange code / refresh token. Includes urlencoded body parser. */\n token: RequestHandler[]\n /** `POST /register` - dynamic client registration. Includes JSON body parser. */\n register: RequestHandler[]\n}\n\n/**\n * Build the OAuth 2.1 proxy route handlers (authorize, callback, token,\n * register, well-known) backed by the configured `auth.provider`. Returns the\n * handlers as individual `RequestHandler`s so callers can register them\n * wherever they like - under `/mcp`, at the server root, etc.\n *\n * `token` and `register` are returned as middleware arrays because the\n * appropriate body parser must run before the handler. Apply with\n * `app.post(path, ...token)`.\n */\nexport function oauthRoutes(auth: AuthConfig): OAuthRouteHandlers {\n if (!auth.provider) { throw new Error('@silkweave/mcp oauthRoutes(): auth.provider is required') }\n const provider = auth.provider\n const callbackPath = auth.callbackPath ?? '/auth/callback'\n\n return {\n callbackPath,\n wellKnownAuthServer: (_req, res) => { sendOAuth(res, provider.metadata()) },\n authorize: async (req, res) => { sendOAuth(res, await provider.authorize(toOAuthReq(req))) },\n callback: async (req, res) => { sendOAuth(res, await provider.callback(toOAuthReq(req))) },\n token: [\n express.urlencoded({ extended: false }),\n async (req, res) => { sendOAuth(res, await provider.token(toOAuthReq(req))) }\n ],\n register: [\n express.json(),\n async (req, res) => { sendOAuth(res, await provider.register(toOAuthReq(req))) }\n ]\n }\n}\n","import { type RequestHandler } from 'express'\nimport { readFile } from 'fs/promises'\nimport { type SideloadResource } from '../util/sideload.js'\n\nexport interface SideloadResourceOptions {\n /** Directory to read sideload resources from. Defaults to `resources/` (cwd-relative). */\n resourceDir?: string\n}\n\n/**\n * Handler for `GET /resource/:id` - serves a large MCP response that was\n * sideloaded to disk as a `{id}` payload with a `{id}.json` metadata sidecar.\n *\n * The route param `id` is provided by the host framework (Express, Nest, etc.).\n */\nexport function sideloadResource(options: SideloadResourceOptions = {}): RequestHandler {\n const { resourceDir = 'resources' } = options\n return async (req, res) => {\n const id = req.params['id']\n if (!id || typeof id !== 'string') { throw new Error('Invalid ID') }\n const resourceMeta: SideloadResource = JSON.parse(await readFile(`${resourceDir}/${id}.json`, 'utf-8'))\n const buffer = await readFile(`${resourceDir}/${id}`)\n res.status(200)\n res.header('Content-Type', resourceMeta.contentType)\n res.send(buffer)\n }\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'\nimport { Action, OnToolCall, SilkweaveContext, SilkweaveOptions, validateActionDisposition } from '@silkweave/core'\nimport { type RequestHandler } from 'express'\nimport { filterErrorResponse, rpcInfo, type FilterActions } from './filter.js'\nimport { registerTools } from './registerTools.js'\n\nfunction createMcpServer(options: SilkweaveOptions, actions: Action[], context: SilkweaveContext, onToolCall?: OnToolCall): McpServer {\n const server = new McpServer({\n name: options.name,\n description: options.description,\n version: options.version\n }, {\n capabilities: { tools: {}, logging: {} }\n })\n registerTools(server, actions, context, { onToolCall })\n return server\n}\n\nexport interface McpTransportHandlers {\n /** `POST /mcp` - handle a single stateless MCP request/response (SSE per call). */\n post: RequestHandler\n}\n\nexport interface McpTransportOptions {\n /**\n * Per-request tool filter, applied before `registerTools()` on every POST.\n * See `FilterActions` for the request stand-in and error semantics.\n */\n filterActions?: FilterActions\n /** Telemetry hook invoked once per tool call (fire-and-forget). */\n onToolCall?: OnToolCall\n}\n\n/**\n * Build the MCP Streamable HTTP transport route handler.\n *\n * Stateless (per the 2026 spec direction): each `POST /mcp` mints a fresh\n * transport + server with `sessionIdGenerator: undefined`, handles exactly that\n * request (streaming progress over SSE when the call carries a `progressToken`),\n * and tears down on response close. No `Mcp-Session-Id`, no session map, no\n * `GET`/`DELETE` reconnect - any request can hit any instance.\n */\nexport function mcpTransport(\n silkweaveOptions: SilkweaveOptions,\n context: SilkweaveContext,\n actions: Action[],\n options: McpTransportOptions = {}\n): McpTransportHandlers {\n // Fail at boot, not per request - the factory runs once when the app is built.\n actions.forEach(validateActionDisposition)\n\n const post: RequestHandler = async (req, res) => {\n let active = actions\n if (options.filterActions) {\n try {\n active = await options.filterActions(actions, { headers: req.headers, url: req.originalUrl ?? req.url, ...rpcInfo(req.body) })\n } catch (error) {\n // A throw never degrades to an empty tool list - it surfaces as its\n // statusCode (SilkweaveError) or a 500, so a bad key reads as an auth\n // failure rather than \"server has no tools\".\n const { status, body } = filterErrorResponse(error, req.body)\n res.status(status).json(body)\n return\n }\n }\n try {\n const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined })\n const server = createMcpServer(silkweaveOptions, active, context, options.onToolCall)\n res.on('close', () => { void transport.close(); void server.close() })\n await server.connect(transport)\n await transport.handleRequest(req, res, req.body)\n } catch (error) {\n console.error('Error handling MCP request:', error)\n if (!res.headersSent) {\n res.status(500).json({ jsonrpc: '2.0', error: { code: -32_603, message: 'Internal server error' }, id: null })\n }\n }\n }\n\n return { post }\n}\n","import { createMcpExpressApp, type CreateMcpExpressAppOptions } from '@modelcontextprotocol/sdk/server/express.js'\nimport { AuthConfig } from '@silkweave/auth'\nimport { Action, AdapterFactory, createContext, OnToolCall, SilkweaveContext, SilkweaveOptions } from '@silkweave/core'\nimport { CorsOptions } from 'cors'\nimport express, { type Express } from 'express'\nimport { Server } from 'http'\nimport { authMiddleware } from '../handlers/auth.js'\nimport { mcpCors } from '../handlers/cors.js'\nimport { protectedResourceMetadata } from '../handlers/metadata.js'\nimport { oauthRoutes } from '../handlers/oauth.js'\nimport { type FilterActions } from '../handlers/filter.js'\nimport { sideloadResource } from '../handlers/sideload.js'\nimport { mcpTransport } from '../handlers/transport.js'\n\nexport interface StartMcpHttpOptions extends CreateMcpExpressAppOptions {\n host: string\n port: number\n auth?: AuthConfig\n /** CORS configuration. `false` to disable, omitted/`true` for permissive defaults, or a `CorsOptions` object. */\n cors?: CorsOptions | boolean\n /** Mount the `/resource/:id` sideload route. Default `true`. */\n sideloadResources?: boolean\n /** Directory the sideload route reads from. Default `'resources'`. */\n resourceDir?: string\n /**\n * Per-request tool filter, applied before `registerTools()` on every\n * `POST /mcp` (the stateless transport recomputes the tool list per request,\n * so permission changes apply on the next `tools/list`). See `FilterActions`\n * for the request stand-in (`headers`/`url`/`method`/`toolName`) and error\n * semantics (a throw surfaces as its `SilkweaveError.statusCode` or 500 -\n * never an empty tool list).\n */\n filterActions?: FilterActions\n /** Telemetry hook invoked once per tool call (fire-and-forget). */\n onToolCall?: OnToolCall\n}\n\n/**\n * Build a fully-wired Express app that exposes the MCP Streamable HTTP\n * transport (plus OAuth / sideload / well-known routes when configured).\n *\n * Pass the resulting `app` to `app.listen(port, host)` yourself, or use the\n * top-level `startMcpServer()` / `http()` adapter conveniences.\n */\nexport function buildMcpExpressApp(\n silkweaveOptions: SilkweaveOptions,\n context: SilkweaveContext,\n actions: Action[],\n options: StartMcpHttpOptions\n): Express {\n const { host, auth, cors: corsConfig, sideloadResources = true, resourceDir, filterActions, onToolCall, ...mcpAppOptions } = options\n const app = createMcpExpressApp({ ...mcpAppOptions, host })\n\n const corsHandler = mcpCors(corsConfig ?? true)\n if (corsHandler) { app.use(corsHandler) }\n\n if (auth?.authorizationServers?.length && auth.resourceUrl) {\n app.get('/.well-known/oauth-protected-resource', protectedResourceMetadata(auth))\n }\n\n let oauthPaths = new Set<string>()\n if (auth?.provider) {\n const oauth = oauthRoutes(auth)\n app.get('/.well-known/oauth-authorization-server', oauth.wellKnownAuthServer)\n app.get('/authorize', oauth.authorize)\n app.get(oauth.callbackPath, oauth.callback)\n app.post('/token', ...oauth.token)\n app.post('/register', ...oauth.register)\n oauthPaths = new Set(['/.well-known/oauth-authorization-server', '/authorize', oauth.callbackPath, '/token', '/register'])\n }\n\n if (auth) {\n const guard = authMiddleware(auth, context)\n app.use((req, res, next) => {\n if (req.path.startsWith('/.well-known/') || oauthPaths.has(req.path)) { return next() }\n return guard(req, res, next)\n })\n }\n\n if (sideloadResources) {\n app.get('/resource/:id', sideloadResource({ resourceDir }))\n }\n\n const transport = mcpTransport(silkweaveOptions, context, actions, { filterActions, onToolCall })\n app.post('/mcp', express.json(), transport.post)\n\n return app\n}\n\n/**\n * Spin up a standalone MCP Streamable HTTP server on `host:port` for the\n * given `actions`. Returns the underlying `Server` so callers can close it.\n *\n * Convenience for use cases that don't go through the `silkweave()` builder.\n */\nexport async function startMcpServer(\n silkweaveOptions: SilkweaveOptions,\n actions: Action[],\n options: StartMcpHttpOptions,\n context?: SilkweaveContext\n): Promise<Server> {\n const ctx = context ?? createContext({ adapter: 'http' })\n const app = buildMcpExpressApp(silkweaveOptions, ctx, actions, options)\n return new Promise<Server>((resolve, reject) => {\n const server = app.listen(options.port, options.host, (error) => {\n if (error) { reject(error); return }\n console.log(`MCP Streamable HTTP Server listening on http://${options.host}:${options.port}/mcp`)\n resolve(server)\n })\n })\n}\n\n/**\n * Silkweave adapter that owns its own HTTP server. Composes the MCP handler\n * primitives into a fully-wired Express app and listens on `host:port`.\n */\nexport const http: AdapterFactory<StartMcpHttpOptions> = (options) => {\n return (silkweaveOptions, baseContext) => {\n const context = baseContext.fork({ adapter: 'http' })\n let httpServer: Server | undefined\n return {\n context,\n start: async (actions) => {\n const app = buildMcpExpressApp(silkweaveOptions, context, actions, options)\n httpServer = await new Promise<Server>((resolve, reject) => {\n const s = app.listen(options.port, options.host, (error) => {\n if (error) { reject(error); return }\n console.log(`MCP Streamable HTTP Server listening on http://${options.host}:${options.port}/mcp`)\n resolve(s)\n })\n })\n },\n stop: async () => {\n if (!httpServer) { return }\n await new Promise<void>((resolve, reject) => {\n httpServer!.close((err) => err ? reject(err) : resolve())\n })\n httpServer = undefined\n }\n }\n }\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { AdapterFactory, OnToolCall, validateActionDisposition } from '@silkweave/core'\nimport { registerTools } from '../handlers/registerTools.js'\n\nexport interface StdioAdapterOptions {\n /** Telemetry hook invoked once per tool call (fire-and-forget). */\n onToolCall?: OnToolCall\n}\n\nexport const stdio: AdapterFactory<StdioAdapterOptions | void> = (adapterOptions) => {\n return (options, baseContext) => {\n const context = baseContext.fork({ adapter: 'stdio' })\n const server = new McpServer({\n name: options.name,\n description: options.description,\n version: options.version\n }, {\n capabilities: { tools: {}, logging: {} }\n })\n return {\n context,\n start: async (actions) => {\n actions.forEach(validateActionDisposition)\n registerTools(server, actions, context, { logStream: false, onToolCall: adapterOptions?.onToolCall })\n const transport = new StdioServerTransport()\n await server.connect(transport)\n },\n stop: async () => {\n await server?.close()\n }\n }\n }\n}\n","import { randomUUID } from 'crypto'\nimport { writeFile } from 'fs/promises'\n\nexport interface SideloadResource {\n id: string\n name: string\n contentType: string\n size: number\n}\n\nexport async function createSideloadResource(buffer: Buffer, { name, contentType }: Pick<SideloadResource, 'name' | 'contentType'>) {\n const id = randomUUID()\n const resource: SideloadResource = { id, name, contentType, size: buffer.length }\n await Promise.all([\n writeFile(`resources/${id}`, buffer),\n writeFile(`resources/${id}.json`, JSON.stringify(resource), 'utf-8')\n ])\n return resource\n}\n"],"mappings":";;;;;;;;;;;;;;AAIA,MAAa,uBAAuB;CAAC;CAAoB;CAAiB;CAAuB;;;;;;;;;AAUjG,SAAgB,QAAQ,aAAoC,MAA6B;AACvF,KAAI,eAAe,MAAS,QAAO;CACnC,MAAM,aAAa,eAAe,OAAO,EAAE,GAAG;CAC9C,MAAM,cAAc,WAAW;CAC/B,MAAM,iBAAiB,CACrB,GAAG,sBACH,GAAI,MAAM,QAAQ,YAAY,GAAG,cAAc,cAAc,CAAC,YAAY,GAAG,EAAE,CAChF;AACD,QAAO,KAAK;EAAE,QAAQ;EAAK,GAAG;EAAY;EAAgB,CAAC;;;;;;;;;ACd7D,SAAgB,0BAA0B,MAAkC;AAC1E,KAAI,CAAC,KAAK,eAAe,CAAC,KAAK,sBAAsB,OACnD,OAAM,IAAI,MAAM,0GAA0G;CAE5H,MAAM,WAAW,kCAAkC,KAAK,aAAa,KAAK,sBAAsB,KAAK,eAAe;AACpH,SAAQ,MAAM,QAAQ;AAAE,MAAI,KAAK,SAAS;;;;;ACV5C,SAAS,WAAW,KAA4B;AAC9C,QAAO;EACL,QAAQ,IAAI;EACZ,KAAK,IAAI,IAAI,IAAI,KAAK,GAAG,IAAI,SAAS,KAAK,IAAI,IAAI,OAAO,GAAG;EAC7D,SAAS,OAAO,YAAY,OAAO,QAAQ,IAAI,QAAQ,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,GAAG,MAAM,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;EAC1G,MAAM,IAAI;EACX;;AAGH,SAAS,UAAU,KAAe,UAAyB;AACzD,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,SAAS,QAAQ,CAAI,KAAI,OAAO,KAAK,MAAM;AACrF,KAAI,SAAS,KACX,KAAI,OAAO,SAAS,OAAO,CAAC,KAAK,OAAO,SAAS,SAAS,WAAW,SAAS,OAAO,KAAK,UAAU,SAAS,KAAK,CAAC;KAEnH,KAAI,OAAO,SAAS,OAAO,CAAC,KAAK;;;;;;;;;;;;AA6BrC,SAAgB,YAAY,MAAsC;AAChE,KAAI,CAAC,KAAK,SAAY,OAAM,IAAI,MAAM,0DAA0D;CAChG,MAAM,WAAW,KAAK;AAGtB,QAAO;EACL,cAHmB,KAAK,gBAAgB;EAIxC,sBAAsB,MAAM,QAAQ;AAAE,aAAU,KAAK,SAAS,UAAU,CAAC;;EACzE,WAAW,OAAO,KAAK,QAAQ;AAAE,aAAU,KAAK,MAAM,SAAS,UAAU,WAAW,IAAI,CAAC,CAAC;;EAC1F,UAAU,OAAO,KAAK,QAAQ;AAAE,aAAU,KAAK,MAAM,SAAS,SAAS,WAAW,IAAI,CAAC,CAAC;;EACxF,OAAO,CACL,QAAQ,WAAW,EAAE,UAAU,OAAO,CAAC,EACvC,OAAO,KAAK,QAAQ;AAAE,aAAU,KAAK,MAAM,SAAS,MAAM,WAAW,IAAI,CAAC,CAAC;IAC5E;EACD,UAAU,CACR,QAAQ,MAAM,EACd,OAAO,KAAK,QAAQ;AAAE,aAAU,KAAK,MAAM,SAAS,SAAS,WAAW,IAAI,CAAC,CAAC;IAC/E;EACF;;;;;;;;;;ACjDH,SAAgB,iBAAiB,UAAmC,EAAE,EAAkB;CACtF,MAAM,EAAE,cAAc,gBAAgB;AACtC,QAAO,OAAO,KAAK,QAAQ;EACzB,MAAM,KAAK,IAAI,OAAO;AACtB,MAAI,CAAC,MAAM,OAAO,OAAO,SAAY,OAAM,IAAI,MAAM,aAAa;EAClE,MAAM,eAAiC,KAAK,MAAM,MAAM,SAAS,GAAG,YAAY,GAAG,GAAG,QAAQ,QAAQ,CAAC;EACvG,MAAM,SAAS,MAAM,SAAS,GAAG,YAAY,GAAG,KAAK;AACrD,MAAI,OAAO,IAAI;AACf,MAAI,OAAO,gBAAgB,aAAa,YAAY;AACpD,MAAI,KAAK,OAAO;;;;;ACjBpB,SAAS,gBAAgB,SAA2B,SAAmB,SAA2B,YAAoC;CACpI,MAAM,SAAS,IAAI,UAAU;EAC3B,MAAM,QAAQ;EACd,aAAa,QAAQ;EACrB,SAAS,QAAQ;EAClB,EAAE,EACD,cAAc;EAAE,OAAO,EAAE;EAAE,SAAS,EAAE;EAAE,EACzC,CAAC;AACF,eAAc,QAAQ,SAAS,SAAS,EAAE,YAAY,CAAC;AACvD,QAAO;;;;;;;;;;;AA2BT,SAAgB,aACd,kBACA,SACA,SACA,UAA+B,EAAE,EACX;AAEtB,SAAQ,QAAQ,0BAA0B;CAE1C,MAAM,OAAuB,OAAO,KAAK,QAAQ;EAC/C,IAAI,SAAS;AACb,MAAI,QAAQ,cACV,KAAI;AACF,YAAS,MAAM,QAAQ,cAAc,SAAS;IAAE,SAAS,IAAI;IAAS,KAAK,IAAI,eAAe,IAAI;IAAK,GAAG,QAAQ,IAAI,KAAK;IAAE,CAAC;WACvH,OAAO;GAId,MAAM,EAAE,QAAQ,SAAS,oBAAoB,OAAO,IAAI,KAAK;AAC7D,OAAI,OAAO,OAAO,CAAC,KAAK,KAAK;AAC7B;;AAGJ,MAAI;GACF,MAAM,YAAY,IAAI,8BAA8B,EAAE,oBAAoB,KAAA,GAAW,CAAC;GACtF,MAAM,SAAS,gBAAgB,kBAAkB,QAAQ,SAAS,QAAQ,WAAW;AACrF,OAAI,GAAG,eAAe;AAAO,cAAU,OAAO;AAAO,WAAO,OAAO;KAAG;AACtE,SAAM,OAAO,QAAQ,UAAU;AAC/B,SAAM,UAAU,cAAc,KAAK,KAAK,IAAI,KAAK;WAC1C,OAAO;AACd,WAAQ,MAAM,+BAA+B,MAAM;AACnD,OAAI,CAAC,IAAI,YACP,KAAI,OAAO,IAAI,CAAC,KAAK;IAAE,SAAS;IAAO,OAAO;KAAE,MAAM;KAAS,SAAS;KAAyB;IAAE,IAAI;IAAM,CAAC;;;AAKpH,QAAO,EAAE,MAAM;;;;;;;;;;;ACpCjB,SAAgB,mBACd,kBACA,SACA,SACA,SACS;CACT,MAAM,EAAE,MAAM,MAAM,MAAM,YAAY,oBAAoB,MAAM,aAAa,eAAe,YAAY,GAAG,kBAAkB;CAC7H,MAAM,MAAM,oBAAoB;EAAE,GAAG;EAAe;EAAM,CAAC;CAE3D,MAAM,cAAc,QAAQ,cAAc,KAAK;AAC/C,KAAI,YAAe,KAAI,IAAI,YAAY;AAEvC,KAAI,MAAM,sBAAsB,UAAU,KAAK,YAC7C,KAAI,IAAI,yCAAyC,0BAA0B,KAAK,CAAC;CAGnF,IAAI,6BAAa,IAAI,KAAa;AAClC,KAAI,MAAM,UAAU;EAClB,MAAM,QAAQ,YAAY,KAAK;AAC/B,MAAI,IAAI,2CAA2C,MAAM,oBAAoB;AAC7E,MAAI,IAAI,cAAc,MAAM,UAAU;AACtC,MAAI,IAAI,MAAM,cAAc,MAAM,SAAS;AAC3C,MAAI,KAAK,UAAU,GAAG,MAAM,MAAM;AAClC,MAAI,KAAK,aAAa,GAAG,MAAM,SAAS;AACxC,eAAa,IAAI,IAAI;GAAC;GAA2C;GAAc,MAAM;GAAc;GAAU;GAAY,CAAC;;AAG5H,KAAI,MAAM;EACR,MAAM,QAAQ,eAAe,MAAM,QAAQ;AAC3C,MAAI,KAAK,KAAK,KAAK,SAAS;AAC1B,OAAI,IAAI,KAAK,WAAW,gBAAgB,IAAI,WAAW,IAAI,IAAI,KAAK,CAAI,QAAO,MAAM;AACrF,UAAO,MAAM,KAAK,KAAK,KAAK;IAC5B;;AAGJ,KAAI,kBACF,KAAI,IAAI,iBAAiB,iBAAiB,EAAE,aAAa,CAAC,CAAC;CAG7D,MAAM,YAAY,aAAa,kBAAkB,SAAS,SAAS;EAAE;EAAe;EAAY,CAAC;AACjG,KAAI,KAAK,QAAQ,QAAQ,MAAM,EAAE,UAAU,KAAK;AAEhD,QAAO;;;;;;;;AAST,eAAsB,eACpB,kBACA,SACA,SACA,SACiB;CAEjB,MAAM,MAAM,mBAAmB,kBADnB,WAAW,cAAc,EAAE,SAAS,QAAQ,CAAC,EACH,SAAS,QAAQ;AACvE,QAAO,IAAI,SAAiB,SAAS,WAAW;EAC9C,MAAM,SAAS,IAAI,OAAO,QAAQ,MAAM,QAAQ,OAAO,UAAU;AAC/D,OAAI,OAAO;AAAE,WAAO,MAAM;AAAE;;AAC5B,WAAQ,IAAI,kDAAkD,QAAQ,KAAK,GAAG,QAAQ,KAAK,MAAM;AACjG,WAAQ,OAAO;IACf;GACF;;;;;;AAOJ,MAAa,QAA6C,YAAY;AACpE,SAAQ,kBAAkB,gBAAgB;EACxC,MAAM,UAAU,YAAY,KAAK,EAAE,SAAS,QAAQ,CAAC;EACrD,IAAI;AACJ,SAAO;GACL;GACA,OAAO,OAAO,YAAY;IACxB,MAAM,MAAM,mBAAmB,kBAAkB,SAAS,SAAS,QAAQ;AAC3E,iBAAa,MAAM,IAAI,SAAiB,SAAS,WAAW;KAC1D,MAAM,IAAI,IAAI,OAAO,QAAQ,MAAM,QAAQ,OAAO,UAAU;AAC1D,UAAI,OAAO;AAAE,cAAO,MAAM;AAAE;;AAC5B,cAAQ,IAAI,kDAAkD,QAAQ,KAAK,GAAG,QAAQ,KAAK,MAAM;AACjG,cAAQ,EAAE;OACV;MACF;;GAEJ,MAAM,YAAY;AAChB,QAAI,CAAC,WAAc;AACnB,UAAM,IAAI,SAAe,SAAS,WAAW;AAC3C,gBAAY,OAAO,QAAQ,MAAM,OAAO,IAAI,GAAG,SAAS,CAAC;MACzD;AACF,iBAAa,KAAA;;GAEhB;;;;;ACjIL,MAAa,SAAqD,mBAAmB;AACnF,SAAQ,SAAS,gBAAgB;EAC/B,MAAM,UAAU,YAAY,KAAK,EAAE,SAAS,SAAS,CAAC;EACtD,MAAM,SAAS,IAAI,UAAU;GAC3B,MAAM,QAAQ;GACd,aAAa,QAAQ;GACrB,SAAS,QAAQ;GAClB,EAAE,EACD,cAAc;GAAE,OAAO,EAAE;GAAE,SAAS,EAAE;GAAE,EACzC,CAAC;AACF,SAAO;GACL;GACA,OAAO,OAAO,YAAY;AACxB,YAAQ,QAAQ,0BAA0B;AAC1C,kBAAc,QAAQ,SAAS,SAAS;KAAE,WAAW;KAAO,YAAY,gBAAgB;KAAY,CAAC;IACrG,MAAM,YAAY,IAAI,sBAAsB;AAC5C,UAAM,OAAO,QAAQ,UAAU;;GAEjC,MAAM,YAAY;AAChB,UAAM,QAAQ,OAAO;;GAExB;;;;;ACrBL,eAAsB,uBAAuB,QAAgB,EAAE,MAAM,eAA+D;CAClI,MAAM,KAAK,YAAY;CACvB,MAAM,WAA6B;EAAE;EAAI;EAAM;EAAa,MAAM,OAAO;EAAQ;AACjF,OAAM,QAAQ,IAAI,CAChB,UAAU,aAAa,MAAM,OAAO,EACpC,UAAU,aAAa,GAAG,QAAQ,KAAK,UAAU,SAAS,EAAE,QAAQ,CACrE,CAAC;AACF,QAAO"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/handlers/cors.ts","../src/handlers/metadata.ts","../src/handlers/oauth.ts","../src/handlers/sideload.ts","../src/handlers/transport.ts","../src/adapter/http.ts","../src/adapter/stdio.ts","../src/util/sideload.ts"],"sourcesContent":["import cors, { CorsOptions } from 'cors'\nimport { type RequestHandler } from 'express'\n\n/** Headers required by the MCP protocol that must always be exposed when CORS is in use. */\nexport const MCP_REQUIRED_HEADERS = ['WWW-Authenticate', 'Last-Event-Id', 'Mcp-Protocol-Version']\n\n/**\n * CORS middleware preconfigured to expose the headers MCP clients need\n * (`Last-Event-Id`, `Mcp-Protocol-Version`, `WWW-Authenticate`) on top of any\n * user-supplied options. (Stateless transport - no `Mcp-Session-Id`.)\n *\n * Pass `false` to disable, omit / pass `true` for permissive defaults, or pass\n * a `CorsOptions` object to override.\n */\nexport function mcpCors(corsConfig: CorsOptions | boolean = true): RequestHandler | null {\n if (corsConfig === false) { return null }\n const userConfig = corsConfig === true ? {} : corsConfig\n const userExposed = userConfig.exposedHeaders\n const exposedHeaders = [\n ...MCP_REQUIRED_HEADERS,\n ...(Array.isArray(userExposed) ? userExposed : userExposed ? [userExposed] : [])\n ]\n return cors({ origin: '*', ...userConfig, exposedHeaders })\n}\n","import { AuthConfig, generateProtectedResourceMetadata } from '@silkweave/auth'\nimport { type RequestHandler } from 'express'\n\n/**\n * Handler for `GET /.well-known/oauth-protected-resource` (RFC 9728). Returns\n * the resource server's metadata pointing at the configured authorization\n * servers. Requires `auth.resourceUrl` and a non-empty `auth.authorizationServers`.\n */\nexport function protectedResourceMetadata(auth: AuthConfig): RequestHandler {\n if (!auth.resourceUrl || !auth.authorizationServers?.length) {\n throw new Error('@silkweave/mcp protectedResourceMetadata(): auth.resourceUrl and auth.authorizationServers are required')\n }\n const metadata = generateProtectedResourceMetadata(auth.resourceUrl, auth.authorizationServers, auth.requiredScopes)\n return (_req, res) => { res.json(metadata) }\n}\n","import { AuthConfig, OAuthRequest, OAuthResponse } from '@silkweave/auth'\nimport express, { type Request, type RequestHandler, type Response } from 'express'\n\nfunction toOAuthReq(req: Request): OAuthRequest {\n return {\n method: req.method,\n url: new URL(req.url, `${req.protocol}://${req.get('host')}`),\n headers: Object.fromEntries(Object.entries(req.headers).map(([k, v]) => [k, Array.isArray(v) ? v[0] : v])),\n body: req.body as Record<string, string> | undefined\n }\n}\n\nfunction sendOAuth(res: Response, oauthRes: OAuthResponse) {\n for (const [key, value] of Object.entries(oauthRes.headers)) { res.header(key, value) }\n if (oauthRes.body) {\n res.status(oauthRes.status).send(typeof oauthRes.body === 'string' ? oauthRes.body : JSON.stringify(oauthRes.body))\n } else {\n res.status(oauthRes.status).end()\n }\n}\n\nexport interface OAuthRouteHandlers {\n /** `GET /.well-known/oauth-authorization-server` - RFC 8414 discovery. */\n wellKnownAuthServer: RequestHandler\n /** `GET /authorize` - start the OAuth flow. */\n authorize: RequestHandler\n /** `GET {callbackPath}` - provider callback. */\n callback: RequestHandler\n /** Path the provider should redirect to (defaults to `/auth/callback`). */\n callbackPath: string\n /** `POST /token` - exchange code / refresh token. Includes urlencoded body parser. */\n token: RequestHandler[]\n /** `POST /register` - dynamic client registration. Includes JSON body parser. */\n register: RequestHandler[]\n}\n\n/**\n * Build the OAuth 2.1 proxy route handlers (authorize, callback, token,\n * register, well-known) backed by the configured `auth.provider`. Returns the\n * handlers as individual `RequestHandler`s so callers can register them\n * wherever they like - under `/mcp`, at the server root, etc.\n *\n * `token` and `register` are returned as middleware arrays because the\n * appropriate body parser must run before the handler. Apply with\n * `app.post(path, ...token)`.\n */\nexport function oauthRoutes(auth: AuthConfig): OAuthRouteHandlers {\n if (!auth.provider) { throw new Error('@silkweave/mcp oauthRoutes(): auth.provider is required') }\n const provider = auth.provider\n const callbackPath = auth.callbackPath ?? '/auth/callback'\n\n return {\n callbackPath,\n wellKnownAuthServer: (_req, res) => { sendOAuth(res, provider.metadata()) },\n authorize: async (req, res) => { sendOAuth(res, await provider.authorize(toOAuthReq(req))) },\n callback: async (req, res) => { sendOAuth(res, await provider.callback(toOAuthReq(req))) },\n token: [\n express.urlencoded({ extended: false }),\n async (req, res) => { sendOAuth(res, await provider.token(toOAuthReq(req))) }\n ],\n register: [\n express.json(),\n async (req, res) => { sendOAuth(res, await provider.register(toOAuthReq(req))) }\n ]\n }\n}\n","import { type RequestHandler } from 'express'\nimport { readFile } from 'fs/promises'\nimport { basename, resolve, sep } from 'path'\nimport { type SideloadResource } from '../util/sideload.js'\n\nexport interface SideloadResourceOptions {\n /** Directory to read sideload resources from. Defaults to `resources/` (cwd-relative). */\n resourceDir?: string\n}\n\n/**\n * Handler for `GET /resource/:id` - serves a large MCP response that was\n * sideloaded to disk as a `{id}` payload with a `{id}.json` metadata sidecar.\n *\n * The route param `id` is provided by the host framework (Express, Nest, etc.).\n */\nexport function sideloadResource(options: SideloadResourceOptions = {}): RequestHandler {\n const { resourceDir = 'resources' } = options\n const baseDir = resolve(resourceDir)\n return async (req, res) => {\n const id = req.params['id']\n if (!id || typeof id !== 'string') { throw new Error('Invalid ID') }\n // Contain the read to resourceDir. Express 5 decodes %2F in the route param,\n // so an untrusted `id` like `../../etc/passwd` would otherwise escape the\n // directory. basename() strips any path separators; the resolve+prefix check\n // is defense in depth against platform-specific separator handling.\n const safeId = basename(id)\n const target = resolve(baseDir, safeId)\n if (safeId !== id || (target !== baseDir && !target.startsWith(baseDir + sep))) {\n res.status(400).json({ error: 'invalid_resource_id' })\n return\n }\n const resourceMeta: SideloadResource = JSON.parse(await readFile(`${target}.json`, 'utf-8'))\n const buffer = await readFile(target)\n res.status(200)\n res.header('Content-Type', resourceMeta.contentType)\n res.send(buffer)\n }\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'\nimport { Action, OnToolCall, SilkweaveContext, SilkweaveOptions, validateActionDisposition } from '@silkweave/core'\nimport { type RequestHandler } from 'express'\nimport { filterErrorResponse, rpcInfo, type FilterActions } from './filter.js'\nimport { registerTools } from './registerTools.js'\n\nfunction createMcpServer(options: SilkweaveOptions, actions: Action[], context: SilkweaveContext, onToolCall?: OnToolCall): McpServer {\n const server = new McpServer({\n name: options.name,\n description: options.description,\n version: options.version\n }, {\n capabilities: { tools: {}, logging: {} }\n })\n registerTools(server, actions, context, { onToolCall })\n return server\n}\n\nexport interface McpTransportHandlers {\n /** `POST /mcp` - handle a single stateless MCP request/response (SSE per call). */\n post: RequestHandler\n}\n\nexport interface McpTransportOptions {\n /**\n * Per-request tool filter, applied before `registerTools()` on every POST.\n * See `FilterActions` for the request stand-in and error semantics.\n */\n filterActions?: FilterActions\n /** Telemetry hook invoked once per tool call (fire-and-forget). */\n onToolCall?: OnToolCall\n}\n\n/**\n * Build the MCP Streamable HTTP transport route handler.\n *\n * Stateless (per the 2026 spec direction): each `POST /mcp` mints a fresh\n * transport + server with `sessionIdGenerator: undefined`, handles exactly that\n * request (streaming progress over SSE when the call carries a `progressToken`),\n * and tears down on response close. No `Mcp-Session-Id`, no session map, no\n * `GET`/`DELETE` reconnect - any request can hit any instance.\n */\nexport function mcpTransport(\n silkweaveOptions: SilkweaveOptions,\n context: SilkweaveContext,\n actions: Action[],\n options: McpTransportOptions = {}\n): McpTransportHandlers {\n // Fail at boot, not per request - the factory runs once when the app is built.\n actions.forEach(validateActionDisposition)\n\n const post: RequestHandler = async (req, res) => {\n // JSON-RPC batching was removed from the MCP spec (2025-06-18). A batch also\n // defeats per-request filterActions: rpcInfo reflects only the first message\n // but the SDK transport would execute every entry, so a later batch entry\n // could invoke a tool the filter gated on the first. Reject batches outright.\n if (Array.isArray(req.body)) {\n res.status(400).json({ jsonrpc: '2.0', error: { code: -32_600, message: 'JSON-RPC batch requests are not supported' }, id: null })\n return\n }\n let active = actions\n if (options.filterActions) {\n try {\n active = await options.filterActions(actions, { headers: req.headers, url: req.originalUrl ?? req.url, ...rpcInfo(req.body) })\n } catch (error) {\n // A throw never degrades to an empty tool list - it surfaces as its\n // statusCode (SilkweaveError) or a 500, so a bad key reads as an auth\n // failure rather than \"server has no tools\".\n const { status, body } = filterErrorResponse(error, req.body)\n res.status(status).json(body)\n return\n }\n }\n try {\n const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined })\n const server = createMcpServer(silkweaveOptions, active, context, options.onToolCall)\n res.on('close', () => { void transport.close(); void server.close() })\n await server.connect(transport)\n await transport.handleRequest(req, res, req.body)\n } catch (error) {\n console.error('Error handling MCP request:', error)\n if (!res.headersSent) {\n res.status(500).json({ jsonrpc: '2.0', error: { code: -32_603, message: 'Internal server error' }, id: null })\n }\n }\n }\n\n return { post }\n}\n","import { createMcpExpressApp, type CreateMcpExpressAppOptions } from '@modelcontextprotocol/sdk/server/express.js'\nimport { AuthConfig } from '@silkweave/auth'\nimport { Action, AdapterFactory, createContext, OnToolCall, SilkweaveContext, SilkweaveOptions } from '@silkweave/core'\nimport { CorsOptions } from 'cors'\nimport express, { type Express } from 'express'\nimport { Server } from 'http'\nimport { authMiddleware } from '../handlers/auth.js'\nimport { mcpCors } from '../handlers/cors.js'\nimport { protectedResourceMetadata } from '../handlers/metadata.js'\nimport { oauthRoutes } from '../handlers/oauth.js'\nimport { type FilterActions } from '../handlers/filter.js'\nimport { sideloadResource } from '../handlers/sideload.js'\nimport { mcpTransport } from '../handlers/transport.js'\n\nexport interface StartMcpHttpOptions extends CreateMcpExpressAppOptions {\n host: string\n port: number\n auth?: AuthConfig\n /** CORS configuration. `false` to disable, omitted/`true` for permissive defaults, or a `CorsOptions` object. */\n cors?: CorsOptions | boolean\n /** Mount the `/resource/:id` sideload route. Default `true`. */\n sideloadResources?: boolean\n /** Directory the sideload route reads from. Default `'resources'`. */\n resourceDir?: string\n /**\n * Per-request tool filter, applied before `registerTools()` on every\n * `POST /mcp` (the stateless transport recomputes the tool list per request,\n * so permission changes apply on the next `tools/list`). See `FilterActions`\n * for the request stand-in (`headers`/`url`/`method`/`toolName`) and error\n * semantics (a throw surfaces as its `SilkweaveError.statusCode` or 500 -\n * never an empty tool list).\n */\n filterActions?: FilterActions\n /** Telemetry hook invoked once per tool call (fire-and-forget). */\n onToolCall?: OnToolCall\n}\n\n/**\n * Build a fully-wired Express app that exposes the MCP Streamable HTTP\n * transport (plus OAuth / sideload / well-known routes when configured).\n *\n * Pass the resulting `app` to `app.listen(port, host)` yourself, or use the\n * top-level `startMcpServer()` / `http()` adapter conveniences.\n */\nexport function buildMcpExpressApp(\n silkweaveOptions: SilkweaveOptions,\n context: SilkweaveContext,\n actions: Action[],\n options: StartMcpHttpOptions\n): Express {\n const { host, auth, cors: corsConfig, sideloadResources = true, resourceDir, filterActions, onToolCall, ...mcpAppOptions } = options\n const app = createMcpExpressApp({ ...mcpAppOptions, host })\n\n const corsHandler = mcpCors(corsConfig ?? true)\n if (corsHandler) { app.use(corsHandler) }\n\n if (auth?.authorizationServers?.length && auth.resourceUrl) {\n app.get('/.well-known/oauth-protected-resource', protectedResourceMetadata(auth))\n }\n\n let oauthPaths = new Set<string>()\n if (auth?.provider) {\n const oauth = oauthRoutes(auth)\n app.get('/.well-known/oauth-authorization-server', oauth.wellKnownAuthServer)\n app.get('/authorize', oauth.authorize)\n app.get(oauth.callbackPath, oauth.callback)\n app.post('/token', ...oauth.token)\n app.post('/register', ...oauth.register)\n oauthPaths = new Set(['/.well-known/oauth-authorization-server', '/authorize', oauth.callbackPath, '/token', '/register'])\n }\n\n if (auth) {\n const guard = authMiddleware(auth, context)\n app.use((req, res, next) => {\n if (req.path.startsWith('/.well-known/') || oauthPaths.has(req.path)) { return next() }\n return guard(req, res, next)\n })\n }\n\n if (sideloadResources) {\n app.get('/resource/:id', sideloadResource({ resourceDir }))\n }\n\n const transport = mcpTransport(silkweaveOptions, context, actions, { filterActions, onToolCall })\n app.post('/mcp', express.json(), transport.post)\n\n return app\n}\n\n/**\n * Spin up a standalone MCP Streamable HTTP server on `host:port` for the\n * given `actions`. Returns the underlying `Server` so callers can close it.\n *\n * Convenience for use cases that don't go through the `silkweave()` builder.\n */\nexport async function startMcpServer(\n silkweaveOptions: SilkweaveOptions,\n actions: Action[],\n options: StartMcpHttpOptions,\n context?: SilkweaveContext\n): Promise<Server> {\n const ctx = context ?? createContext({ adapter: 'http' })\n const app = buildMcpExpressApp(silkweaveOptions, ctx, actions, options)\n return new Promise<Server>((resolve, reject) => {\n const server = app.listen(options.port, options.host, (error) => {\n if (error) { reject(error); return }\n console.log(`MCP Streamable HTTP Server listening on http://${options.host}:${options.port}/mcp`)\n resolve(server)\n })\n })\n}\n\n/**\n * Silkweave adapter that owns its own HTTP server. Composes the MCP handler\n * primitives into a fully-wired Express app and listens on `host:port`.\n */\nexport const http: AdapterFactory<StartMcpHttpOptions> = (options) => {\n return (silkweaveOptions, baseContext) => {\n const context = baseContext.fork({ adapter: 'http' })\n let httpServer: Server | undefined\n return {\n context,\n start: async (actions) => {\n const app = buildMcpExpressApp(silkweaveOptions, context, actions, options)\n httpServer = await new Promise<Server>((resolve, reject) => {\n const s = app.listen(options.port, options.host, (error) => {\n if (error) { reject(error); return }\n console.log(`MCP Streamable HTTP Server listening on http://${options.host}:${options.port}/mcp`)\n resolve(s)\n })\n })\n },\n stop: async () => {\n if (!httpServer) { return }\n await new Promise<void>((resolve, reject) => {\n httpServer!.close((err) => err ? reject(err) : resolve())\n })\n httpServer = undefined\n }\n }\n }\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { AdapterFactory, OnToolCall, validateActionDisposition } from '@silkweave/core'\nimport { registerTools } from '../handlers/registerTools.js'\n\nexport interface StdioAdapterOptions {\n /** Telemetry hook invoked once per tool call (fire-and-forget). */\n onToolCall?: OnToolCall\n}\n\nexport const stdio: AdapterFactory<StdioAdapterOptions | void> = (adapterOptions) => {\n return (options, baseContext) => {\n const context = baseContext.fork({ adapter: 'stdio' })\n const server = new McpServer({\n name: options.name,\n description: options.description,\n version: options.version\n }, {\n capabilities: { tools: {}, logging: {} }\n })\n return {\n context,\n start: async (actions) => {\n actions.forEach(validateActionDisposition)\n registerTools(server, actions, context, { logStream: false, onToolCall: adapterOptions?.onToolCall })\n const transport = new StdioServerTransport()\n await server.connect(transport)\n },\n stop: async () => {\n await server?.close()\n }\n }\n }\n}\n","import { randomUUID } from 'crypto'\nimport { writeFile } from 'fs/promises'\n\nexport interface SideloadResource {\n id: string\n name: string\n contentType: string\n size: number\n}\n\nexport async function createSideloadResource(buffer: Buffer, { name, contentType }: Pick<SideloadResource, 'name' | 'contentType'>) {\n const id = randomUUID()\n const resource: SideloadResource = { id, name, contentType, size: buffer.length }\n await Promise.all([\n writeFile(`resources/${id}`, buffer),\n writeFile(`resources/${id}.json`, JSON.stringify(resource), 'utf-8')\n ])\n return resource\n}\n"],"mappings":"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"}
@@ -161,12 +161,13 @@ function structuredResult(action, result) {
161
161
  const fields = [...new Set(parsed.error.issues.map((issue) => issue.path.join(".") || "(root)"))].join(", ");
162
162
  return errorToolResult(new SilkweaveError(`Output validation failed for '${action.name}' at: ${fields}. The tool returned a shape that does not match its declared output schema - this is a server-side bug, not an input problem; retrying with different arguments will not help.`, "output_validation_error"));
163
163
  }
164
+ if (!action.output.safeParse(parsed.data).success) return errorToolResult(new SilkweaveError(`Output schema for '${action.name}' is not idempotent (a field-level .transform()?) and cannot back a 'structured' contract - use disposition 'json' or remove the transform.`, "output_schema_not_structurable"));
164
165
  return structuredToolResult(parsed.data);
165
166
  }
166
167
  /** MCP-only telemetry fields, computed only when a hook is registered. */
167
168
  function resultMeta(result, formatted) {
168
169
  return {
169
- resultBytes: JSON.stringify(result).length,
170
+ resultBytes: new TextEncoder().encode(JSON.stringify(result)).length,
170
171
  sideloaded: (formatted.content ?? []).some((block) => block.type === "resource")
171
172
  };
172
173
  }
@@ -253,4 +254,4 @@ function registerTools(server, actions, context, options = {}) {
253
254
  //#endregion
254
255
  export { authMiddleware as a, rpcInfo as i, requestFromExtra as n, authStorage as o, filterErrorResponse as r, registerTools as t };
255
256
 
256
- //# sourceMappingURL=registerTools-DkaraOyx.mjs.map
257
+ //# sourceMappingURL=registerTools-B_vjxOrs.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registerTools-B_vjxOrs.mjs","names":[],"sources":["../src/handlers/auth.ts","../src/handlers/filter.ts","../src/handlers/registerTools.ts"],"sourcesContent":["import { AuthConfig, AuthInfo, validateToken } from '@silkweave/auth'\nimport { SilkweaveContext } from '@silkweave/core'\nimport { type RequestHandler } from 'express'\nimport { AsyncLocalStorage } from 'node:async_hooks'\n\n/**\n * Per-request bearer-token storage used by tool handlers to read the resolved\n * `AuthInfo` for the currently-handled MCP call.\n */\nexport const authStorage = new AsyncLocalStorage<AuthInfo>()\n\n/**\n * Express middleware that validates the `Authorization: Bearer …` header via\n * the supplied `AuthConfig`. On success the resolved `AuthInfo` is placed in\n * `authStorage` for the duration of the downstream handler - `mcpTransport`'s\n * tool callbacks pick it up to populate the silkweave context's `auth` key.\n *\n * The middleware should NOT be applied to OAuth-discovery / token routes -\n * compose it only on the routes that require an authenticated caller (the MCP\n * transport itself, sideload, etc.).\n */\nexport function authMiddleware(auth: AuthConfig, context: SilkweaveContext): RequestHandler {\n return async (req, res, next) => {\n const result = await validateToken(req.headers.authorization, auth, context.fork({ request: req }))\n if (result.error) {\n for (const [key, value] of Object.entries(result.error.headers)) { res.header(key, value) }\n res.status(result.error.statusCode).json(result.error.body)\n return\n }\n if (result.auth) {\n authStorage.run(result.auth, () => { next() })\n } else {\n next()\n }\n }\n}\n","import { Action, SilkweaveError } from '@silkweave/core'\n\n/**\n * Normalized request stand-in handed to `filterActions` - the same shape for\n * the Express `http()` transport and the Web-Standard `edge()` adapter.\n */\nexport interface FilterRequest {\n /** Inbound HTTP headers (lower-cased keys, as delivered by the host). */\n headers: Record<string, string | string[] | undefined>\n /** Full request URL (or path, as delivered by the host). */\n url: string\n /**\n * JSON-RPC method of the POSTed message (`'initialize'`, `'tools/list'`,\n * `'tools/call'`, `'ping'`, ...). Lets the callback skip expensive\n * permission lookups on `initialize`/`ping`, and doubles as an\n * observability tap (e.g. counting `tools/list`). Empty string when the body\n * is not a recognizable JSON-RPC message. JSON-RPC batches are rejected by the\n * transport before the filter runs, so this always reflects a single message.\n */\n method: string\n /** `params.name` of a `tools/call` message; unset for every other method. */\n toolName?: string\n}\n\n/**\n * Per-request tool filter for the stateless MCP transports. Runs before\n * `registerTools()` on every `POST /mcp`; only the returned actions exist for\n * that request (`tools/list` and `tools/call` alike - a client that cached a\n * wider list is still denied). May be async (e.g. a DB lookup of API-key\n * permissions).\n *\n * Error semantics: a thrown `SilkweaveError` propagates as its `statusCode`\n * (e.g. 401 invalid key, 403 insufficient permissions) with a JSON-RPC error\n * body; any other throw maps to HTTP 500. A thrown error NEVER degrades to an\n * empty tool list - return `[]` explicitly if \"no tools\" is the intended\n * answer.\n */\nexport type FilterActions = (actions: Action[], request: FilterRequest) => Action[] | Promise<Action[]>\n\n/**\n * Extract the JSON-RPC `method` (and `toolName` for `tools/call`) from a\n * parsed request body. Tolerates a legacy batch (first request wins) and\n * unrecognizable bodies (empty `method`).\n */\nexport function rpcInfo(body: unknown): { method: string; toolName?: string } {\n const message = (Array.isArray(body) ? body[0] : body) as { method?: unknown; params?: { name?: unknown } } | undefined\n const method = typeof message?.method === 'string' ? message.method : ''\n const toolName = method === 'tools/call' && typeof message?.params?.name === 'string' ? message.params.name : undefined\n return { method, ...(toolName !== undefined ? { toolName } : {}) }\n}\n\n/**\n * Map a `filterActions` throw to an HTTP status + JSON-RPC error body: a\n * `SilkweaveError` keeps its `statusCode` (so an invalid key surfaces as an\n * auth failure, not \"server has no tools\"), anything else is a 500. `id` is\n * echoed from the request body when available.\n */\nexport function filterErrorResponse(error: unknown, body: unknown): { status: number; body: object } {\n const message = (Array.isArray(body) ? body[0] : body) as { id?: unknown } | undefined\n const id = message?.id ?? null\n if (error instanceof SilkweaveError) {\n return {\n status: error.statusCode,\n body: { jsonrpc: '2.0', error: { code: -32_000, message: error.message }, id }\n }\n }\n console.error('filterActions error:', error)\n return {\n status: 500,\n body: { jsonrpc: '2.0', error: { code: -32_603, message: 'Internal server error' }, id }\n }\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { RequestHandlerExtra } from '@modelcontextprotocol/sdk/shared/protocol.js'\nimport type { ServerNotification, ServerRequest } from '@modelcontextprotocol/sdk/types.js'\nimport { Action, ActionRun, createLogger, emitToolCall, isStreamingAction, OnToolCall, runStreamingAction, SilkweaveContext, SilkweaveError, ToolCallEvent } from '@silkweave/core'\nimport { capitalCase, pascalCase } from 'change-case'\nimport { errorToolResult, handleToolError, jsonToolResult, smartToolResult, structuredToolResult } from '../util/result.js'\nimport { authStorage } from './auth.js'\n\ntype LogStream = NonNullable<Parameters<typeof createLogger>[0]>['stream']\ntype ToolExtra = RequestHandlerExtra<ServerRequest, ServerNotification>\n\nexport interface RegisterToolsOptions {\n /**\n * Where the per-call logger writes its human-readable stream. The `stdio`\n * adapter MUST pass `false` (stdout is the MCP protocol channel); the HTTP\n * transports default to `process.stderr`.\n */\n logStream?: LogStream\n /**\n * Telemetry hook invoked once per tool call (fire-and-forget; errors are\n * logged, never propagated). Fires after result formatting, so events carry\n * `resultBytes` (serialized raw-result size) and `sideloaded` (whether\n * `smartToolResult` offloaded to an embedded resource).\n */\n onToolCall?: OnToolCall\n}\n\n/**\n * Build the generic `request` context value (the same key REST/tRPC populate)\n * from the MCP SDK's `extra.requestInfo`, so transport-agnostic consumers - e.g.\n * `@silkweave/nestjs` `@UseGuards` guards reading\n * `switchToHttp().getRequest().headers` - work over MCP. There are no path\n * `params`/`query`/`body` on the raw MCP call, so those start as empty\n * stand-ins; `@silkweave/nestjs` later fills them from the validated tool input\n * per the reflected param sources (path -> `params`, `@Query` -> `query`,\n * `@Body` -> `body`) so guards reading any of them decide as they would over\n * REST. Returns `undefined` when no HTTP request info is available.\n */\nexport function requestFromExtra(requestInfo: { headers?: unknown; url?: { toString(): string } } | undefined) {\n if (!requestInfo) { return undefined }\n return { headers: requestInfo.headers ?? {}, url: requestInfo.url?.toString(), params: {}, query: {}, body: {} }\n}\n\n/** Per-call logger that bridges silkweave logs/progress onto MCP notifications. */\nfunction createToolLogger(extra: ToolExtra, stream: LogStream) {\n return createLogger({\n stream,\n onLog: (level, data) => {\n extra.sendNotification({ method: 'notifications/message', params: { level, data } })\n },\n onProgress: ({ progress, total, message }) => {\n if (!extra._meta?.progressToken) { return }\n extra.sendNotification({\n method: 'notifications/progress',\n params: { progress, total, message, progressToken: extra._meta.progressToken }\n })\n }\n })\n}\n\n/** Run the action, streaming chunks as progress notifications when a token is set. */\nasync function runAction(action: Action, input: object, context: SilkweaveContext, extra: ToolExtra): Promise<object | object[]> {\n const progressToken = extra._meta?.progressToken\n if (isStreamingAction(action)) {\n return runStreamingAction(action, input, context, progressToken\n ? async (chunk, index) => {\n await extra.sendNotification({\n method: 'notifications/progress',\n params: { progressToken, progress: index + 1, message: JSON.stringify(chunk) }\n })\n }\n : undefined)\n }\n return (action.run as ActionRun<object, object>)(input, context)\n}\n\n/**\n * Result path for a `disposition: 'structured'` action: parse the raw result\n * through the output schema and ship the PARSED data as `structuredContent`.\n * Parsing strips extra fields (so the client-side JSON-Schema validator, which\n * rejects `additionalProperties`, passes by construction) and a genuine\n * mismatch (missing required field, wrong type) degrades to an `isError` tool\n * result - which the SDK exempts from output validation - instead of an opaque\n * protocol error.\n */\nfunction structuredResult(action: Action, result: object | object[]) {\n const parsed = action.output!.safeParse(result)\n if (!parsed.success) {\n const fields = [...new Set(parsed.error.issues.map((issue) => issue.path.join('.') || '(root)'))].join(', ')\n return errorToolResult(new SilkweaveError(\n `Output validation failed for '${action.name}' at: ${fields}. The tool returned a shape that does not match its declared output schema - this is a server-side bug, not an input problem; retrying with different arguments will not help.`,\n 'output_validation_error'\n ))\n }\n // The SDK independently re-parses `structuredContent` against the same schema.\n // A non-idempotent output schema (a field-level `.transform()`) yields data\n // that fails that second parse, which the SDK raises as an opaque protocol\n // error. Detect it here and degrade to a clear isError result (SDK-exempt)\n // instead - structured contracts must be idempotent (no transforms).\n if (!action.output!.safeParse(parsed.data).success) {\n return errorToolResult(new SilkweaveError(\n `Output schema for '${action.name}' is not idempotent (a field-level .transform()?) and cannot back a 'structured' contract - use disposition 'json' or remove the transform.`,\n 'output_schema_not_structurable'\n ))\n }\n return structuredToolResult(parsed.data as object)\n}\n\n/** MCP-only telemetry fields, computed only when a hook is registered. */\nfunction resultMeta(result: object | object[], formatted: { content?: { type: string }[] }): Pick<ToolCallEvent, 'resultBytes' | 'sideloaded'> {\n return {\n // Actual UTF-8 byte count (String.length counts UTF-16 code units, which\n // understates multibyte payloads). TextEncoder keeps this edge-safe.\n resultBytes: new TextEncoder().encode(JSON.stringify(result)).length,\n sideloaded: (formatted.content ?? []).some((block) => block.type === 'resource')\n }\n}\n\n/** Error identity for telemetry: a SilkweaveError's `code`, else the error's name. */\nfunction errorMeta(error: unknown): Pick<ToolCallEvent, 'errorCode' | 'errorMessage'> {\n if (error instanceof SilkweaveError) { return { errorCode: error.code, errorMessage: error.message } }\n if (error instanceof Error) { return { errorCode: error.name, errorMessage: error.message } }\n return { errorCode: 'unknown' }\n}\n\n/** Format via the action's `toolResult` hook, else the resolved disposition. */\nfunction formatToolResult(action: Action, result: object | object[], context: SilkweaveContext, disposition: unknown) {\n if (action.toolResult) {\n const response = action.toolResult(result, context)\n if (response) { return response }\n }\n // A structured action's output schema is a contract fixed at tools/list\n // time - a client's `_meta.disposition` cannot demote it.\n if (action.disposition === 'structured') { return structuredResult(action, result) }\n return disposition === 'smart' ? smartToolResult(result) : jsonToolResult(result)\n}\n\n/**\n * Register every action as an MCP tool on `server`. Shared by all MCP transports\n * (`stdio`, `http`, `edge`). Each tool call forks `context` with a per-call\n * `logger`, the SDK `extra`, a synthesized `request` (see `requestFromExtra`),\n * and - when bearer auth ran for this request - the resolved `auth`. The result\n * is formatted by the action's `toolResult` hook if present, otherwise per the\n * resolved disposition (client `_meta.disposition` > `action.disposition` >\n * `json`); a `'structured'` action always ships schema-parsed\n * `structuredContent` (its contract cannot be demoted per-call).\n */\nexport function registerTools(\n server: McpServer,\n actions: Action[],\n context: SilkweaveContext,\n options: RegisterToolsOptions = {}\n) {\n const stream = options.logStream ?? process.stderr\n for (const action of actions) {\n server.registerTool(pascalCase(action.name), {\n title: capitalCase(action.name),\n description: action.description,\n inputSchema: action.input,\n // Derived base (query ⇒ read-only), explicit annotations merged over.\n annotations: { readOnlyHint: action.kind === 'query', ...action.annotations },\n // Only structured actions declare an outputSchema contract - the SDK\n // enforces it server-side and SDK clients enforce it independently, so\n // forwarding is strictly opt-in via disposition.\n ...(action.disposition === 'structured' && action.output ? { outputSchema: action.output } : {})\n }, async (input, extra) => {\n const logger = createToolLogger(extra, stream)\n const currentAuth = authStorage.getStore()\n const actionContext = context.fork({\n logger,\n extra,\n request: requestFromExtra(extra.requestInfo),\n ...(currentAuth ? { auth: currentAuth } : {})\n })\n // Client-sent `_meta.disposition` wins; otherwise fall back to the\n // action's configured default (`json` when neither is set).\n const disposition = extra._meta?.disposition ?? action.disposition\n const base = { action: action.name, tool: pascalCase(action.name), transport: 'mcp' as const, context: actionContext }\n const started = Date.now()\n try {\n const result = await runAction(action, input, actionContext, extra)\n const formatted = formatToolResult(action, result, actionContext, disposition)\n emitToolCall(options.onToolCall, {\n ...base,\n durationMs: Date.now() - started,\n ok: formatted.isError !== true,\n ...(options.onToolCall ? resultMeta(result, formatted) : {})\n })\n return formatted\n } catch (error) {\n emitToolCall(options.onToolCall, { ...base, durationMs: Date.now() - started, ok: false, ...errorMeta(error) })\n return handleToolError(error)\n }\n })\n }\n}\n"],"mappings":"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"}
@@ -16,9 +16,9 @@ interface FilterRequest {
16
16
  * JSON-RPC method of the POSTed message (`'initialize'`, `'tools/list'`,
17
17
  * `'tools/call'`, `'ping'`, ...). Lets the callback skip expensive
18
18
  * permission lookups on `initialize`/`ping`, and doubles as an
19
- * observability tap (e.g. counting `tools/list`). For a legacy JSON-RPC
20
- * batch this is the first request's method. Empty string when the body is
21
- * not a recognizable JSON-RPC message (the transport will reject it anyway).
19
+ * observability tap (e.g. counting `tools/list`). Empty string when the body
20
+ * is not a recognizable JSON-RPC message. JSON-RPC batches are rejected by the
21
+ * transport before the filter runs, so this always reflects a single message.
22
22
  */
23
23
  method: string;
24
24
  /** `params.name` of a `tools/call` message; unset for every other method. */
@@ -133,4 +133,4 @@ declare function parseResourceMessage({
133
133
  }: EmbeddedResource): string;
134
134
  //#endregion
135
135
  export { smartToolResult as a, registerTools as c, FilterRequest as d, filterErrorResponse as f, parseResourceMessage as i, requestFromExtra as l, handleToolError as n, structuredToolResult as o, rpcInfo as p, jsonToolResult as r, RegisterToolsOptions as s, errorToolResult as t, FilterActions as u };
136
- //# sourceMappingURL=result-BN0lxM6Q.d.mts.map
136
+ //# sourceMappingURL=result-I04HIJR5.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"result-BN0lxM6Q.d.mts","names":[],"sources":["../src/handlers/filter.ts","../src/handlers/registerTools.ts","../src/util/result.ts"],"mappings":";;;;;;;;;UAMiB,aAAA;EAAa;EAE5B,OAAA,EAAS,MAAA;EAAM;EAEf,GAAA;EAFS;;;;;;AA6BX;;EAlBE,MAAA;EAkBoC;EAhBpC,QAAA;AAAA;;;;;;;;;;;;;;KAgBU,aAAA,IAAiB,OAAA,EAAS,MAAA,IAAU,OAAA,EAAS,aAAA,KAAkB,MAAA,KAAW,OAAA,CAAQ,MAAA;;;;;;iBAO9E,OAAA,CAAQ,IAAA;EAAkB,MAAA;EAAgB,QAAA;AAAA;;;;;;;iBAa1C,mBAAA,CAAoB,KAAA,WAAgB,IAAA;EAAkB,MAAA;EAAgB,IAAA;AAAA;;;KCjDjF,SAAA,GAAY,WAAA,CAAY,UAAA,QAAkB,YAAA;AAAA,UAG9B,oBAAA;;ADLjB;;;;ECWE,SAAA,GAAY,SAAA;EDTH;;;;;;ECgBT,UAAA,GAAa,UAAA;AAAA;;;;;;;;;;;;iBAcC,gBAAA,CAAiB,WAAA;EAAe,OAAA;EAAmB,GAAA;IAAQ,QAAA;EAAA;AAAA;;;;;;;;;;ADmB3E;;;;;;;iBC6EgB,aAAA,CACd,MAAA,EAAQ,SAAA,EACR,OAAA,EAAS,MAAA,IACT,OAAA,EAAS,gBAAA,EACT,OAAA,GAAS,oBAAA;;;iBCtIK,eAAA,CAAgB,IAAA,+BAAmC,cAAA;;;AFEnE;;;;;;iBE2BgB,oBAAA,CAAqB,IAAA,WAAe,cAAA;AAAA,iBAOpC,cAAA,CAAe,IAAA,UAAc,OAAA,aAAkB,cAAA;AAAA,iBAM/C,eAAA,CAAA;EAAkB,IAAA;EAAM,IAAA;EAAM;AAAA,GAAW,cAAA,GAAiB,cAAA;AAAA,iBAU1D,eAAA,CAAgB,KAAA,YAAiB,cAAA;AAAA,iBAejC,oBAAA,CAAA;EAAuB;AAAA,GAAY,gBAAA"}
1
+ {"version":3,"file":"result-I04HIJR5.d.mts","names":[],"sources":["../src/handlers/filter.ts","../src/handlers/registerTools.ts","../src/util/result.ts"],"mappings":";;;;;;;;;UAMiB,aAAA;EAAa;EAE5B,OAAA,EAAS,MAAA;EAAM;EAEf,GAAA;EAFS;;;;;;AA6BX;;EAlBE,MAAA;EAkBoC;EAhBpC,QAAA;AAAA;;;;;;;;;;;;;;KAgBU,aAAA,IAAiB,OAAA,EAAS,MAAA,IAAU,OAAA,EAAS,aAAA,KAAkB,MAAA,KAAW,OAAA,CAAQ,MAAA;;;;;;iBAO9E,OAAA,CAAQ,IAAA;EAAkB,MAAA;EAAgB,QAAA;AAAA;;;;;;;iBAa1C,mBAAA,CAAoB,KAAA,WAAgB,IAAA;EAAkB,MAAA;EAAgB,IAAA;AAAA;;;KCjDjF,SAAA,GAAY,WAAA,CAAY,UAAA,QAAkB,YAAA;AAAA,UAG9B,oBAAA;;ADLjB;;;;ECWE,SAAA,GAAY,SAAA;EDTH;;;;;;ECgBT,UAAA,GAAa,UAAA;AAAA;;;;;;;;;;;;iBAcC,gBAAA,CAAiB,WAAA;EAAe,OAAA;EAAmB,GAAA;IAAQ,QAAA;EAAA;AAAA;;;;;;;;;;ADmB3E;;;;;;;iBC0FgB,aAAA,CACd,MAAA,EAAQ,SAAA,EACR,OAAA,EAAS,MAAA,IACT,OAAA,EAAS,gBAAA,EACT,OAAA,GAAS,oBAAA;;;iBCnJK,eAAA,CAAgB,IAAA,+BAAmC,cAAA;;;AFEnE;;;;;;iBE2BgB,oBAAA,CAAqB,IAAA,WAAe,cAAA;AAAA,iBAOpC,cAAA,CAAe,IAAA,UAAc,OAAA,aAAkB,cAAA;AAAA,iBAM/C,eAAA,CAAA;EAAkB,IAAA;EAAM,IAAA;EAAM;AAAA,GAAW,cAAA,GAAiB,cAAA;AAAA,iBAU1D,eAAA,CAAgB,KAAA,YAAiB,cAAA;AAAA,iBAejC,oBAAA,CAAA;EAAuB;AAAA,GAAY,gBAAA"}
package/build/tools.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { a as smartToolResult, c as registerTools, d as FilterRequest, f as filterErrorResponse, i as parseResourceMessage, l as requestFromExtra, n as handleToolError, o as structuredToolResult, p as rpcInfo, r as jsonToolResult, s as RegisterToolsOptions, t as errorToolResult, u as FilterActions } from "./result-BN0lxM6Q.mjs";
1
+ import { a as smartToolResult, c as registerTools, d as FilterRequest, f as filterErrorResponse, i as parseResourceMessage, l as requestFromExtra, n as handleToolError, o as structuredToolResult, p as rpcInfo, r as jsonToolResult, s as RegisterToolsOptions, t as errorToolResult, u as FilterActions } from "./result-I04HIJR5.mjs";
2
2
  export { FilterActions, FilterRequest, RegisterToolsOptions, errorToolResult, filterErrorResponse, handleToolError, jsonToolResult, parseResourceMessage, registerTools, requestFromExtra, rpcInfo, smartToolResult, structuredToolResult };
package/build/tools.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { i as rpcInfo, n as requestFromExtra, r as filterErrorResponse, t as registerTools } from "./registerTools-DkaraOyx.mjs";
1
+ import { i as rpcInfo, n as requestFromExtra, r as filterErrorResponse, t as registerTools } from "./registerTools-B_vjxOrs.mjs";
2
2
  import { a as smartToolResult, i as parseResourceMessage, n as handleToolError, o as structuredToolResult, r as jsonToolResult, t as errorToolResult } from "./result-CXgeE8ca.mjs";
3
3
  export { errorToolResult, filterErrorResponse, handleToolError, jsonToolResult, parseResourceMessage, registerTools, requestFromExtra, rpcInfo, smartToolResult, structuredToolResult };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@silkweave/mcp",
3
- "version": "3.2.0",
3
+ "version": "3.2.1",
4
4
  "description": "Silkweave MCP Package",
5
5
  "license": "MIT",
6
6
  "homepage": "https://www.silkweave.dev",
@@ -39,8 +39,8 @@
39
39
  "@modelcontextprotocol/sdk": "^1.29.0",
40
40
  "change-case": "^5.4.4",
41
41
  "zod": "^3.25.0",
42
- "@silkweave/auth": "3.2.0",
43
- "@silkweave/core": "3.2.0"
42
+ "@silkweave/auth": "3.2.1",
43
+ "@silkweave/core": "3.2.1"
44
44
  },
45
45
  "peerDependencies": {
46
46
  "commander": "^13.1.0",
@@ -1 +0,0 @@
1
- {"version":3,"file":"registerTools-DkaraOyx.mjs","names":[],"sources":["../src/handlers/auth.ts","../src/handlers/filter.ts","../src/handlers/registerTools.ts"],"sourcesContent":["import { AuthConfig, AuthInfo, validateToken } from '@silkweave/auth'\nimport { SilkweaveContext } from '@silkweave/core'\nimport { type RequestHandler } from 'express'\nimport { AsyncLocalStorage } from 'node:async_hooks'\n\n/**\n * Per-request bearer-token storage used by tool handlers to read the resolved\n * `AuthInfo` for the currently-handled MCP call.\n */\nexport const authStorage = new AsyncLocalStorage<AuthInfo>()\n\n/**\n * Express middleware that validates the `Authorization: Bearer …` header via\n * the supplied `AuthConfig`. On success the resolved `AuthInfo` is placed in\n * `authStorage` for the duration of the downstream handler - `mcpTransport`'s\n * tool callbacks pick it up to populate the silkweave context's `auth` key.\n *\n * The middleware should NOT be applied to OAuth-discovery / token routes -\n * compose it only on the routes that require an authenticated caller (the MCP\n * transport itself, sideload, etc.).\n */\nexport function authMiddleware(auth: AuthConfig, context: SilkweaveContext): RequestHandler {\n return async (req, res, next) => {\n const result = await validateToken(req.headers.authorization, auth, context.fork({ request: req }))\n if (result.error) {\n for (const [key, value] of Object.entries(result.error.headers)) { res.header(key, value) }\n res.status(result.error.statusCode).json(result.error.body)\n return\n }\n if (result.auth) {\n authStorage.run(result.auth, () => { next() })\n } else {\n next()\n }\n }\n}\n","import { Action, SilkweaveError } from '@silkweave/core'\n\n/**\n * Normalized request stand-in handed to `filterActions` - the same shape for\n * the Express `http()` transport and the Web-Standard `edge()` adapter.\n */\nexport interface FilterRequest {\n /** Inbound HTTP headers (lower-cased keys, as delivered by the host). */\n headers: Record<string, string | string[] | undefined>\n /** Full request URL (or path, as delivered by the host). */\n url: string\n /**\n * JSON-RPC method of the POSTed message (`'initialize'`, `'tools/list'`,\n * `'tools/call'`, `'ping'`, ...). Lets the callback skip expensive\n * permission lookups on `initialize`/`ping`, and doubles as an\n * observability tap (e.g. counting `tools/list`). For a legacy JSON-RPC\n * batch this is the first request's method. Empty string when the body is\n * not a recognizable JSON-RPC message (the transport will reject it anyway).\n */\n method: string\n /** `params.name` of a `tools/call` message; unset for every other method. */\n toolName?: string\n}\n\n/**\n * Per-request tool filter for the stateless MCP transports. Runs before\n * `registerTools()` on every `POST /mcp`; only the returned actions exist for\n * that request (`tools/list` and `tools/call` alike - a client that cached a\n * wider list is still denied). May be async (e.g. a DB lookup of API-key\n * permissions).\n *\n * Error semantics: a thrown `SilkweaveError` propagates as its `statusCode`\n * (e.g. 401 invalid key, 403 insufficient permissions) with a JSON-RPC error\n * body; any other throw maps to HTTP 500. A thrown error NEVER degrades to an\n * empty tool list - return `[]` explicitly if \"no tools\" is the intended\n * answer.\n */\nexport type FilterActions = (actions: Action[], request: FilterRequest) => Action[] | Promise<Action[]>\n\n/**\n * Extract the JSON-RPC `method` (and `toolName` for `tools/call`) from a\n * parsed request body. Tolerates a legacy batch (first request wins) and\n * unrecognizable bodies (empty `method`).\n */\nexport function rpcInfo(body: unknown): { method: string; toolName?: string } {\n const message = (Array.isArray(body) ? body[0] : body) as { method?: unknown; params?: { name?: unknown } } | undefined\n const method = typeof message?.method === 'string' ? message.method : ''\n const toolName = method === 'tools/call' && typeof message?.params?.name === 'string' ? message.params.name : undefined\n return { method, ...(toolName !== undefined ? { toolName } : {}) }\n}\n\n/**\n * Map a `filterActions` throw to an HTTP status + JSON-RPC error body: a\n * `SilkweaveError` keeps its `statusCode` (so an invalid key surfaces as an\n * auth failure, not \"server has no tools\"), anything else is a 500. `id` is\n * echoed from the request body when available.\n */\nexport function filterErrorResponse(error: unknown, body: unknown): { status: number; body: object } {\n const message = (Array.isArray(body) ? body[0] : body) as { id?: unknown } | undefined\n const id = message?.id ?? null\n if (error instanceof SilkweaveError) {\n return {\n status: error.statusCode,\n body: { jsonrpc: '2.0', error: { code: -32_000, message: error.message }, id }\n }\n }\n console.error('filterActions error:', error)\n return {\n status: 500,\n body: { jsonrpc: '2.0', error: { code: -32_603, message: 'Internal server error' }, id }\n }\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { RequestHandlerExtra } from '@modelcontextprotocol/sdk/shared/protocol.js'\nimport type { ServerNotification, ServerRequest } from '@modelcontextprotocol/sdk/types.js'\nimport { Action, ActionRun, createLogger, emitToolCall, isStreamingAction, OnToolCall, runStreamingAction, SilkweaveContext, SilkweaveError, ToolCallEvent } from '@silkweave/core'\nimport { capitalCase, pascalCase } from 'change-case'\nimport { errorToolResult, handleToolError, jsonToolResult, smartToolResult, structuredToolResult } from '../util/result.js'\nimport { authStorage } from './auth.js'\n\ntype LogStream = NonNullable<Parameters<typeof createLogger>[0]>['stream']\ntype ToolExtra = RequestHandlerExtra<ServerRequest, ServerNotification>\n\nexport interface RegisterToolsOptions {\n /**\n * Where the per-call logger writes its human-readable stream. The `stdio`\n * adapter MUST pass `false` (stdout is the MCP protocol channel); the HTTP\n * transports default to `process.stderr`.\n */\n logStream?: LogStream\n /**\n * Telemetry hook invoked once per tool call (fire-and-forget; errors are\n * logged, never propagated). Fires after result formatting, so events carry\n * `resultBytes` (serialized raw-result size) and `sideloaded` (whether\n * `smartToolResult` offloaded to an embedded resource).\n */\n onToolCall?: OnToolCall\n}\n\n/**\n * Build the generic `request` context value (the same key REST/tRPC populate)\n * from the MCP SDK's `extra.requestInfo`, so transport-agnostic consumers - e.g.\n * `@silkweave/nestjs` `@UseGuards` guards reading\n * `switchToHttp().getRequest().headers` - work over MCP. There are no path\n * `params`/`query`/`body` on the raw MCP call, so those start as empty\n * stand-ins; `@silkweave/nestjs` later fills them from the validated tool input\n * per the reflected param sources (path -> `params`, `@Query` -> `query`,\n * `@Body` -> `body`) so guards reading any of them decide as they would over\n * REST. Returns `undefined` when no HTTP request info is available.\n */\nexport function requestFromExtra(requestInfo: { headers?: unknown; url?: { toString(): string } } | undefined) {\n if (!requestInfo) { return undefined }\n return { headers: requestInfo.headers ?? {}, url: requestInfo.url?.toString(), params: {}, query: {}, body: {} }\n}\n\n/** Per-call logger that bridges silkweave logs/progress onto MCP notifications. */\nfunction createToolLogger(extra: ToolExtra, stream: LogStream) {\n return createLogger({\n stream,\n onLog: (level, data) => {\n extra.sendNotification({ method: 'notifications/message', params: { level, data } })\n },\n onProgress: ({ progress, total, message }) => {\n if (!extra._meta?.progressToken) { return }\n extra.sendNotification({\n method: 'notifications/progress',\n params: { progress, total, message, progressToken: extra._meta.progressToken }\n })\n }\n })\n}\n\n/** Run the action, streaming chunks as progress notifications when a token is set. */\nasync function runAction(action: Action, input: object, context: SilkweaveContext, extra: ToolExtra): Promise<object | object[]> {\n const progressToken = extra._meta?.progressToken\n if (isStreamingAction(action)) {\n return runStreamingAction(action, input, context, progressToken\n ? async (chunk, index) => {\n await extra.sendNotification({\n method: 'notifications/progress',\n params: { progressToken, progress: index + 1, message: JSON.stringify(chunk) }\n })\n }\n : undefined)\n }\n return (action.run as ActionRun<object, object>)(input, context)\n}\n\n/**\n * Result path for a `disposition: 'structured'` action: parse the raw result\n * through the output schema and ship the PARSED data as `structuredContent`.\n * Parsing strips extra fields (so the client-side JSON-Schema validator, which\n * rejects `additionalProperties`, passes by construction) and a genuine\n * mismatch (missing required field, wrong type) degrades to an `isError` tool\n * result - which the SDK exempts from output validation - instead of an opaque\n * protocol error.\n */\nfunction structuredResult(action: Action, result: object | object[]) {\n const parsed = action.output!.safeParse(result)\n if (!parsed.success) {\n const fields = [...new Set(parsed.error.issues.map((issue) => issue.path.join('.') || '(root)'))].join(', ')\n return errorToolResult(new SilkweaveError(\n `Output validation failed for '${action.name}' at: ${fields}. The tool returned a shape that does not match its declared output schema - this is a server-side bug, not an input problem; retrying with different arguments will not help.`,\n 'output_validation_error'\n ))\n }\n return structuredToolResult(parsed.data as object)\n}\n\n/** MCP-only telemetry fields, computed only when a hook is registered. */\nfunction resultMeta(result: object | object[], formatted: { content?: { type: string }[] }): Pick<ToolCallEvent, 'resultBytes' | 'sideloaded'> {\n return {\n resultBytes: JSON.stringify(result).length,\n sideloaded: (formatted.content ?? []).some((block) => block.type === 'resource')\n }\n}\n\n/** Error identity for telemetry: a SilkweaveError's `code`, else the error's name. */\nfunction errorMeta(error: unknown): Pick<ToolCallEvent, 'errorCode' | 'errorMessage'> {\n if (error instanceof SilkweaveError) { return { errorCode: error.code, errorMessage: error.message } }\n if (error instanceof Error) { return { errorCode: error.name, errorMessage: error.message } }\n return { errorCode: 'unknown' }\n}\n\n/** Format via the action's `toolResult` hook, else the resolved disposition. */\nfunction formatToolResult(action: Action, result: object | object[], context: SilkweaveContext, disposition: unknown) {\n if (action.toolResult) {\n const response = action.toolResult(result, context)\n if (response) { return response }\n }\n // A structured action's output schema is a contract fixed at tools/list\n // time - a client's `_meta.disposition` cannot demote it.\n if (action.disposition === 'structured') { return structuredResult(action, result) }\n return disposition === 'smart' ? smartToolResult(result) : jsonToolResult(result)\n}\n\n/**\n * Register every action as an MCP tool on `server`. Shared by all MCP transports\n * (`stdio`, `http`, `edge`). Each tool call forks `context` with a per-call\n * `logger`, the SDK `extra`, a synthesized `request` (see `requestFromExtra`),\n * and - when bearer auth ran for this request - the resolved `auth`. The result\n * is formatted by the action's `toolResult` hook if present, otherwise per the\n * resolved disposition (client `_meta.disposition` > `action.disposition` >\n * `json`); a `'structured'` action always ships schema-parsed\n * `structuredContent` (its contract cannot be demoted per-call).\n */\nexport function registerTools(\n server: McpServer,\n actions: Action[],\n context: SilkweaveContext,\n options: RegisterToolsOptions = {}\n) {\n const stream = options.logStream ?? process.stderr\n for (const action of actions) {\n server.registerTool(pascalCase(action.name), {\n title: capitalCase(action.name),\n description: action.description,\n inputSchema: action.input,\n // Derived base (query ⇒ read-only), explicit annotations merged over.\n annotations: { readOnlyHint: action.kind === 'query', ...action.annotations },\n // Only structured actions declare an outputSchema contract - the SDK\n // enforces it server-side and SDK clients enforce it independently, so\n // forwarding is strictly opt-in via disposition.\n ...(action.disposition === 'structured' && action.output ? { outputSchema: action.output } : {})\n }, async (input, extra) => {\n const logger = createToolLogger(extra, stream)\n const currentAuth = authStorage.getStore()\n const actionContext = context.fork({\n logger,\n extra,\n request: requestFromExtra(extra.requestInfo),\n ...(currentAuth ? { auth: currentAuth } : {})\n })\n // Client-sent `_meta.disposition` wins; otherwise fall back to the\n // action's configured default (`json` when neither is set).\n const disposition = extra._meta?.disposition ?? action.disposition\n const base = { action: action.name, tool: pascalCase(action.name), transport: 'mcp' as const, context: actionContext }\n const started = Date.now()\n try {\n const result = await runAction(action, input, actionContext, extra)\n const formatted = formatToolResult(action, result, actionContext, disposition)\n emitToolCall(options.onToolCall, {\n ...base,\n durationMs: Date.now() - started,\n ok: formatted.isError !== true,\n ...(options.onToolCall ? resultMeta(result, formatted) : {})\n })\n return formatted\n } catch (error) {\n emitToolCall(options.onToolCall, { ...base, durationMs: Date.now() - started, ok: false, ...errorMeta(error) })\n return handleToolError(error)\n }\n })\n }\n}\n"],"mappings":"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"}