@stigmer/mcp-server 3.2.2 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/cli/mcp-server-stigmer.js +9 -9
  2. package/config.d.ts +12 -0
  3. package/config.d.ts.map +1 -1
  4. package/config.js +5 -0
  5. package/config.js.map +1 -1
  6. package/domains/records/calls.d.ts +48 -0
  7. package/domains/records/calls.d.ts.map +1 -0
  8. package/domains/records/calls.js +118 -0
  9. package/domains/records/calls.js.map +1 -0
  10. package/domains/records/errors.d.ts +24 -0
  11. package/domains/records/errors.d.ts.map +1 -0
  12. package/domains/records/errors.js +79 -0
  13. package/domains/records/errors.js.map +1 -0
  14. package/domains/records/tools.d.ts +7 -0
  15. package/domains/records/tools.d.ts.map +1 -0
  16. package/domains/records/tools.js +135 -0
  17. package/domains/records/tools.js.map +1 -0
  18. package/gen/agent.d.ts +56 -0
  19. package/gen/agent.d.ts.map +1 -1
  20. package/gen/agent.js +24 -1
  21. package/gen/agent.js.map +1 -1
  22. package/gen/workflow.d.ts +4 -4
  23. package/gen/workflow.js +1 -1
  24. package/gen/workflow.js.map +1 -1
  25. package/index.d.ts +2 -2
  26. package/index.d.ts.map +1 -1
  27. package/index.js +4 -4
  28. package/index.js.map +1 -1
  29. package/package.json +3 -3
  30. package/server.d.ts +31 -1
  31. package/server.d.ts.map +1 -1
  32. package/server.js +41 -3
  33. package/server.js.map +1 -1
  34. package/src/config.ts +19 -0
  35. package/src/domains/records/calls.ts +209 -0
  36. package/src/domains/records/errors.ts +98 -0
  37. package/src/domains/records/records.integration.test.ts +313 -0
  38. package/src/domains/records/tools.ts +206 -0
  39. package/src/gen/agent.ts +28 -1
  40. package/src/gen/workflow.ts +1 -1
  41. package/src/http.integration.test.ts +1 -0
  42. package/src/index.ts +12 -5
  43. package/src/server.ts +59 -5
package/src/server.ts CHANGED
@@ -24,6 +24,7 @@ import { registerAgentTools } from "./domains/agents/tools.js";
24
24
  import type { BackendTarget } from "./domains/client.js";
25
25
  import { registerMcpServerResources } from "./domains/mcpservers/resources.js";
26
26
  import { registerMcpServerTools } from "./domains/mcpservers/tools.js";
27
+ import { registerRecordTools } from "./domains/records/tools.js";
27
28
  import { registerSearchTools } from "./domains/search/tools.js";
28
29
  import { registerSkillResources } from "./domains/skills/resources.js";
29
30
  import { registerSkillTools } from "./domains/skills/tools.js";
@@ -60,6 +61,22 @@ export function createServer(target: BackendTarget): McpServer {
60
61
  return server;
61
62
  }
62
63
 
64
+ /**
65
+ * Build a records-only MCP server: the five record tools with the
66
+ * agent-facing argument surface, and nothing else (T05 R1). This is
67
+ * the roster the runner-synthesized datastore attachment connects to —
68
+ * a structural guarantee that an agent session never sees the
69
+ * management tools (apply/delete/…) its empty approval maps would make
70
+ * approval-free. Served on the /records HTTP route and as the stdio
71
+ * roster when STIGMER_MCP_ROSTER=records.
72
+ */
73
+ export function createRecordsServer(target: BackendTarget): McpServer {
74
+ const server = new McpServer({ name: "mcp-server-stigmer-records", version: SERVER_VERSION });
75
+ const tools = registerRecordTools(server, target, "agent");
76
+ log.info("tools registered (records roster)", { count: tools.length, tools });
77
+ return server;
78
+ }
79
+
63
80
  /**
64
81
  * Wire up every domain's tools. Each domain returns the names it registered so
65
82
  * the startup log's count and roster cannot drift from what is actually wired,
@@ -75,6 +92,10 @@ function registerTools(server: McpServer, target: BackendTarget): void {
75
92
  ...registerValidateWorkflowYamlTool(server, target),
76
93
  ...registerTaskKindTools(server, target),
77
94
  ...registerWorkflowExecutionTools(server, target),
95
+ // The record tools also serve external MCP clients — as direct
96
+ // principals with the org argument and honest annotations (the
97
+ // agent-facing variant lives on the records-only roster).
98
+ ...registerRecordTools(server, target, "direct"),
78
99
  ];
79
100
  log.info("tools registered", { count: tools.length, tools });
80
101
  }
@@ -123,6 +144,25 @@ export async function serveStdio(server: McpServer, signal: AbortSignal): Promis
123
144
  /** Builds a fresh, fully-registered server. One is created per MCP session. */
