@crewhaus/mcp-server 0.5.2 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/build.d.ts CHANGED
@@ -33,9 +33,16 @@ export declare function resolveSubAgentTools(subAgents: readonly McpSubAgentDesc
33
33
  /** The tool names a projection will register, without building a server. */
34
34
  export declare function computeToolNames(opts: CreateMcpServerOptions): readonly string[];
35
35
  /** Register the projected tools onto `server`; returns their names in order. */
36
- export declare function registerAgentTools(server: McpServer, opts: CreateMcpServerOptions): readonly string[];
37
- /** Mint a fresh SDK server with the projected tools registered. */
38
- export declare function buildConfiguredServer(opts: CreateMcpServerOptions): {
36
+ export declare function registerAgentTools(server: McpServer, opts: CreateMcpServerOptions, sessionId?: string): readonly string[];
37
+ /**
38
+ * Mint a fresh SDK server with the projected tools registered.
39
+ *
40
+ * `sessionId` is threaded into every {@link McpInvokeContext} this server's
41
+ * tools build. The SSE path mints one server PER SESSION and passes that
42
+ * session's id, so a projected bundle can key its own conversation state per
43
+ * caller; stdio passes nothing (one process, one conversation).
44
+ */
45
+ export declare function buildConfiguredServer(opts: CreateMcpServerOptions, sessionId?: string): {
39
46
  readonly server: McpServer;
40
47
  readonly toolNames: readonly string[];
41
48
  };
package/dist/build.js CHANGED
@@ -82,7 +82,7 @@ async function runInvoke(invoke, message, context) {
82
82
  }
83
83
  }
84
84
  /** Register the projected tools onto `server`; returns their names in order. */
85
- export function registerAgentTools(server, opts) {
85
+ export function registerAgentTools(server, opts, sessionId) {
86
86
  const agentName = opts.name ?? DEFAULT_NAME;
87
87
  const names = [];
88
88
  server.registerTool(CHAT_TOOL_NAME, {
@@ -90,7 +90,10 @@ export function registerAgentTools(server, opts) {
90
90
  description: opts.chatToolDescription ??
91
91
  `Send a message to the ${agentName} agent and receive its final response.`,
92
92
  inputSchema: { message: z.string().describe("The message to send to the agent.") },
93
- }, ({ message }) => runInvoke(opts.invoke, message, { toolName: CHAT_TOOL_NAME }));
93
+ }, ({ message }) => runInvoke(opts.invoke, message, {
94
+ toolName: CHAT_TOOL_NAME,
95
+ ...(sessionId !== undefined ? { sessionId } : {}),
96
+ }));
94
97
  names.push(CHAT_TOOL_NAME);
95
98
  if (resolveToolsMode(opts.tools) === "per-subagent") {
96
99
  for (const tool of resolveSubAgentTools(opts.subAgents ?? [])) {
@@ -100,15 +103,26 @@ export function registerAgentTools(server, opts) {
100
103
  inputSchema: {
101
104
  message: z.string().describe(`The message to route to the ${tool.subAgent} sub-agent.`),
102
105
  },
103
- }, ({ message }) => runInvoke(opts.invoke, message, { toolName: tool.toolName, subAgent: tool.subAgent }));
106
+ }, ({ message }) => runInvoke(opts.invoke, message, {
107
+ toolName: tool.toolName,
108
+ subAgent: tool.subAgent,
109
+ ...(sessionId !== undefined ? { sessionId } : {}),
110
+ }));
104
111
  names.push(tool.toolName);
105
112
  }
106
113
  }
107
114
  return names;
108
115
  }
109
- /** Mint a fresh SDK server with the projected tools registered. */
110
- export function buildConfiguredServer(opts) {
116
+ /**
117
+ * Mint a fresh SDK server with the projected tools registered.
118
+ *
119
+ * `sessionId` is threaded into every {@link McpInvokeContext} this server's
120
+ * tools build. The SSE path mints one server PER SESSION and passes that
121
+ * session's id, so a projected bundle can key its own conversation state per
122
+ * caller; stdio passes nothing (one process, one conversation).
123
+ */
124
+ export function buildConfiguredServer(opts, sessionId) {
111
125
  const server = new McpServer({ name: opts.name ?? DEFAULT_NAME, version: opts.version ?? DEFAULT_VERSION }, opts.instructions === undefined ? undefined : { instructions: opts.instructions });
112
- const toolNames = registerAgentTools(server, opts);
126
+ const toolNames = registerAgentTools(server, opts, sessionId);
113
127
  return { server, toolNames };
114
128
  }
package/dist/index.js CHANGED
@@ -60,9 +60,14 @@ function createSseServer(opts) {
60
60
  await session.server.close().catch(() => { });
61
61
  };
62
62
  const openSession = async (request) => {
63
- const { server } = buildConfiguredServer(opts);
63
+ // The id is minted HERE rather than by the transport's generator so the
64
+ // per-session server can be built with it: a projected bundle keys its
65
+ // own conversation state on this, and a server built before the id
66
+ // existed could only ever hand every caller the same one.
67
+ const sessionId = randomUUID();
68
+ const { server } = buildConfiguredServer(opts, sessionId);
64
69
  const transport = new WebStandardStreamableHTTPServerTransport({
65
- sessionIdGenerator: () => randomUUID(),
70
+ sessionIdGenerator: () => sessionId,
66
71
  onsessioninitialized: (id) => {
67
72
  sessions.set(id, { server, transport });
68
73
  },
package/dist/types.d.ts CHANGED
@@ -49,6 +49,19 @@ export declare const CHAT_TOOL_NAME = "chat";
49
49
  export interface McpInvokeContext {
50
50
  readonly toolName: string;
51
51
  readonly subAgent?: string;
52
+ /**
53
+ * The MCP transport's session id, on the `sse` transport once the client
54
+ * has initialized one. Absent on `stdio` (one process, one conversation)
55
+ * and on the very first request of an SSE session.
56
+ *
57
+ * It exists so a bundle that projects itself can key its OWN conversation
58
+ * state per CALLER: a channel daemon exposing `expose.mcp` maps this onto
59
+ * a harness session id, so two IDEs driving the same daemon do not write
60
+ * into one transcript. Without it every caller collapses onto a single
61
+ * shared session — which is not a subtle wrong, it is one user reading
62
+ * another's conversation.
63
+ */
64
+ readonly sessionId?: string;
52
65
  }
53
66
  /**
54
67
  * The injected delegate: run one agent turn for `message` and resolve with the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crewhaus/mcp-server",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
4
4
  "type": "module",
5
5
  "description": "Project a compiled bundle's turn function as an MCP server (stdio + SSE) — a chat/invoke tool plus optional per-sub-agent tools delegating to an injected invoke fn, built on @modelcontextprotocol/sdk",
6
6
  "main": "dist/index.js",
@@ -15,7 +15,7 @@
15
15
  "test": "bun test src"
16
16
  },
17
17
  "dependencies": {
18
- "@crewhaus/errors": "0.5.2",
18
+ "@crewhaus/errors": "0.5.4",
19
19
  "@modelcontextprotocol/sdk": "^1.29.0",
20
20
  "zod": "^3.23.8"
21
21
  },