@silkweave/mcp 2.6.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -39,7 +39,7 @@ Configure in Claude Desktop or Claude Code:
39
39
 
40
40
  ### http
41
41
 
42
- Session-based MCP transport over HTTP with SSE streaming and resumability.
42
+ **Stateless** MCP transport over HTTP with per-call SSE streaming (no sessions, so any request can hit any instance behind a plain load balancer).
43
43
 
44
44
  ```typescript
45
45
  import { silkweave } from '@silkweave/core'
@@ -51,7 +51,7 @@ await silkweave({ name: 'my-tools', description: 'My Tools', version: '1.0.0' })
51
51
  .start()
52
52
  ```
53
53
 
54
- Exposes `POST /mcp` (invoke tools), `GET /mcp` (SSE stream), and `DELETE /mcp` (terminate session). Built on Express with `StreamableHTTPServerTransport` from the MCP SDK.
54
+ Exposes a single stateless `POST /mcp` (each call mints a fresh transport with `sessionIdGenerator: undefined`; progress streams over SSE per call). No `Mcp-Session-Id`, no `GET`/`DELETE` reconnect. Built on Express with `StreamableHTTPServerTransport` from the MCP SDK.
55
55
 
56
56
  | Option | Type | Description |
57
57
  |--------|------|-------------|
@@ -194,4 +194,4 @@ All result utilities are exported from `@silkweave/mcp`:
194
194
 
