@stigmer/mcp-server 3.5.2 → 3.6.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.
@@ -26,7 +26,7 @@ export const McpServerInputShape = {
26
26
  stdio: z.lazy(() => StdioServerConfigInputSchema).optional().describe("stdio-based server (subprocess with stdin/stdout communication). Most common type - used for Node.js, Python, and other CLI-based MCP servers."),
27
27
  http: z.lazy(() => HttpServerConfigInputSchema).optional().describe("HTTP-based server (HTTP + Server-Sent Events communication). Used for remote/managed MCP services accessible over the network."),
28
28
  default_enabled_tools: z.array(z.string()).optional().describe("Default tools to enable from this MCP server. Empty list means all tools are enabled by default. @internal Tool names must match exactly what the MCP server reports via tools/list. Only names from discovered_capabilities.tools are valid here. Do NOT include names from discovered_capabilities.resource_templates — resource templates are read-only data endpoints, not callable tools. Including a resource template name here causes a fatal runtime error."),
29
- env: z.record(z.lazy(() => EnvVarDeclarationInputSchema)).optional().describe("Environment variable declarations for this MCP server. Keys are variable names; values describe their metadata and optionality."),
29
+ env: z.record(z.lazy(() => EnvVarDeclarationInputSchema)).optional().describe("Environment variable declarations for this MCP server. Keys are variable names; values describe their metadata and optionality. @internal Reserved platform keys — declared here like any other variable, but their values are injected by the runner per resolution context and are authoritative over same-named user env entries: STIGMER_CALLER_IDENTITY_KIND — the verified caller's kind token ('whatsapp_phone', 'slack_user_id', 'stigmer_user', 'anonymous') STIGMER_CALLER_IDENTITY_VALUE — the identity value (wa_id, user id, email); empty for anonymous STIGMER_SESSION_ID — the session the identity was resolved for; empty outside a session Reserved keys MUST be declared 'optional: true': they have no value at execution-create time, and a required declaration fails the pipeline's env-completeness validation before the runner ever injects them. Discovery (the connect workflow) runs with no session and injects the anonymous sentinel — a server consuming these keys must answer tools/list for anonymous callers and gate tool CALLS instead. The injected header is runner-asserted, not signed: pair it with a shared secret and treat it as trustworthy only for servers you operate."),
30
30
  pinned_tool_approvals: z.array(z.lazy(() => ToolApprovalPolicyInputSchema)).optional().describe("Manual tool approval overrides set by the MCP server owner. @internal These take precedence over system-generated 'McpServerStatus.tool_approvals'. Never auto-modified — only changed by explicit user action (apply/update). Use cases: - Force approval for a tool the classifier marked as auto-approve - Exempt a safe tool the classifier flagged as needing approval - Establish organization-wide safety policies for dangerous tools Policy chain (lowest to highest priority): 1. McpServerStatus.tool_approvals - System-generated defaults 2. McpServerSpec.pinned_tool_approvals - Manual overrides (this field) 3. Agent.McpServerUsage.tool_approval_overrides - Per-agent customization 4. AgentExecution.auto_approve_all - Runtime bypass"),
31
31
  repository_url: z.string().optional().describe("URL of the upstream source repository for this MCP server. Shown in the marketplace so users can inspect the implementation for trust and transparency. Example: 'https://github.com/modelcontextprotocol/servers'"),
32
32
  github_stars: z.number().optional().describe("GitHub star count at the time of curation. Used as a popularity signal in marketplace display. 0 if unknown or non-GitHub repository."),
@@ -45,7 +45,7 @@ type StdioServerConfigInput = z.infer<typeof StdioServerConfigInputSchema>;
45
45
 
46
46
  const HttpServerConfigInputSchema = z.object({
47
47
  url: z.string().describe("Base URL of the MCP server endpoint. Must be a valid HTTP or HTTPS URL. Examples: - 'http://localhost:3000/mcp' - 'https://mcp.example.com/v1' - 'https://api.company.com/mcp/github'"),
48
- headers: z.record(z.string()).optional().describe("HTTP headers to include with every request. Use for authentication, API versioning, or custom routing. Header values can reference environment variables using ${VAR_NAME} syntax. These placeholders are resolved at runtime from AgentInstance's environment. Examples: 'Authorization': 'Bearer ${API_TOKEN}' 'X-API-Version': '2024-01' 'X-Tenant-ID': '${TENANT_ID}'"),
48
+ headers: z.record(z.string()).optional().describe("HTTP headers to include with every request. Use for authentication, API versioning, or custom routing. Header values can reference environment variables using ${VAR_NAME} syntax. These placeholders are resolved at runtime from AgentInstance's environment. Examples: 'Authorization': 'Bearer ${API_TOKEN}' 'X-API-Version': '2024-01' 'X-Tenant-ID': '${TENANT_ID}' @internal Templates may also reference the reserved caller-identity keys (STIGMER_CALLER_IDENTITY_KIND / _VALUE, STIGMER_SESSION_ID) when the server declares them in spec.env — see the env field's reserved-key contract. Placeholders resolve against the env FILTERED to declared keys, so an undeclared reserved key in a template fails resolution."),
49
49
  query_params: z.record(z.string()).optional().describe("Query parameters to append to the URL. Values can reference environment variables using ${VAR_NAME} syntax. Examples: 'region': '${AWS_REGION}' 'version': 'v1'"),
50
50
  timeout_seconds: z.number().optional().describe("Timeout for HTTP requests in seconds. Applies to both the initial connection and response streaming. Default: 30 seconds if not specified. Set higher values for MCP servers that perform long-running operations."),
51
51
  });
package/src/server.ts CHANGED
@@ -22,6 +22,7 @@ import type { Config } from "./config.js";
22
22
  import { registerAgentExecutionTools } from "./domains/agentexecutions/tools.js";
23
23
  import { registerAgentResources } from "./domains/agents/resources.js";
24
24
  import { registerAgentTools } from "./domains/agents/tools.js";
25
+ import { registerChannelTools } from "./domains/channels/tools.js";
25
26
  import type { BackendTarget } from "./domains/client.js";
26
27
  import { registerDatastoreResources } from "./domains/datastores/resources.js";
27
28
  import { registerDatastoreTools } from "./domains/datastores/tools.js";
@@ -83,6 +84,21 @@ export function createRecordsServer(target: BackendTarget): McpServer {
83
84
  return server;
84
85
  }
85
86
 
87
+ /**
88
+ * Build a channels-only MCP server: send_channel_message with the
89
+ * agent-facing argument surface, and nothing else (proactive-messaging
90
+ * DD-006 D8 — the records-roster pattern). This is the roster the
91
+ * runner-synthesized channel attachment connects to; the structural
92
+ * guarantee mirrors the records roster's. Served on the /channels HTTP
93
+ * route and as the stdio roster when STIGMER_MCP_ROSTER=channels.
94
+ */
95
+ export function createChannelsServer(target: BackendTarget): McpServer {
96
+ const server = new McpServer({ name: "mcp-server-stigmer-channels", version: SERVER_VERSION });
97
+ const tools = registerChannelTools(server, target);
98
+ log.info("tools registered (channels roster)", { count: tools.length, tools });
99
+ return server;
100
+ }
101
+
86
102
  /**
87
103
  * Wire up every domain's tools. Each domain returns the names it registered so
88
104
  * the startup log's count and roster cannot drift from what is actually wired,
@@ -167,12 +183,20 @@ export type RouteServerFactory = (path: string) => McpServer;
167
183
  /** HTTP route serving the records-only roster (T05 R1). */
168
184
  export const RECORDS_ROUTE = "/records";
169
185
 
186
+ /** HTTP route serving the channels-only roster (DD-006 D8). */
187
+ export const CHANNELS_ROUTE = "/channels";
188
+
170
189
  /**
171
190
  * The standard HTTP route dispatch: the records-only roster on
172
- * {@link RECORDS_ROUTE}, the full roster everywhere else.
191
+ * {@link RECORDS_ROUTE}, the channels-only roster on
192
+ * {@link CHANNELS_ROUTE}, the full roster everywhere else.
173
193
  */
174
194
  export function routedServerFactory(target: BackendTarget): RouteServerFactory {
175
- return (path) => (path === RECORDS_ROUTE ? createRecordsServer(target) : createServer(target));
195
+ return (path) => {
196
+ if (path === RECORDS_ROUTE) return createRecordsServer(target);
197
+ if (path === CHANNELS_ROUTE) return createChannelsServer(target);
198
+ return createServer(target);
199
+ };
176
200
  }
177
201
 
178
202
  /**
@@ -350,12 +374,14 @@ async function routeRequest(
350
374
  }
351
375
 
352
376
  /**
353
- * The stdio server for the configured roster: the records-only roster
354
- * when STIGMER_MCP_ROSTER=records (what the OSS runner-synthesized
355
- * datastore attachment spawns), the full roster otherwise.
377
+ * The stdio server for the configured roster: the records-only or
378
+ * channels-only roster when STIGMER_MCP_ROSTER names one (what the OSS
379
+ * runner-synthesized attachments spawn), the full roster otherwise.
356
380
  */
357
381
  export function stdioServer(target: BackendTarget, cfg: Config): McpServer {
358
- return cfg.roster === "records" ? createRecordsServer(target) : createServer(target);
382
+ if (cfg.roster === "records") return createRecordsServer(target);
383
+ if (cfg.roster === "channels") return createChannelsServer(target);
384
+ return createServer(target);
359
385
  }
360
386
 
361
387
  /** Return a single header value, collapsing the array form Node may produce. */