124
145
  export type ServerFactory = () => McpServer;
125
146
 
147
+ /**
148
+ * Builds the server for an inbound HTTP `initialize` request, selected
149
+ * by request path. Only the initialize request consults the path — an
150
+ * established session's transport already carries the server it was
151
+ * built with, so follow-up requests dispatch by Mcp-Session-Id alone.
152
+ */
153
+ export type RouteServerFactory = (path: string) => McpServer;
154
+
155
+ /** HTTP route serving the records-only roster (T05 R1). */
156
+ export const RECORDS_ROUTE = "/records";
157
+
158
+ /**
159
+ * The standard HTTP route dispatch: the records-only roster on
160
+ * {@link RECORDS_ROUTE}, the full roster everywhere else.
161
+ */
162
+ export function routedServerFactory(target: BackendTarget): RouteServerFactory {
163
+ return (path) => (path === RECORDS_ROUTE ? createRecordsServer(target) : createServer(target));
164
+ }
165
+
126
166
  /**
127
167
  * Serve over Streamable HTTP until `signal` aborts.
128
168
  *
@@ -144,7 +184,11 @@ export type ServerFactory = () => McpServer;
144
184
  * DNS-rebinding allow-lists are intentionally out of parity scope (the Go server
145
185
  * has none).
146
186
  */
147
- export async function serveHttp(makeServer: ServerFactory, cfg: Config, signal: AbortSignal): Promise<void> {
187
+ export async function serveHttp(
188
+ makeServer: RouteServerFactory,
189
+ cfg: Config,
190
+ signal: AbortSignal,
191
+ ): Promise<void> {
148
192
  const sessions = new Map<string, StreamableHTTPServerTransport>();
149
193
 
150
194
  const httpServer = createHttpServer((req, res) => {
@@ -189,8 +233,8 @@ export async function serveBoth(target: BackendTarget, cfg: Config, signal: Abor
189
233
  signal.addEventListener("abort", onParentAbort, { once: true });
190
234
 
191
235
  const tasks = [
192
- serveStdio(createServer(target), linked.signal),
193
- serveHttp(() => createServer(target), cfg, linked.signal),
236
+ serveStdio(stdioServer(target, cfg), linked.signal),
237
+ serveHttp(routedServerFactory(target), cfg, linked.signal),
194
238
  ];
195
239
 
196
240
  try {
@@ -215,7 +259,7 @@ async function routeRequest(
215
259
  req: IncomingMessage & { auth?: AuthInfo },
216
260
  res: ServerResponse,
217
261
  sessions: Map<string, StreamableHTTPServerTransport>,
218
- makeServer: ServerFactory,
262
+ makeServer: RouteServerFactory,
219
263
  cfg: Config,
220
264
  ): Promise<void> {
221
265
  if (req.method === "GET" && req.url === "/health") {
@@ -247,6 +291,7 @@ async function routeRequest(
247
291
  req.auth = { token, clientId: "stigmer-mcp-passthrough", scopes: [] };
248
292
  }
249
293
 
294
+ const path = requestPath(req);
250
295
  const sessionId = headerValue(req, "mcp-session-id");
251
296
 
252
297
  // Established session → dispatch to its transport.
@@ -288,10 +333,19 @@ async function routeRequest(
288
333
  if (transport.sessionId !== undefined) sessions.delete(transport.sessionId);
289
334
  };
290
335
 
291
- await makeServer().connect(transport);
336
+ await makeServer(path).connect(transport);
292
337
  await transport.handleRequest(req, res, body);
293
338
  }
294
339
 
340
+ /**
341
+ * The stdio server for the configured roster: the records-only roster
342
+ * when STIGMER_MCP_ROSTER=records (what the OSS runner-synthesized
343
+ * datastore attachment spawns), the full roster otherwise.
344
+ */
345
+ export function stdioServer(target: BackendTarget, cfg: Config): McpServer {
346
+ return cfg.roster === "records" ? createRecordsServer(target) : createServer(target);
347
+ }
348
+
295
349
  /** Return a single header value, collapsing the array form Node may produce. */
296
350
  function headerValue(req: IncomingMessage, name: string): string | undefined {
297
351
  const v = req.headers[name];