195
195
  - [Silkweave README](https://github.com/silkweave/silkweave) - Full documentation
196
196
  - [`@silkweave/core`](https://www.npmjs.com/package/@silkweave/core) - Core library
197
- - [`@silkweave/vercel`](https://www.npmjs.com/package/@silkweave/vercel) - Stateless MCP for Vercel
197
+ - [`@silkweave/edge`](https://www.npmjs.com/package/@silkweave/edge) - Stateless MCP on Web-Standard edge/serverless runtimes (Cloudflare Workers, Vercel, Bun, Deno)
@@ -1,11 +1,11 @@
1
- import { i as parseResourceMessage } from "./result-BiFuFt5B.mjs";
2
- import { LoggingMessageNotificationSchema, ProgressNotificationSchema } from "@modelcontextprotocol/sdk/types.js";
1
+ import { i as parseResourceMessage } from "./result-B_9Eo1ey.mjs";
3
2
  import { createCLILogger } from "@silkweave/logger";
4
3
  import { kebabCase } from "change-case";
5
4
  import { randomUUID } from "crypto";
6
5
  import { intro } from "@clack/prompts";
7
6
  import { Client } from "@modelcontextprotocol/sdk/client";
8
7
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
8
+ import { LoggingMessageNotificationSchema, ProgressNotificationSchema } from "@modelcontextprotocol/sdk/types.js";
9
9
  import { Command } from "commander";
10
10
  //#region src/adapter/cliProxy.ts
11
11
  function coerce(value, type) {
package/build/index.d.mts CHANGED
@@ -1,10 +1,10 @@
1
+ import { a as smartToolResult, c as requestFromExtra, i as parseResourceMessage, n as handleToolError, o as RegisterToolsOptions, r as jsonToolResult, s as registerTools, t as errorToolResult } from "./result-BVpEX7jU.mjs";
1
2
  import { CreateMcpExpressAppOptions } from "@modelcontextprotocol/sdk/server/express.js";
2
- import { Action, AdapterFactory, SilkweaveContext, SilkweaveError, SilkweaveOptions } from "@silkweave/core";
3
+ import { Action, AdapterFactory, SilkweaveContext, SilkweaveOptions } from "@silkweave/core";
3
4
  import { Express, RequestHandler } from "express";
4
5
  import { AuthConfig, AuthInfo } from "@silkweave/auth";
5
6
  import { AsyncLocalStorage } from "node:async_hooks";
6
7
  import { CorsOptions, CorsOptions as CorsOptions$1 } from "cors";
7
- import { CallToolResult, EmbeddedResource } from "@modelcontextprotocol/sdk/types.js";
8
8
  import { Server } from "http";
9
9
 
10
10
  //#region src/adapter/http.d.ts
@@ -66,8 +66,8 @@ declare function authMiddleware(auth: AuthConfig, context: SilkweaveContext): Re
66
66
  declare const MCP_REQUIRED_HEADERS: string[];
67
67
  /**
68
68
  * CORS middleware preconfigured to expose the headers MCP clients need
69
- * (`Mcp-Session-Id`, `Last-Event-Id`, `Mcp-Protocol-Version`, `WWW-Authenticate`)
70
- * on top of any user-supplied options.
69
+ * (`Last-Event-Id`, `Mcp-Protocol-Version`, `WWW-Authenticate`) on top of any
70
+ * user-supplied options. (Stateless transport - no `Mcp-Session-Id`.)
71
71
  *
72
72
  * Pass `false` to disable, omit / pass `true` for permissive defaults, or pass
73
73
  * a `CorsOptions` object to override.
@@ -124,33 +124,20 @@ declare function sideloadResource(options?: SideloadResourceOptions): RequestHan
124
124
  //#endregion
125
125
  //#region src/handlers/transport.d.ts
126
126
  interface McpTransportHandlers {
127
- /** `POST /mcp` - initialize session or dispatch request to an existing one. */
127
+ /** `POST /mcp` - handle a single stateless MCP request/response (SSE per call). */
128
128
  post: RequestHandler;
129
- /** `GET /mcp` and `DELETE /mcp` - long-poll session stream / session termination. Also covers `GET /mcp/resource/:id`. */
130
- stream: RequestHandler;
131
129
  }
132
130
  /**
133
- * Build the MCP Streamable HTTP transport route handlers.
131
+ * Build the MCP Streamable HTTP transport route handler.
134
132
  *
135
- * Sessions are kept in a per-call closure (one map per `mcpTransport()` call),
136
- * so the same handler object should be registered for all three transport
137
- * routes - `POST /mcp`, `GET /mcp`, `DELETE /mcp` - to share session state.
133
+ * Stateless (per the 2026 spec direction): each `POST /mcp` mints a fresh
134
+ * transport + server with `sessionIdGenerator: undefined`, handles exactly that
135
+ * request (streaming progress over SSE when the call carries a `progressToken`),
136
+ * and tears down on response close. No `Mcp-Session-Id`, no session map, no
137
+ * `GET`/`DELETE` reconnect - any request can hit any instance.
138
138
  */
139
139
  declare function mcpTransport(silkweaveOptions: SilkweaveOptions, context: SilkweaveContext, actions: Action[]): McpTransportHandlers;
140
140
  //#endregion
141
- //#region src/util/result.d.ts
142
- declare function smartToolResult(data: string | object | object[]): CallToolResult;
143
- declare function jsonToolResult(data: object, isError?: boolean): CallToolResult;
144
- declare function errorToolResult({
145
- code,
146
- name,
147
- message
148
- }: SilkweaveError): CallToolResult;
149
- declare function handleToolError(error: unknown): CallToolResult;
150
- declare function parseResourceMessage({
151
- resource
152
- }: EmbeddedResource): string;
153
- //#endregion
154
141
  //#region src/util/sideload.d.ts
155
142
  interface SideloadResource {
156
143
  id: string;
@@ -163,5 +150,5 @@ declare function createSideloadResource(buffer: Buffer, {
163
150
  contentType
164
151
  }: Pick<SideloadResource, 'name' | 'contentType'>): Promise<SideloadResource>;
165
152
  //#endregion
166
- export { type CorsOptions, MCP_REQUIRED_HEADERS, McpTransportHandlers, OAuthRouteHandlers, SideloadResource, SideloadResourceOptions, StartMcpHttpOptions, authMiddleware, authStorage, buildMcpExpressApp, createSideloadResource, errorToolResult, handleToolError, http, jsonToolResult, mcpCors, mcpTransport, oauthRoutes, parseResourceMessage, protectedResourceMetadata, sideloadResource, smartToolResult, startMcpServer, stdio };
153
+ export { type CorsOptions, MCP_REQUIRED_HEADERS, McpTransportHandlers, OAuthRouteHandlers, RegisterToolsOptions, SideloadResource, SideloadResourceOptions, StartMcpHttpOptions, authMiddleware, authStorage, buildMcpExpressApp, createSideloadResource, errorToolResult, handleToolError, http, jsonToolResult, mcpCors, mcpTransport, oauthRoutes, parseResourceMessage, protectedResourceMetadata, registerTools, requestFromExtra, sideloadResource, smartToolResult, startMcpServer, stdio };
167
154
  //# sourceMappingURL=index.d.mts.map
@@ -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/result.ts","../src/util/sideload.ts"],"mappings":";;;;;;;;;;UAaiB,mBAAA,SAA4B,0BAAA;EAC3C,IAAA;EACA,IAAA;EACA,IAAA,GAAO,UAAA;EAHQ;EAKf,IAAA,GAAO,aAAA;;EAEP,iBAAA;EAFO;EAIP,WAAA;AAAA;;;;;;;;iBAUc,kBAAA,CACd,gBAAA,EAAkB,gBAAA,EAClB,OAAA,EAAS,gBAAA,EACT,OAAA,EAAS,MAAA,IACT,OAAA,EAAS,mBAAA,GACR,OAAA;;;;;AALH;;iBAsDsB,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;;;cCxDrB,KAAA,EAAO,cAAA;;;;;;;cC1CP,WAAA,EAAW,iBAAA,CAAA,QAAA;;AFIxB;;;;;;;;;iBEQgB,cAAA,CAAe,IAAA,EAAM,UAAA,EAAY,OAAA,EAAS,gBAAA,GAAmB,cAAA;;;;cCjBhE,oBAAA;;;;;;;AHSb;;iBGCgB,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;ELhBmC;EKkBnC,KAAA,EAAO,cAAA;ELfA;EKiBP,QAAA,EAAU,cAAA;AAAA;;;;;;;;;;;iBAaI,WAAA,CAAY,IAAA,EAAM,UAAA,GAAa,kBAAA;;;UC1C9B,uBAAA;;EAEf,WAAA;AAAA;;;;;;ANOF;iBMEgB,gBAAA,CAAiB,OAAA,GAAS,uBAAA,GAA+B,cAAA;;;UCiGxD,oBAAA;;EAEf,IAAA,EAAM,cAAA;;EAEN,MAAA,EAAQ,cAAA;AAAA;;;APvGV;;;;;iBOiHgB,YAAA,CACd,gBAAA,EAAkB,gBAAA,EAClB,OAAA,EAAS,gBAAA,EACT,OAAA,EAAS,MAAA,KACR,oBAAA;;;iBC9Ha,eAAA,CAAgB,IAAA,+BAAmC,cAAA;AAAA,iBAqBnD,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;;;UCrDlC,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":";;;;;;;;;;UAaiB,mBAAA,SAA4B,0BAAA;EAC3C,IAAA;EACA,IAAA;EACA,IAAA,GAAO,UAAA;EAHQ;EAKf,IAAA,GAAO,aAAA;;EAEP,iBAAA;EAFO;EAIP,WAAA;AAAA;;;;;;;;iBAUc,kBAAA,CACd,gBAAA,EAAkB,gBAAA,EAClB,OAAA,EAAS,gBAAA,EACT,OAAA,EAAS,MAAA,IACT,OAAA,EAAS,mBAAA,GACR,OAAA;;;;;AALH;;iBAmDsB,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;;;cCnGrB,KAAA,EAAO,cAAA;;;;;;;cCIP,WAAA,EAAW,iBAAA,CAAA,QAAA;;AFIxB;;;;;;;;;iBEQgB,cAAA,CAAe,IAAA,EAAM,UAAA,EAAY,OAAA,EAAS,gBAAA,GAAmB,cAAA;;;;cCjBhE,oBAAA;;;;;;;AHSb;;iBGCgB,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;ELhBmC;EKkBnC,KAAA,EAAO,cAAA;ELfA;EKiBP,QAAA,EAAU,cAAA;AAAA;;;;;;;;;;;iBAaI,WAAA,CAAY,IAAA,EAAM,UAAA,GAAa,kBAAA;;;UC1C9B,uBAAA;;EAEf,WAAA;AAAA;;;;;;ANOF;iBMEgB,gBAAA,CAAiB,OAAA,GAAS,uBAAA,GAA+B,cAAA;;;UCGxD,oBAAA;;EAEf,IAAA,EAAM,cAAA;AAAA;;;;;APPR;;;;;iBOmBgB,YAAA,CACd,gBAAA,EAAkB,gBAAA,EAClB,OAAA,EAAS,gBAAA,EACT,OAAA,EAAS,MAAA,KACR,oBAAA;;;UCjCc,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,62 +1,26 @@
1
- import { a as smartToolResult, i as parseResourceMessage, n as handleToolError, r as jsonToolResult, t as errorToolResult } from "./result-BiFuFt5B.mjs";
1
+ import { i as authStorage, n as requestFromExtra, r as authMiddleware, t as registerTools } from "./registerTools-DlguElKV.mjs";
2
+ import { a as smartToolResult, i as parseResourceMessage, n as handleToolError, r as jsonToolResult, t as errorToolResult } from "./result-B_9Eo1ey.mjs";
2
3
  import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
3
- import { createContext, isStreamingAction, runStreamingAction } from "@silkweave/core";
4
+ import { createContext } from "@silkweave/core";
4
5
  import express from "express";
5
- import { generateProtectedResourceMetadata, validateToken } from "@silkweave/auth";
6
- import { AsyncLocalStorage } from "node:async_hooks";
6
+ import { generateProtectedResourceMetadata } from "@silkweave/auth";
7
7
  import cors from "cors";
8
8
  import { readFile, writeFile } from "fs/promises";
9
- import { InMemoryEventStore } from "@modelcontextprotocol/sdk/examples/shared/inMemoryEventStore.js";
10
9
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
11
10
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
12
- import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
13
- import { createLogger } from "@silkweave/logger";
14
- import { capitalCase, pascalCase } from "change-case";
15
- import { randomUUID } from "crypto";
16
11
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
17
- //#region src/handlers/auth.ts
18
- /**
19
- * Per-request bearer-token storage used by tool handlers to read the resolved
20
- * `AuthInfo` for the currently-handled MCP call.
21
- */
22
- const authStorage = new AsyncLocalStorage();
23
- /**
24
- * Express middleware that validates the `Authorization: Bearer …` header via
25
- * the supplied `AuthConfig`. On success the resolved `AuthInfo` is placed in
26
- * `authStorage` for the duration of the downstream handler - `mcpTransport`'s
27
- * tool callbacks pick it up to populate the silkweave context's `auth` key.
28
- *
29
- * The middleware should NOT be applied to OAuth-discovery / token routes -
30
- * compose it only on the routes that require an authenticated caller (the MCP
31
- * transport itself, sideload, etc.).
32
- */
33
- function authMiddleware(auth, context) {
34
- return async (req, res, next) => {
35
- const result = await validateToken(req.headers.authorization, auth, context.fork({ request: req }));
36
- if (result.error) {
37
- for (const [key, value] of Object.entries(result.error.headers)) res.header(key, value);
38
- res.status(result.error.statusCode).json(result.error.body);
39
- return;
40
- }
41
- if (result.auth) authStorage.run(result.auth, () => {
42
- next();
43
- });
44
- else next();
45
- };
46
- }
47
- //#endregion
12
+ import { randomUUID } from "crypto";
48
13
  //#region src/handlers/cors.ts
49
14
  /** Headers required by the MCP protocol that must always be exposed when CORS is in use. */
50
15
  const MCP_REQUIRED_HEADERS = [
51
16
  "WWW-Authenticate",
52
- "Mcp-Session-Id",
53
17
  "Last-Event-Id",
54
18
  "Mcp-Protocol-Version"
55
19
  ];
56
20
  /**
57
21
  * CORS middleware preconfigured to expose the headers MCP clients need
58
- * (`Mcp-Session-Id`, `Last-Event-Id`, `Mcp-Protocol-Version`, `WWW-Authenticate`)
59
- * on top of any user-supplied options.
22
+ * (`Last-Event-Id`, `Mcp-Protocol-Version`, `WWW-Authenticate`) on top of any
23
+ * user-supplied options. (Stateless transport - no `Mcp-Session-Id`.)
60
24
  *
61
25
  * Pass `false` to disable, omit / pass `true` for permissive defaults, or pass
62
26
  * a `CorsOptions` object to override.
@@ -81,7 +45,7 @@ function mcpCors(corsConfig = true) {
81
45
  */
82
46
  function protectedResourceMetadata(auth) {
83
47
  if (!auth.resourceUrl || !auth.authorizationServers?.length) throw new Error("@silkweave/mcp protectedResourceMetadata(): auth.resourceUrl and auth.authorizationServers are required");
84
- const metadata = generateProtectedResourceMetadata(auth.resourceUrl, auth.authorizationServers);
48
+ const metadata = generateProtectedResourceMetadata(auth.resourceUrl, auth.authorizationServers, auth.requiredScopes);
85
49
  return (_req, res) => {
86
50
  res.json(metadata);
87
51
  };
@@ -155,85 +119,6 @@ function sideloadResource(options = {}) {
155
119
  }
156
120
  //#endregion
157
121
  //#region src/handlers/transport.ts
158
- /**
159
- * Build the generic `request` context value (the same key REST/tRPC populate)
160
- * from the MCP SDK's `extra.requestInfo`, so transport-agnostic consumers - e.g.
161
- * `@silkweave/nestjs` `@UseGuards` guards reading
162
- * `switchToHttp().getRequest().headers` - work over MCP. There is no path
163
- * `params`/`query` on an MCP call, so those are empty stand-ins for graceful
164
- * degradation. Returns `undefined` when no HTTP request info is available.
165
- */
166
- function requestFromExtra(requestInfo) {
167
- if (!requestInfo) return;
168
- return {
169
- headers: requestInfo.headers ?? {},
170
- url: requestInfo.url?.toString(),
171
- params: {},
172
- query: {}
173
- };
174
- }
175
- function registerTools$1(server, actions, context) {
176
- for (const action of actions) server.registerTool(pascalCase(action.name), {
177
- title: capitalCase(action.name),
178
- description: action.description,
179
- inputSchema: action.input
180
- }, async (input, extra) => {
181
- const logger = createLogger({
182
- stream: process.stderr,
183
- onLog: (level, data) => {
184
- extra.sendNotification({
185
- method: "notifications/message",
186
- params: {
187
- level,
188
- data
189
- }
190
- });
191
- },
192
- onProgress: ({ progress, total, message }) => {
193
- if (!extra._meta?.progressToken) return;
194
- extra.sendNotification({
195
- method: "notifications/progress",
196
- params: {
197
- progress,
198
- total,
199
- message,
200
- progressToken: extra._meta.progressToken
201
- }
202
- });
203
- }
204
- });
205
- const currentAuth = authStorage.getStore();
206
- const actionContext = context.fork({
207
- logger,
208
- extra,
209
- request: requestFromExtra(extra.requestInfo),
210
- ...currentAuth ? { auth: currentAuth } : {}
211
- });
212
- const disposition = extra._meta?.disposition ?? action.disposition;
213
- const progressToken = extra._meta?.progressToken;
214
- try {
215
- let result;
216
- if (isStreamingAction(action)) result = await runStreamingAction(action, input, actionContext, progressToken ? async (chunk, index) => {
217
- await extra.sendNotification({
218
- method: "notifications/progress",
219
- params: {
220
- progressToken,
221
- progress: index + 1,
222
- message: JSON.stringify(chunk)
223
- }
224
- });
225
- } : void 0);
226
- else result = await action.run(input, actionContext);
227
- if (action.toolResult) {
228
- const response = action.toolResult(result, actionContext);
229
- if (response) return response;
230
- }
231
- return disposition === "json" ? jsonToolResult(result) : smartToolResult(result);
232
- } catch (error) {
233
- return handleToolError(error);
234
- }
235
- });
236
- }
237
122
  function createMcpServer(options, actions, context) {
238
123
  const server = new McpServer({
239
124
  name: options.name,
@@ -243,56 +128,28 @@ function createMcpServer(options, actions, context) {
243
128
  tools: {},
244
129
  logging: {}
245
130
  } });
246
- registerTools$1(server, actions, context);
131
+ registerTools(server, actions, context);
247
132
  return server;
248
133
  }
249
- function createSessionTransport(transports) {
250
- const transport = new StreamableHTTPServerTransport({
251
- sessionIdGenerator: () => randomUUID(),
252
- enableJsonResponse: false,
253
- eventStore: new InMemoryEventStore(),
254
- onsessioninitialized: (sId) => {
255
- transports[sId] = transport;
256
- }
257
- });
258
- transport.onerror = (error) => {
259
- console.error(error);
260
- };
261
- transport.onclose = () => {
262
- const sid = transport.sessionId;
263
- if (sid && transports[sid]) delete transports[sid];
264
- };
265
- return transport;
266
- }
267
134
  /**
268
- * Build the MCP Streamable HTTP transport route handlers.
135
+ * Build the MCP Streamable HTTP transport route handler.
269
136
  *
270
- * Sessions are kept in a per-call closure (one map per `mcpTransport()` call),
271
- * so the same handler object should be registered for all three transport
272
- * routes - `POST /mcp`, `GET /mcp`, `DELETE /mcp` - to share session state.
137
+ * Stateless (per the 2026 spec direction): each `POST /mcp` mints a fresh
138
+ * transport + server with `sessionIdGenerator: undefined`, handles exactly that
139
+ * request (streaming progress over SSE when the call carries a `progressToken`),
140
+ * and tears down on response close. No `Mcp-Session-Id`, no session map, no
141
+ * `GET`/`DELETE` reconnect - any request can hit any instance.
273
142
  */
274
143
  function mcpTransport(silkweaveOptions, context, actions) {
275
- const transports = {};
276
144
  const post = async (req, res) => {
277
- const sessionId = req.headers["mcp-session-id"];
278
145
  try {
279
- if (sessionId && transports[sessionId]) {
280
- await transports[sessionId].handleRequest(req, res, req.body);
281
- return;
282
- }
283
- if (!isInitializeRequest(req.body)) {
284
- res.status(404).json({
285
- jsonrpc: "2.0",
286
- error: {
287
- code: -32e3,
288
- message: "Session not found"
289
- },
290
- id: null
291
- });
292
- return;
293
- }
294
- const transport = createSessionTransport(transports);
295
- await createMcpServer(silkweaveOptions, actions, context).connect(transport);
146
+ const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: void 0 });
147
+ const server = createMcpServer(silkweaveOptions, actions, context);
148
+ res.on("close", () => {
149
+ transport.close();
150
+ server.close();
151
+ });
152
+ await server.connect(transport);
296
153
  await transport.handleRequest(req, res, req.body);
297
154
  } catch (error) {
298
155
  console.error("Error handling MCP request:", error);
@@ -306,18 +163,7 @@ function mcpTransport(silkweaveOptions, context, actions) {
306
163
  });
307
164
  }
308
165
  };
309
- const stream = async (req, res) => {
310
- const sessionId = req.headers["mcp-session-id"];
311
- if (!sessionId || !transports[sessionId]) {
312
- res.status(400).send("Invalid or missing session ID");
313
- return;
314
- }
315
- await transports[sessionId].handleRequest(req, res);
316
- };
317
- return {
318
- post,
319
- stream
320
- };
166
+ return { post };
321
167
  }
322
168
  //#endregion
323
169
  //#region src/adapter/http.ts
@@ -363,9 +209,6 @@ function buildMcpExpressApp(silkweaveOptions, context, actions, options) {
363
209
  if (sideloadResources) app.get("/resource/:id", sideloadResource({ resourceDir }));
364
210
  const transport = mcpTransport(silkweaveOptions, context, actions);
365
211
  app.post("/mcp", express.json(), transport.post);
366
- app.get("/mcp", transport.stream);
367
- app.delete("/mcp", transport.stream);
368
- app.get("/mcp/resource/:id", transport.stream);
369
212
  return app;
370
213
  }
371
214
  /**
@@ -422,60 +265,6 @@ const http = (options) => {
422
265
  };
423
266
  //#endregion
424
267
  //#region src/adapter/stdio.ts
425
- function registerTools(server, actions, context) {
426
- for (const action of actions) server.registerTool(pascalCase(action.name), {
427
- title: capitalCase(action.name),
428
- description: action.description,
429
- inputSchema: action.input
430
- }, async (input, extra) => {
431
- const logger = createLogger({
432
- stream: false,
433
- onLog: (level, data) => {
434
- extra.sendNotification({
435
- method: "notifications/message",
436
- params: {
437
- level,
438
- data
439
- }
440
- });
441
- },
442
- onProgress: ({ progress, total, message }) => {
443
- if (!extra._meta?.progressToken) return;
444
- extra.sendNotification({
445
- method: "notifications/progress",
446
- params: {
447
- progress,
448
- total,
449
- message,
450
- progressToken: extra._meta.progressToken
451
- }
452
- });
453
- }
454
- });
455
- const actionContext = context.fork({
456
- logger,
457
- extra
458
- });
459
- const progressToken = extra._meta?.progressToken;
460
- try {
461
- let result;
462
- if (isStreamingAction(action)) result = await runStreamingAction(action, input, actionContext, progressToken ? async (chunk, index) => {
463
- await extra.sendNotification({
464
- method: "notifications/progress",
465
- params: {
466
- progressToken,
467
- progress: index + 1,
468
- message: JSON.stringify(chunk)
469
- }
470
- });
471
- } : void 0);
472
- else result = await action.run(input, actionContext);
473
- return action.toolResult?.(result, actionContext) ?? smartToolResult(result);
474
- } catch (error) {
475
- return handleToolError(error);
476
- }
477
- });
478
- }
479
268
  const stdio = () => {
480
269
  return (options, baseContext) => {
481
270
  const context = baseContext.fork({ adapter: "stdio" });
@@ -490,7 +279,7 @@ const stdio = () => {
490
279
  return {
491
280
  context,
492
281
  start: async (actions) => {
493
- registerTools(server, actions, context);
282
+ registerTools(server, actions, context, { logStream: false });
494
283
  const transport = new StdioServerTransport();
495
284
  await server.connect(transport);
496
285
  },
@@ -514,6 +303,6 @@ async function createSideloadResource(buffer, { name, contentType }) {
514
303
  return resource;
515
304
  }
516
305
  //#endregion
517
- export { MCP_REQUIRED_HEADERS, authMiddleware, authStorage, buildMcpExpressApp, createSideloadResource, errorToolResult, handleToolError, http, jsonToolResult, mcpCors, mcpTransport, oauthRoutes, parseResourceMessage, protectedResourceMetadata, sideloadResource, smartToolResult, startMcpServer, stdio };
306
+ export { MCP_REQUIRED_HEADERS, authMiddleware, authStorage, buildMcpExpressApp, createSideloadResource, errorToolResult, handleToolError, http, jsonToolResult, mcpCors, mcpTransport, oauthRoutes, parseResourceMessage, protectedResourceMetadata, registerTools, requestFromExtra, sideloadResource, smartToolResult, startMcpServer, stdio };
518
307
 
519
308
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["registerTools"],"sources":["../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/adapter/http.ts","../src/adapter/stdio.ts","../src/util/sideload.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 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', 'Mcp-Session-Id', 'Last-Event-Id', 'Mcp-Protocol-Version']\n\n/**\n * CORS middleware preconfigured to expose the headers MCP clients need\n * (`Mcp-Session-Id`, `Last-Event-Id`, `Mcp-Protocol-Version`, `WWW-Authenticate`)\n * on top of any user-supplied options.\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)\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 { InMemoryEventStore } from '@modelcontextprotocol/sdk/examples/shared/inMemoryEventStore.js'\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'\nimport { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js'\nimport { Action, ActionRun, isStreamingAction, runStreamingAction, SilkweaveContext, SilkweaveOptions } from '@silkweave/core'\nimport { createLogger } from '@silkweave/logger'\nimport { capitalCase, pascalCase } from 'change-case'\nimport { randomUUID } from 'crypto'\nimport { type RequestHandler } from 'express'\nimport { handleToolError, jsonToolResult, smartToolResult } from '../util/result.js'\nimport { authStorage } from './auth.js'\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 is no path\n * `params`/`query` on an MCP call, so those are empty stand-ins for graceful\n * degradation. Returns `undefined` when no HTTP request info is available.\n */\nfunction requestFromExtra(requestInfo: { headers?: unknown; url?: { toString(): string } } | undefined) {\n if (!requestInfo) { return undefined }\n return { headers: requestInfo.headers ?? {}, url: requestInfo.url?.toString(), params: {}, query: {} }\n}\n\nfunction registerTools(server: McpServer, actions: Action[], context: SilkweaveContext) {\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 }, async (input, extra) => {\n const logger = createLogger({\n stream: process.stderr,\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 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 (`smart` when neither is set).\n const disposition = extra._meta?.disposition ?? action.disposition\n const progressToken = extra._meta?.progressToken\n try {\n let result: object | object[]\n if (isStreamingAction(action)) {\n result = await runStreamingAction(action, input, actionContext, 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 } else {\n result = await (action.run as ActionRun<object, object>)(input, actionContext)\n }\n if (action.toolResult) {\n const response = action.toolResult(result, actionContext)\n if (response) { return response }\n }\n return disposition === 'json' ? jsonToolResult(result) : smartToolResult(result)\n } catch (error) {\n return handleToolError(error)\n }\n })\n }\n}\n\nfunction createMcpServer(options: SilkweaveOptions, actions: Action[], context: SilkweaveContext): 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)\n return server\n}\n\nfunction createSessionTransport(transports: Record<string, StreamableHTTPServerTransport>): StreamableHTTPServerTransport {\n const eventStore = new InMemoryEventStore()\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: () => randomUUID(),\n enableJsonResponse: false,\n eventStore,\n onsessioninitialized: (sId) => {\n transports[sId] = transport\n }\n })\n transport.onerror = (error) => { console.error(error) }\n transport.onclose = () => {\n const sid = transport.sessionId\n if (sid && transports[sid]) { delete transports[sid] }\n }\n return transport\n}\n\nexport interface McpTransportHandlers {\n /** `POST /mcp` - initialize session or dispatch request to an existing one. */\n post: RequestHandler\n /** `GET /mcp` and `DELETE /mcp` - long-poll session stream / session termination. Also covers `GET /mcp/resource/:id`. */\n stream: RequestHandler\n}\n\n/**\n * Build the MCP Streamable HTTP transport route handlers.\n *\n * Sessions are kept in a per-call closure (one map per `mcpTransport()` call),\n * so the same handler object should be registered for all three transport\n * routes - `POST /mcp`, `GET /mcp`, `DELETE /mcp` - to share session state.\n */\nexport function mcpTransport(\n silkweaveOptions: SilkweaveOptions,\n context: SilkweaveContext,\n actions: Action[]\n): McpTransportHandlers {\n const transports: Record<string, StreamableHTTPServerTransport> = {}\n\n const post: RequestHandler = async (req, res) => {\n const sessionId = req.headers['mcp-session-id'] as string | undefined\n try {\n if (sessionId && transports[sessionId]) {\n await transports[sessionId].handleRequest(req, res, req.body)\n return\n }\n if (!isInitializeRequest(req.body)) {\n res.status(404).json({ jsonrpc: '2.0', error: { code: -32_000, message: 'Session not found' }, id: null })\n return\n }\n const transport = createSessionTransport(transports)\n await createMcpServer(silkweaveOptions, actions, context).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 const stream: RequestHandler = async (req, res) => {\n const sessionId = req.headers['mcp-session-id'] as string | undefined\n if (!sessionId || !transports[sessionId]) {\n res.status(400).send('Invalid or missing session ID')\n return\n }\n await transports[sessionId].handleRequest(req, res)\n }\n\n return { post, stream }\n}\n","import { createMcpExpressApp, type CreateMcpExpressAppOptions } from '@modelcontextprotocol/sdk/server/express.js'\nimport { AuthConfig } from '@silkweave/auth'\nimport { Action, AdapterFactory, createContext, 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 { 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\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, ...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)\n app.post('/mcp', express.json(), transport.post)\n app.get('/mcp', transport.stream)\n app.delete('/mcp', transport.stream)\n app.get('/mcp/resource/:id', transport.stream)\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 { Action, ActionRun, AdapterFactory, isStreamingAction, runStreamingAction, SilkweaveContext } from '@silkweave/core'\nimport { createLogger } from '@silkweave/logger'\nimport { capitalCase, pascalCase } from 'change-case'\nimport { handleToolError, smartToolResult } from '../util/result.js'\n\nfunction registerTools(server: McpServer, actions: Action[], context: SilkweaveContext) {\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 }, async (input, extra) => {\n const logger = createLogger({\n stream: false,\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 const actionContext = context.fork({ logger, extra })\n const progressToken = extra._meta?.progressToken\n try {\n let result: object | object[]\n if (isStreamingAction(action)) {\n result = await runStreamingAction(action, input, actionContext, 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 } else {\n result = await (action.run as ActionRun<object, object>)(input, actionContext)\n }\n return action.toolResult?.(result, actionContext) ?? smartToolResult(result)\n } catch (error) {\n return handleToolError(error)\n }\n })\n }\n}\n\nexport const stdio: AdapterFactory = () => {\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 registerTools(server, actions, context)\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":";;;;;;;;;;;;;;;;;;;;;AASA,MAAa,cAAc,IAAI,mBAA6B;;;;;;;;;;;AAY5D,SAAgB,eAAe,MAAkB,SAA2C;AAC1F,QAAO,OAAO,KAAK,KAAK,SAAS;EAC/B,MAAM,SAAS,MAAM,cAAc,IAAI,QAAQ,eAAe,MAAM,QAAQ,KAAK,EAAE,SAAS,KAAK,CAAC,CAAC;AACnG,MAAI,OAAO,OAAO;AAChB,QAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,MAAM,QAAQ,CAAI,KAAI,OAAO,KAAK,MAAM;AACzF,OAAI,OAAO,OAAO,MAAM,WAAW,CAAC,KAAK,OAAO,MAAM,KAAK;AAC3D;;AAEF,MAAI,OAAO,KACT,aAAY,IAAI,OAAO,YAAY;AAAE,SAAM;IAAG;MAE9C,OAAM;;;;;;AC5BZ,MAAa,uBAAuB;CAAC;CAAoB;CAAkB;CAAiB;CAAuB;;;;;;;;;AAUnH,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,qBAAqB;AAC/F,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;;;;;;;;;;;;;ACJpB,SAAS,iBAAiB,aAA8E;AACtG,KAAI,CAAC,YAAe;AACpB,QAAO;EAAE,SAAS,YAAY,WAAW,EAAE;EAAE,KAAK,YAAY,KAAK,UAAU;EAAE,QAAQ,EAAE;EAAE,OAAO,EAAE;EAAE;;AAGxG,SAASA,gBAAc,QAAmB,SAAmB,SAA2B;AACtF,MAAK,MAAM,UAAU,QACnB,QAAO,aAAa,WAAW,OAAO,KAAK,EAAE;EAC3C,OAAO,YAAY,OAAO,KAAK;EAC/B,aAAa,OAAO;EACpB,aAAa,OAAO;EACrB,EAAE,OAAO,OAAO,UAAU;EACzB,MAAM,SAAS,aAAa;GAC1B,QAAQ,QAAQ;GAChB,QAAQ,OAAO,SAAS;AACtB,UAAM,iBAAiB;KAAE,QAAQ;KAAyB,QAAQ;MAAE;MAAO;MAAM;KAAE,CAAC;;GAEtF,aAAa,EAAE,UAAU,OAAO,cAAc;AAC5C,QAAI,CAAC,MAAM,OAAO,cAAiB;AACnC,UAAM,iBAAiB;KACrB,QAAQ;KACR,QAAQ;MAAE;MAAU;MAAO;MAAS,eAAe,MAAM,MAAM;MAAe;KAC/E,CAAC;;GAEL,CAAC;EACF,MAAM,cAAc,YAAY,UAAU;EAC1C,MAAM,gBAAgB,QAAQ,KAAK;GACjC;GACA;GACA,SAAS,iBAAiB,MAAM,YAAY;GAC5C,GAAI,cAAc,EAAE,MAAM,aAAa,GAAG,EAAE;GAC7C,CAAC;EAGF,MAAM,cAAc,MAAM,OAAO,eAAe,OAAO;EACvD,MAAM,gBAAgB,MAAM,OAAO;AACnC,MAAI;GACF,IAAI;AACJ,OAAI,kBAAkB,OAAO,CAC3B,UAAS,MAAM,mBAAmB,QAAQ,OAAO,eAAe,gBAC5D,OAAO,OAAO,UAAU;AACxB,UAAM,MAAM,iBAAiB;KAC3B,QAAQ;KACR,QAAQ;MAAE;MAAe,UAAU,QAAQ;MAAG,SAAS,KAAK,UAAU,MAAM;MAAE;KAC/E,CAAC;OAEF,KAAA,EAAU;OAEd,UAAS,MAAO,OAAO,IAAkC,OAAO,cAAc;AAEhF,OAAI,OAAO,YAAY;IACrB,MAAM,WAAW,OAAO,WAAW,QAAQ,cAAc;AACzD,QAAI,SAAY,QAAO;;AAEzB,UAAO,gBAAgB,SAAS,eAAe,OAAO,GAAG,gBAAgB,OAAO;WACzE,OAAO;AACd,UAAO,gBAAgB,MAAM;;GAE/B;;AAIN,SAAS,gBAAgB,SAA2B,SAAmB,SAAsC;CAC3G,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,iBAAc,QAAQ,SAAS,QAAQ;AACvC,QAAO;;AAGT,SAAS,uBAAuB,YAA0F;CAExH,MAAM,YAAY,IAAI,8BAA8B;EAClD,0BAA0B,YAAY;EACtC,oBAAoB;EACpB,YAAA,IAJqB,oBAIX;EACV,uBAAuB,QAAQ;AAC7B,cAAW,OAAO;;EAErB,CAAC;AACF,WAAU,WAAW,UAAU;AAAE,UAAQ,MAAM,MAAM;;AACrD,WAAU,gBAAgB;EACxB,MAAM,MAAM,UAAU;AACtB,MAAI,OAAO,WAAW,KAAQ,QAAO,WAAW;;AAElD,QAAO;;;;;;;;;AAiBT,SAAgB,aACd,kBACA,SACA,SACsB;CACtB,MAAM,aAA4D,EAAE;CAEpE,MAAM,OAAuB,OAAO,KAAK,QAAQ;EAC/C,MAAM,YAAY,IAAI,QAAQ;AAC9B,MAAI;AACF,OAAI,aAAa,WAAW,YAAY;AACtC,UAAM,WAAW,WAAW,cAAc,KAAK,KAAK,IAAI,KAAK;AAC7D;;AAEF,OAAI,CAAC,oBAAoB,IAAI,KAAK,EAAE;AAClC,QAAI,OAAO,IAAI,CAAC,KAAK;KAAE,SAAS;KAAO,OAAO;MAAE,MAAM;MAAS,SAAS;MAAqB;KAAE,IAAI;KAAM,CAAC;AAC1G;;GAEF,MAAM,YAAY,uBAAuB,WAAW;AACpD,SAAM,gBAAgB,kBAAkB,SAAS,QAAQ,CAAC,QAAQ,UAAU;AAC5E,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;;;CAKpH,MAAM,SAAyB,OAAO,KAAK,QAAQ;EACjD,MAAM,YAAY,IAAI,QAAQ;AAC9B,MAAI,CAAC,aAAa,CAAC,WAAW,YAAY;AACxC,OAAI,OAAO,IAAI,CAAC,KAAK,gCAAgC;AACrD;;AAEF,QAAM,WAAW,WAAW,cAAc,KAAK,IAAI;;AAGrD,QAAO;EAAE;EAAM;EAAQ;;;;;;;;;;;ACpIzB,SAAgB,mBACd,kBACA,SACA,SACA,SACS;CACT,MAAM,EAAE,MAAM,MAAM,MAAM,YAAY,oBAAoB,MAAM,aAAa,GAAG,kBAAkB;CAClG,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,QAAQ;AAClE,KAAI,KAAK,QAAQ,QAAQ,MAAM,EAAE,UAAU,KAAK;AAChD,KAAI,IAAI,QAAQ,UAAU,OAAO;AACjC,KAAI,OAAO,QAAQ,UAAU,OAAO;AACpC,KAAI,IAAI,qBAAqB,UAAU,OAAO;AAE9C,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;;;;;AC3HL,SAAS,cAAc,QAAmB,SAAmB,SAA2B;AACtF,MAAK,MAAM,UAAU,QACnB,QAAO,aAAa,WAAW,OAAO,KAAK,EAAE;EAC3C,OAAO,YAAY,OAAO,KAAK;EAC/B,aAAa,OAAO;EACpB,aAAa,OAAO;EACrB,EAAE,OAAO,OAAO,UAAU;EACzB,MAAM,SAAS,aAAa;GAC1B,QAAQ;GACR,QAAQ,OAAO,SAAS;AACtB,UAAM,iBAAiB;KAAE,QAAQ;KAAyB,QAAQ;MAAE;MAAO;MAAM;KAAE,CAAC;;GAEtF,aAAa,EAAE,UAAU,OAAO,cAAc;AAC5C,QAAI,CAAC,MAAM,OAAO,cAAiB;AACnC,UAAM,iBAAiB;KACrB,QAAQ;KACR,QAAQ;MAAE;MAAU;MAAO;MAAS,eAAe,MAAM,MAAM;MAAe;KAC/E,CAAC;;GAEL,CAAC;EACF,MAAM,gBAAgB,QAAQ,KAAK;GAAE;GAAQ;GAAO,CAAC;EACrD,MAAM,gBAAgB,MAAM,OAAO;AACnC,MAAI;GACF,IAAI;AACJ,OAAI,kBAAkB,OAAO,CAC3B,UAAS,MAAM,mBAAmB,QAAQ,OAAO,eAAe,gBAC5D,OAAO,OAAO,UAAU;AACxB,UAAM,MAAM,iBAAiB;KAC3B,QAAQ;KACR,QAAQ;MAAE;MAAe,UAAU,QAAQ;MAAG,SAAS,KAAK,UAAU,MAAM;MAAE;KAC/E,CAAC;OAEF,KAAA,EAAU;OAEd,UAAS,MAAO,OAAO,IAAkC,OAAO,cAAc;AAEhF,UAAO,OAAO,aAAa,QAAQ,cAAc,IAAI,gBAAgB,OAAO;WACrE,OAAO;AACd,UAAO,gBAAgB,MAAM;;GAE/B;;AAIN,MAAa,cAA8B;AACzC,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,kBAAc,QAAQ,SAAS,QAAQ;IACvC,MAAM,YAAY,IAAI,sBAAsB;AAC5C,UAAM,OAAO,QAAQ,UAAU;;GAEjC,MAAM,YAAY;AAChB,UAAM,QAAQ,OAAO;;GAExB;;;;;AC7DL,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 { 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, SilkweaveContext, SilkweaveOptions } from '@silkweave/core'\nimport { type RequestHandler } from 'express'\nimport { registerTools } from './registerTools.js'\n\nfunction createMcpServer(options: SilkweaveOptions, actions: Action[], context: SilkweaveContext): 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)\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\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): McpTransportHandlers {\n const post: RequestHandler = async (req, res) => {\n try {\n const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined })\n const server = createMcpServer(silkweaveOptions, actions, context)\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, 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 { 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\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, ...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)\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 } from '@silkweave/core'\nimport { registerTools } from '../handlers/registerTools.js'\n\nexport const stdio: AdapterFactory = () => {\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 registerTools(server, actions, context, { logStream: false })\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;;;;;AClBpB,SAAS,gBAAgB,SAA2B,SAAmB,SAAsC;CAC3G,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,QAAQ;AACvC,QAAO;;;;;;;;;;;AAiBT,SAAgB,aACd,kBACA,SACA,SACsB;CACtB,MAAM,OAAuB,OAAO,KAAK,QAAQ;AAC/C,MAAI;GACF,MAAM,YAAY,IAAI,8BAA8B,EAAE,oBAAoB,KAAA,GAAW,CAAC;GACtF,MAAM,SAAS,gBAAgB,kBAAkB,SAAS,QAAQ;AAClE,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;;;;;;;;;;;ACpBjB,SAAgB,mBACd,kBACA,SACA,SACA,SACS;CACT,MAAM,EAAE,MAAM,MAAM,MAAM,YAAY,oBAAoB,MAAM,aAAa,GAAG,kBAAkB;CAClG,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,QAAQ;AAClE,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;;;;;AC1HL,MAAa,cAA8B;AACzC,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,kBAAc,QAAQ,SAAS,SAAS,EAAE,WAAW,OAAO,CAAC;IAC7D,MAAM,YAAY,IAAI,sBAAsB;AAC5C,UAAM,OAAO,QAAQ,UAAU;;GAEjC,MAAM,YAAY;AAChB,UAAM,QAAQ,OAAO;;GAExB;;;;;ACfL,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"}
@@ -0,0 +1,145 @@
1
+ import { a as smartToolResult, n as handleToolError, r as jsonToolResult } from "./result-B_9Eo1ey.mjs";
2
+ import { isStreamingAction, runStreamingAction } from "@silkweave/core";
3
+ import { validateToken } from "@silkweave/auth";
4
+ import { AsyncLocalStorage } from "node:async_hooks";
5
+ import { createLogger } from "@silkweave/logger";
6
+ import { capitalCase, pascalCase } from "change-case";
7
+ //#region src/handlers/auth.ts
8
+ /**
9
+ * Per-request bearer-token storage used by tool handlers to read the resolved
10
+ * `AuthInfo` for the currently-handled MCP call.
11
+ */
12
+ const authStorage = new AsyncLocalStorage();
13
+ /**
14
+ * Express middleware that validates the `Authorization: Bearer …` header via
15
+ * the supplied `AuthConfig`. On success the resolved `AuthInfo` is placed in
16
+ * `authStorage` for the duration of the downstream handler - `mcpTransport`'s
17
+ * tool callbacks pick it up to populate the silkweave context's `auth` key.
18
+ *
19
+ * The middleware should NOT be applied to OAuth-discovery / token routes -
20
+ * compose it only on the routes that require an authenticated caller (the MCP
21
+ * transport itself, sideload, etc.).
22
+ */
23
+ function authMiddleware(auth, context) {
24
+ return async (req, res, next) => {
25
+ const result = await validateToken(req.headers.authorization, auth, context.fork({ request: req }));
26
+ if (result.error) {
27
+ for (const [key, value] of Object.entries(result.error.headers)) res.header(key, value);
28
+ res.status(result.error.statusCode).json(result.error.body);
29
+ return;
30
+ }
31
+ if (result.auth) authStorage.run(result.auth, () => {
32
+ next();
33
+ });
34
+ else next();
35
+ };
36
+ }
37
+ //#endregion
38
+ //#region src/handlers/registerTools.ts
39
+ /**
40
+ * Build the generic `request` context value (the same key REST/tRPC populate)
41
+ * from the MCP SDK's `extra.requestInfo`, so transport-agnostic consumers - e.g.
42
+ * `@silkweave/nestjs` `@UseGuards` guards reading
43
+ * `switchToHttp().getRequest().headers` - work over MCP. There are no path
44
+ * `params`/`query`/`body` on the raw MCP call, so those start as empty
45
+ * stand-ins; `@silkweave/nestjs` later fills them from the validated tool input
46
+ * per the reflected param sources (path -> `params`, `@Query` -> `query`,
47
+ * `@Body` -> `body`) so guards reading any of them decide as they would over
48
+ * REST. Returns `undefined` when no HTTP request info is available.
49
+ */
50
+ function requestFromExtra(requestInfo) {
51
+ if (!requestInfo) return;
52
+ return {
53
+ headers: requestInfo.headers ?? {},
54
+ url: requestInfo.url?.toString(),
55
+ params: {},
56
+ query: {},
57
+ body: {}
58
+ };
59
+ }
60
+ /** Per-call logger that bridges silkweave logs/progress onto MCP notifications. */
61
+ function createToolLogger(extra, stream) {
62
+ return createLogger({
63
+ stream,
64
+ onLog: (level, data) => {
65
+ extra.sendNotification({
66
+ method: "notifications/message",
67
+ params: {
68
+ level,
69
+ data
70
+ }
71
+ });
72
+ },
73
+ onProgress: ({ progress, total, message }) => {
74
+ if (!extra._meta?.progressToken) return;
75
+ extra.sendNotification({
76
+ method: "notifications/progress",
77
+ params: {
78
+ progress,
79
+ total,
80
+ message,
81
+ progressToken: extra._meta.progressToken
82
+ }
83
+ });
84
+ }
85
+ });
86
+ }
87
+ /** Run the action, streaming chunks as progress notifications when a token is set. */
88
+ async function runAction(action, input, context, extra) {
89
+ const progressToken = extra._meta?.progressToken;
90
+ if (isStreamingAction(action)) return runStreamingAction(action, input, context, progressToken ? async (chunk, index) => {
91
+ await extra.sendNotification({
92
+ method: "notifications/progress",
93
+ params: {
94
+ progressToken,
95
+ progress: index + 1,
96
+ message: JSON.stringify(chunk)
97
+ }
98
+ });
99
+ } : void 0);
100
+ return action.run(input, context);
101
+ }
102
+ /** Format via the action's `toolResult` hook, else the resolved disposition. */
103
+ function formatToolResult(action, result, context, disposition) {
104
+ if (action.toolResult) {
105
+ const response = action.toolResult(result, context);
106
+ if (response) return response;
107
+ }
108
+ return disposition === "json" ? jsonToolResult(result) : smartToolResult(result);
109
+ }
110
+ /**
111
+ * Register every action as an MCP tool on `server`. Shared by all MCP transports
112
+ * (`stdio`, `http`, `edge`). Each tool call forks `context` with a per-call
113
+ * `logger`, the SDK `extra`, a synthesized `request` (see `requestFromExtra`),
114
+ * and - when bearer auth ran for this request - the resolved `auth`. The result
115
+ * is formatted by the action's `toolResult` hook if present, otherwise per the
116
+ * resolved disposition (client `_meta.disposition` > `action.disposition` >
117
+ * `smart`).
118
+ */
119
+ function registerTools(server, actions, context, options = {}) {
120
+ const stream = options.logStream ?? process.stderr;
121
+ for (const action of actions) server.registerTool(pascalCase(action.name), {
122
+ title: capitalCase(action.name),
123
+ description: action.description,
124
+ inputSchema: action.input
125
+ }, async (input, extra) => {
126
+ const logger = createToolLogger(extra, stream);
127
+ const currentAuth = authStorage.getStore();
128
+ const actionContext = context.fork({
129
+ logger,
130
+ extra,
131
+ request: requestFromExtra(extra.requestInfo),
132
+ ...currentAuth ? { auth: currentAuth } : {}
133
+ });
134
+ const disposition = extra._meta?.disposition ?? action.disposition;
135
+ try {
136
+ return formatToolResult(action, await runAction(action, input, actionContext, extra), actionContext, disposition);
137
+ } catch (error) {
138
+ return handleToolError(error);
139
+ }
140
+ });
141
+ }
142
+ //#endregion
143
+ export { authStorage as i, requestFromExtra as n, authMiddleware as r, registerTools as t };
144
+
145
+ //# sourceMappingURL=registerTools-DlguElKV.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registerTools-DlguElKV.mjs","names":[],"sources":["../src/handlers/auth.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 { 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, isStreamingAction, runStreamingAction, SilkweaveContext } from '@silkweave/core'\nimport { createLogger } from '@silkweave/logger'\nimport { capitalCase, pascalCase } from 'change-case'\nimport { handleToolError, jsonToolResult, smartToolResult } 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\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/** 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 return disposition === 'json' ? jsonToolResult(result) : smartToolResult(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 * `smart`).\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 }, 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 (`smart` when neither is set).\n const disposition = extra._meta?.disposition ?? action.disposition\n try {\n const result = await runAction(action, input, actionContext, extra)\n return formatToolResult(action, result, actionContext, disposition)\n } catch (error) {\n return handleToolError(error)\n }\n })\n }\n}\n"],"mappings":";;;;;;;;;;;AASA,MAAa,cAAc,IAAI,mBAA6B;;;;;;;;;;;AAY5D,SAAgB,eAAe,MAAkB,SAA2C;AAC1F,QAAO,OAAO,KAAK,KAAK,SAAS;EAC/B,MAAM,SAAS,MAAM,cAAc,IAAI,QAAQ,eAAe,MAAM,QAAQ,KAAK,EAAE,SAAS,KAAK,CAAC,CAAC;AACnG,MAAI,OAAO,OAAO;AAChB,QAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,MAAM,QAAQ,CAAI,KAAI,OAAO,KAAK,MAAM;AACzF,OAAI,OAAO,OAAO,MAAM,WAAW,CAAC,KAAK,OAAO,MAAM,KAAK;AAC3D;;AAEF,MAAI,OAAO,KACT,aAAY,IAAI,OAAO,YAAY;AAAE,SAAM;IAAG;MAE9C,OAAM;;;;;;;;;;;;;;;;ACAZ,SAAgB,iBAAiB,aAA8E;AAC7G,KAAI,CAAC,YAAe;AACpB,QAAO;EAAE,SAAS,YAAY,WAAW,EAAE;EAAE,KAAK,YAAY,KAAK,UAAU;EAAE,QAAQ,EAAE;EAAE,OAAO,EAAE;EAAE,MAAM,EAAE;EAAE;;;AAIlH,SAAS,iBAAiB,OAAkB,QAAmB;AAC7D,QAAO,aAAa;EAClB;EACA,QAAQ,OAAO,SAAS;AACtB,SAAM,iBAAiB;IAAE,QAAQ;IAAyB,QAAQ;KAAE;KAAO;KAAM;IAAE,CAAC;;EAEtF,aAAa,EAAE,UAAU,OAAO,cAAc;AAC5C,OAAI,CAAC,MAAM,OAAO,cAAiB;AACnC,SAAM,iBAAiB;IACrB,QAAQ;IACR,QAAQ;KAAE;KAAU;KAAO;KAAS,eAAe,MAAM,MAAM;KAAe;IAC/E,CAAC;;EAEL,CAAC;;;AAIJ,eAAe,UAAU,QAAgB,OAAe,SAA2B,OAA8C;CAC/H,MAAM,gBAAgB,MAAM,OAAO;AACnC,KAAI,kBAAkB,OAAO,CAC3B,QAAO,mBAAmB,QAAQ,OAAO,SAAS,gBAC9C,OAAO,OAAO,UAAU;AACxB,QAAM,MAAM,iBAAiB;GAC3B,QAAQ;GACR,QAAQ;IAAE;IAAe,UAAU,QAAQ;IAAG,SAAS,KAAK,UAAU,MAAM;IAAE;GAC/E,CAAC;KAEF,KAAA,EAAU;AAEhB,QAAQ,OAAO,IAAkC,OAAO,QAAQ;;;AAIlE,SAAS,iBAAiB,QAAgB,QAA2B,SAA2B,aAAsB;AACpH,KAAI,OAAO,YAAY;EACrB,MAAM,WAAW,OAAO,WAAW,QAAQ,QAAQ;AACnD,MAAI,SAAY,QAAO;;AAEzB,QAAO,gBAAgB,SAAS,eAAe,OAAO,GAAG,gBAAgB,OAAO;;;;;;;;;;;AAYlF,SAAgB,cACd,QACA,SACA,SACA,UAAgC,EAAE,EAClC;CACA,MAAM,SAAS,QAAQ,aAAa,QAAQ;AAC5C,MAAK,MAAM,UAAU,QACnB,QAAO,aAAa,WAAW,OAAO,KAAK,EAAE;EAC3C,OAAO,YAAY,OAAO,KAAK;EAC/B,aAAa,OAAO;EACpB,aAAa,OAAO;EACrB,EAAE,OAAO,OAAO,UAAU;EACzB,MAAM,SAAS,iBAAiB,OAAO,OAAO;EAC9C,MAAM,cAAc,YAAY,UAAU;EAC1C,MAAM,gBAAgB,QAAQ,KAAK;GACjC;GACA;GACA,SAAS,iBAAiB,MAAM,YAAY;GAC5C,GAAI,cAAc,EAAE,MAAM,aAAa,GAAG,EAAE;GAC7C,CAAC;EAGF,MAAM,cAAc,MAAM,OAAO,eAAe,OAAO;AACvD,MAAI;AAEF,UAAO,iBAAiB,QAAQ,MADX,UAAU,QAAQ,OAAO,eAAe,MAAM,EAC3B,eAAe,YAAY;WAC5D,OAAO;AACd,UAAO,gBAAgB,MAAM;;GAE/B"}
@@ -0,0 +1,64 @@
1
+ import { Action, SilkweaveContext, SilkweaveError } from "@silkweave/core";
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { createLogger } from "@silkweave/logger";
4
+ import { CallToolResult, EmbeddedResource } from "@modelcontextprotocol/sdk/types.js";
5
+
6
+ //#region src/handlers/registerTools.d.ts
7
+ type LogStream = NonNullable<Parameters<typeof createLogger>[0]>['stream'];
8
+ interface RegisterToolsOptions {
9
+ /**
10
+ * Where the per-call logger writes its human-readable stream. The `stdio`
11
+ * adapter MUST pass `false` (stdout is the MCP protocol channel); the HTTP
12
+ * transports default to `process.stderr`.
13
+ */
14
+ logStream?: LogStream;
15
+ }
16
+ /**
17
+ * Build the generic `request` context value (the same key REST/tRPC populate)
18
+ * from the MCP SDK's `extra.requestInfo`, so transport-agnostic consumers - e.g.
19
+ * `@silkweave/nestjs` `@UseGuards` guards reading
20
+ * `switchToHttp().getRequest().headers` - work over MCP. There are no path
21
+ * `params`/`query`/`body` on the raw MCP call, so those start as empty
22
+ * stand-ins; `@silkweave/nestjs` later fills them from the validated tool input
23
+ * per the reflected param sources (path -> `params`, `@Query` -> `query`,
24
+ * `@Body` -> `body`) so guards reading any of them decide as they would over
25
+ * REST. Returns `undefined` when no HTTP request info is available.
26
+ */
27
+ declare function requestFromExtra(requestInfo: {
28
+ headers?: unknown;
29
+ url?: {
30
+ toString(): string;
31
+ };
32
+ } | undefined): {
33
+ headers: {};
34
+ url: string | undefined;
35
+ params: {};
36
+ query: {};
37
+ body: {};
38
+ } | undefined;
39
+ /**
40
+ * Register every action as an MCP tool on `server`. Shared by all MCP transports
41
+ * (`stdio`, `http`, `edge`). Each tool call forks `context` with a per-call
42
+ * `logger`, the SDK `extra`, a synthesized `request` (see `requestFromExtra`),
43
+ * and - when bearer auth ran for this request - the resolved `auth`. The result
44
+ * is formatted by the action's `toolResult` hook if present, otherwise per the
45
+ * resolved disposition (client `_meta.disposition` > `action.disposition` >
46
+ * `smart`).
47
+ */
48
+ declare function registerTools(server: McpServer, actions: Action[], context: SilkweaveContext, options?: RegisterToolsOptions): void;
49
+ //#endregion
50
+ //#region src/util/result.d.ts
51
+ declare function smartToolResult(data: string | object | object[]): CallToolResult;
52
+ declare function jsonToolResult(data: object, isError?: boolean): CallToolResult;
53
+ declare function errorToolResult({
54
+ code,
55
+ name,
56
+ message
57
+ }: SilkweaveError): CallToolResult;
58
+ declare function handleToolError(error: unknown): CallToolResult;
59
+ declare function parseResourceMessage({
60
+ resource
61
+ }: EmbeddedResource): string;
62
+ //#endregion
63
+ export { smartToolResult as a, requestFromExtra as c, parseResourceMessage as i, handleToolError as n, RegisterToolsOptions as o, jsonToolResult as r, registerTools as s, errorToolResult as t };
64
+ //# sourceMappingURL=result-BVpEX7jU.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result-BVpEX7jU.d.mts","names":[],"sources":["../src/handlers/registerTools.ts","../src/util/result.ts"],"mappings":";;;;;;KASK,SAAA,GAAY,WAAA,CAAY,UAAA,QAAkB,YAAA;AAAA,UAG9B,oBAAA;;AAR+B;;;;EAc9C,SAAA,GAAY,SAAA;AAAA;;;;;;;;AANd;;;;iBAoBgB,gBAAA,CAAiB,WAAA;EAAe,OAAA;EAAmB,GAAA;IAAQ,QAAA;EAAA;AAAA;;;;;;;;;;;AAwD3E;;;;;iBAAgB,aAAA,CACd,MAAA,EAAQ,SAAA,EACR,OAAA,EAAS,MAAA,IACT,OAAA,EAAS,gBAAA,EACT,OAAA,GAAS,oBAAA;;;iBCxFK,eAAA,CAAgB,IAAA,+BAAmC,cAAA;AAAA,iBAqBnD,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"}
@@ -76,4 +76,4 @@ function parseResourceMessage({ resource }) {
76
76
  //#endregion
77
77
  export { smartToolResult as a, parseResourceMessage as i, handleToolError as n, jsonToolResult as r, errorToolResult as t };
78
78
 
79
- //# sourceMappingURL=result-BiFuFt5B.mjs.map
79
+ //# sourceMappingURL=result-B_9Eo1ey.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"result-BiFuFt5B.mjs","names":[],"sources":["../src/util/result.ts"],"sourcesContent":["import { EmbeddedResource, type CallToolResult } from '@modelcontextprotocol/sdk/types.js'\nimport { SilkweaveError } from '@silkweave/core'\nimport { randomUUID } from 'node:crypto'\n\nexport function smartToolResult(data: string | object | object[]): CallToolResult {\n const text = typeof data === 'string' ? data : JSON.stringify(data)\n const mimeType = typeof data === 'string' ? 'text/plain' : 'application/json'\n const ext = typeof data === 'string' ? 'txt' : 'json'\n if (text.length > 4096) {\n const uri = `mcp://toolResult/${randomUUID()}.${ext}`\n const buffer = Buffer.from(text)\n const blob = buffer.toString('base64')\n return {\n content: [\n { type: 'text', text: `Received resource ${uri} with ${buffer.byteLength} bytes` },\n { type: 'resource', resource: { uri, mimeType, blob } }\n ]\n }\n } else {\n return {\n content: [{ type: 'text' as const, text }]\n }\n }\n}\n\nexport function jsonToolResult(data: object, isError = false): CallToolResult {\n const result: CallToolResult = { content: [{ type: 'text' as const, text: JSON.stringify(data) }] }\n if (isError) { result.isError = true }\n return result\n}\n\nexport function errorToolResult({ code, name, message }: SilkweaveError): CallToolResult {\n return {\n isError: true,\n content: [{\n type: 'text',\n text: JSON.stringify({ success: false, code, name, message })\n }]\n }\n}\n\nexport function handleToolError(error: unknown): CallToolResult {\n if (error instanceof SilkweaveError) {\n return jsonToolResult({ success: false, name: error.name, message: error.message, code: error.code }, true)\n } else if (error instanceof Error) {\n // Log the full error (incl. stack) to stderr only - never put the stack trace\n // on the wire, where it would leak server internals to the MCP client.\n console.error(error)\n return jsonToolResult({ success: false, name: error.name, message: error.message }, true)\n } else {\n // Likewise keep the raw thrown value server-side - only a generic message goes out.\n console.error('Unknown tool error:', error)\n return jsonToolResult({ success: false, name: 'Unknown error', message: 'An unknown error occurred' }, true)\n }\n}\n\nexport function parseResourceMessage({ resource }: EmbeddedResource) {\n const text = ('blob' in resource) ? Buffer.from(resource.blob, 'base64').toString('utf-8') : resource.text\n return text\n}\n"],"mappings":";;;AAIA,SAAgB,gBAAgB,MAAkD;CAChF,MAAM,OAAO,OAAO,SAAS,WAAW,OAAO,KAAK,UAAU,KAAK;CACnE,MAAM,WAAW,OAAO,SAAS,WAAW,eAAe;CAC3D,MAAM,MAAM,OAAO,SAAS,WAAW,QAAQ;AAC/C,KAAI,KAAK,SAAS,MAAM;EACtB,MAAM,MAAM,oBAAoB,YAAY,CAAC,GAAG;EAChD,MAAM,SAAS,OAAO,KAAK,KAAK;EAChC,MAAM,OAAO,OAAO,SAAS,SAAS;AACtC,SAAO,EACL,SAAS,CACP;GAAE,MAAM;GAAQ,MAAM,qBAAqB,IAAI,QAAQ,OAAO,WAAW;GAAS,EAClF;GAAE,MAAM;GAAY,UAAU;IAAE;IAAK;IAAU;IAAM;GAAE,CACxD,EACF;OAED,QAAO,EACL,SAAS,CAAC;EAAE,MAAM;EAAiB;EAAM,CAAC,EAC3C;;AAIL,SAAgB,eAAe,MAAc,UAAU,OAAuB;CAC5E,MAAM,SAAyB,EAAE,SAAS,CAAC;EAAE,MAAM;EAAiB,MAAM,KAAK,UAAU,KAAK;EAAE,CAAC,EAAE;AACnG,KAAI,QAAW,QAAO,UAAU;AAChC,QAAO;;AAGT,SAAgB,gBAAgB,EAAE,MAAM,MAAM,WAA2C;AACvF,QAAO;EACL,SAAS;EACT,SAAS,CAAC;GACR,MAAM;GACN,MAAM,KAAK,UAAU;IAAE,SAAS;IAAO;IAAM;IAAM;IAAS,CAAC;GAC9D,CAAC;EACH;;AAGH,SAAgB,gBAAgB,OAAgC;AAC9D,KAAI,iBAAiB,eACnB,QAAO,eAAe;EAAE,SAAS;EAAO,MAAM,MAAM;EAAM,SAAS,MAAM;EAAS,MAAM,MAAM;EAAM,EAAE,KAAK;UAClG,iBAAiB,OAAO;AAGjC,UAAQ,MAAM,MAAM;AACpB,SAAO,eAAe;GAAE,SAAS;GAAO,MAAM,MAAM;GAAM,SAAS,MAAM;GAAS,EAAE,KAAK;QACpF;AAEL,UAAQ,MAAM,uBAAuB,MAAM;AAC3C,SAAO,eAAe;GAAE,SAAS;GAAO,MAAM;GAAiB,SAAS;GAA6B,EAAE,KAAK;;;AAIhH,SAAgB,qBAAqB,EAAE,YAA8B;AAEnE,QADc,UAAU,WAAY,OAAO,KAAK,SAAS,MAAM,SAAS,CAAC,SAAS,QAAQ,GAAG,SAAS"}
1
+ {"version":3,"file":"result-B_9Eo1ey.mjs","names":[],"sources":["../src/util/result.ts"],"sourcesContent":["import { EmbeddedResource, type CallToolResult } from '@modelcontextprotocol/sdk/types.js'\nimport { SilkweaveError } from '@silkweave/core'\nimport { randomUUID } from 'node:crypto'\n\nexport function smartToolResult(data: string | object | object[]): CallToolResult {\n const text = typeof data === 'string' ? data : JSON.stringify(data)\n const mimeType = typeof data === 'string' ? 'text/plain' : 'application/json'\n const ext = typeof data === 'string' ? 'txt' : 'json'\n if (text.length > 4096) {\n const uri = `mcp://toolResult/${randomUUID()}.${ext}`\n const buffer = Buffer.from(text)\n const blob = buffer.toString('base64')\n return {\n content: [\n { type: 'text', text: `Received resource ${uri} with ${buffer.byteLength} bytes` },\n { type: 'resource', resource: { uri, mimeType, blob } }\n ]\n }\n } else {\n return {\n content: [{ type: 'text' as const, text }]\n }\n }\n}\n\nexport function jsonToolResult(data: object, isError = false): CallToolResult {\n const result: CallToolResult = { content: [{ type: 'text' as const, text: JSON.stringify(data) }] }\n if (isError) { result.isError = true }\n return result\n}\n\nexport function errorToolResult({ code, name, message }: SilkweaveError): CallToolResult {\n return {\n isError: true,\n content: [{\n type: 'text',\n text: JSON.stringify({ success: false, code, name, message })\n }]\n }\n}\n\nexport function handleToolError(error: unknown): CallToolResult {\n if (error instanceof SilkweaveError) {\n return jsonToolResult({ success: false, name: error.name, message: error.message, code: error.code }, true)\n } else if (error instanceof Error) {\n // Log the full error (incl. stack) to stderr only - never put the stack trace\n // on the wire, where it would leak server internals to the MCP client.\n console.error(error)\n return jsonToolResult({ success: false, name: error.name, message: error.message }, true)\n } else {\n // Likewise keep the raw thrown value server-side - only a generic message goes out.\n console.error('Unknown tool error:', error)\n return jsonToolResult({ success: false, name: 'Unknown error', message: 'An unknown error occurred' }, true)\n }\n}\n\nexport function parseResourceMessage({ resource }: EmbeddedResource) {\n const text = ('blob' in resource) ? Buffer.from(resource.blob, 'base64').toString('utf-8') : resource.text\n return text\n}\n"],"mappings":";;;AAIA,SAAgB,gBAAgB,MAAkD;CAChF,MAAM,OAAO,OAAO,SAAS,WAAW,OAAO,KAAK,UAAU,KAAK;CACnE,MAAM,WAAW,OAAO,SAAS,WAAW,eAAe;CAC3D,MAAM,MAAM,OAAO,SAAS,WAAW,QAAQ;AAC/C,KAAI,KAAK,SAAS,MAAM;EACtB,MAAM,MAAM,oBAAoB,YAAY,CAAC,GAAG;EAChD,MAAM,SAAS,OAAO,KAAK,KAAK;EAChC,MAAM,OAAO,OAAO,SAAS,SAAS;AACtC,SAAO,EACL,SAAS,CACP;GAAE,MAAM;GAAQ,MAAM,qBAAqB,IAAI,QAAQ,OAAO,WAAW;GAAS,EAClF;GAAE,MAAM;GAAY,UAAU;IAAE;IAAK;IAAU;IAAM;GAAE,CACxD,EACF;OAED,QAAO,EACL,SAAS,CAAC;EAAE,MAAM;EAAiB;EAAM,CAAC,EAC3C;;AAIL,SAAgB,eAAe,MAAc,UAAU,OAAuB;CAC5E,MAAM,SAAyB,EAAE,SAAS,CAAC;EAAE,MAAM;EAAiB,MAAM,KAAK,UAAU,KAAK;EAAE,CAAC,EAAE;AACnG,KAAI,QAAW,QAAO,UAAU;AAChC,QAAO;;AAGT,SAAgB,gBAAgB,EAAE,MAAM,MAAM,WAA2C;AACvF,QAAO;EACL,SAAS;EACT,SAAS,CAAC;GACR,MAAM;GACN,MAAM,KAAK,UAAU;IAAE,SAAS;IAAO;IAAM;IAAM;IAAS,CAAC;GAC9D,CAAC;EACH;;AAGH,SAAgB,gBAAgB,OAAgC;AAC9D,KAAI,iBAAiB,eACnB,QAAO,eAAe;EAAE,SAAS;EAAO,MAAM,MAAM;EAAM,SAAS,MAAM;EAAS,MAAM,MAAM;EAAM,EAAE,KAAK;UAClG,iBAAiB,OAAO;AAGjC,UAAQ,MAAM,MAAM;AACpB,SAAO,eAAe;GAAE,SAAS;GAAO,MAAM,MAAM;GAAM,SAAS,MAAM;GAAS,EAAE,KAAK;QACpF;AAEL,UAAQ,MAAM,uBAAuB,MAAM;AAC3C,SAAO,eAAe;GAAE,SAAS;GAAO,MAAM;GAAiB,SAAS;GAA6B,EAAE,KAAK;;;AAIhH,SAAgB,qBAAqB,EAAE,YAA8B;AAEnE,QADc,UAAU,WAAY,OAAO,KAAK,SAAS,MAAM,SAAS,CAAC,SAAS,QAAQ,GAAG,SAAS"}
@@ -0,0 +1,2 @@
1
+ import { a as smartToolResult, c as requestFromExtra, i as parseResourceMessage, n as handleToolError, o as RegisterToolsOptions, r as jsonToolResult, s as registerTools, t as errorToolResult } from "./result-BVpEX7jU.mjs";
2
+ export { RegisterToolsOptions, errorToolResult, handleToolError, jsonToolResult, parseResourceMessage, registerTools, requestFromExtra, smartToolResult };
@@ -0,0 +1,3 @@
1
+ import { n as requestFromExtra, t as registerTools } from "./registerTools-DlguElKV.mjs";
2
+ import { a as smartToolResult, i as parseResourceMessage, n as handleToolError, r as jsonToolResult, t as errorToolResult } from "./result-B_9Eo1ey.mjs";
3
+ export { errorToolResult, handleToolError, jsonToolResult, parseResourceMessage, registerTools, requestFromExtra, smartToolResult };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@silkweave/mcp",
3
- "version": "2.6.1",
3
+ "version": "3.0.0",
4
4
  "description": "Silkweave MCP Package",
5
5
  "license": "MIT",
6
6
  "homepage": "https://www.silkweave.dev",
@@ -24,6 +24,11 @@
24
24
  "types": "./build/index.d.mts",
25
25
  "default": "./build/index.mjs"
26
26
  },
27
+ "./tools": {
28
+ "@silkweave/source": "./src/tools.ts",
29
+ "types": "./build/tools.d.mts",
30
+ "default": "./build/tools.mjs"
31
+ },
27
32
  "./cli-proxy": {
28
33
  "@silkweave/source": "./src/cliProxy.ts",
29
34
  "types": "./build/cliProxy.d.mts",
@@ -33,16 +38,16 @@
33
38
  "dependencies": {
34
39
  "@modelcontextprotocol/sdk": "^1.29.0",
35
40
  "change-case": "^5.4.4",
36
- "cors": "^2.8.6",
37
- "express": "^5.2.1",
38
41
  "zod": "^3.25.0",
39
- "@silkweave/auth": "2.6.1",
40
- "@silkweave/core": "2.6.1",
41
- "@silkweave/logger": "2.6.1"
42
+ "@silkweave/core": "3.0.0",
43
+ "@silkweave/logger": "3.0.0",
44
+ "@silkweave/auth": "3.0.0"
42
45
  },
43
46
  "peerDependencies": {
44
47
  "@clack/prompts": "^1.0.1",
45
- "commander": "^13.1.0"
48
+ "commander": "^13.1.0",
49
+ "cors": "^2.8.5",
50
+ "express": "^5.0.0"
46
51
  },
47
52
  "peerDependenciesMeta": {
48
53
  "@clack/prompts": {
@@ -50,23 +55,32 @@
50
55
  },
51
56
  "commander": {
52
57
  "optional": true
58
+ },
59
+ "cors": {
60
+ "optional": true
61
+ },
62
+ "express": {
63
+ "optional": true
53
64
  }
54
65
  },
55
66
  "devDependencies": {
56
67
  "@clack/prompts": "^1.0.1",
57
68
  "@eslint/js": "^10.0.1",
58
69
  "@modelcontextprotocol/inspector": "^0.21.1",
59
- "commander": "^13.1.0",
60
70
  "@stylistic/eslint-plugin": "^5.10.0",
61
71
  "@types/cors": "^2.8.19",
62
72
  "@types/express": "^5.0.6",
63
73
  "@types/node": "^22.0.0",
74
+ "commander": "^13.1.0",
75
+ "cors": "^2.8.6",
64
76
  "eslint": "^10.4.0",
77
+ "express": "^5.2.1",
65
78
  "rimraf": "^6.1.3",
66
79
  "tsdown": "^0.21.8",
67
80
  "tsx": "^4.21.0",
68
81
  "typescript": "^5.9.3",
69
- "typescript-eslint": "^8.56.1"
82
+ "typescript-eslint": "^8.56.1",
83
+ "vitest": "^4.1.9"
70
84
  },
71
85
  "scripts": {
72
86
  "clean": "rimraf build",
@@ -74,6 +88,7 @@
74
88
  "watch": "tsdown --watch",
75
89
  "typecheck": "tsc --noEmit",
76
90
  "lint": "eslint",
77
- "check": "pnpm lint && pnpm typecheck"
91
+ "check": "pnpm lint && pnpm typecheck",
92
+ "test": "vitest run"
78
93
  }
79
94
  }