Files
TREK/server/src/mcp/tools.ts
T
Maurice 8632f93c37 feat(server): correlate log lines with a request id per entry point
A 5xx stack and its access-log line were two unrelated lines, the
exception filter printed the stack with console.error and no request
context, and the MCP, WebSocket, plugin RPC and cron entry points logged
nothing per call. A self-hosted bug report could not be traced from a
failure back to the call that caused it.

Every HTTP request now runs under an id held in AsyncLocalStorage: a
well-formed incoming X-Request-Id is kept, anything else is replaced by a
UUID, and the response carries it in X-Request-Id. The logger tags every
line written inside a unit of work with `[http <id>]`. The exception
filter leaves a server-side failure on res.locals and the access log
writes the request line and the stack as one entry under that id; without
an access log watching the response it still prints the error itself.

MCP tool, resource and prompt calls (through a new `around` seam on the
registry), WebSocket messages, plugin RPC dispatches and cron ticks each
run through traceEntry(), which gives the call its own id (remembering the
outer one as its parent) and logs one line for it: debug on success, like
the HTTP access log, and warn or error with the reason on failure. The
call's result and timing are unchanged, a failed cron tick is logged once
rather than twice, and a WebSocket handler rejection no longer reaches
rxjs's asynchronous rethrow.
2026-10-08 18:40:15 +02:00

24 lines
1.5 KiB
TypeScript

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp';
import type { McpAttachOptions, McpRegistry } from '../nest-mcp';
import { getPluginMcpToolSource } from '../plugin-mcp-tools';
/**
* Attaches the MCP surface to a session's server.
*
* Every domain registers through an @McpController class the container
* discovers, so there is nothing to wire here by hand any more. What is left
* is the seam that supplies the default arguments and the null-registry
* escape hatch. Production passes the container-discovered McpRegistryService
* (injected into the transport service); the no-Nest harness hands in
* createTestRegistry()'s build. A null registry (direct callers without either)
* skips the attach.
*
* `dynamicTools` defaults to the process-level plugin source rather than being
* passed down from the transport, so mcp-transport/ never learns that plugins
* exist and an app built without the plugins runtime resolves it to undefined.
* Tests pass it explicitly instead of reaching for the global.
*/
export async function registerTools(registry: McpRegistry | null, server: McpServer, userId: number, scopes: string[] | null, isStaticToken = false, getDeprecationNotice: () => string | null = () => null, onInvoke?: McpAttachOptions['onInvoke'], dynamicTools: McpAttachOptions['dynamicTools'] = getPluginMcpToolSource() ?? undefined, around?: McpAttachOptions['around']): Promise<void> {
if (registry) await registry.attach(server, { userId, scopes, isStaticToken, getDeprecationNotice }, { onInvoke, dynamicTools, around });
}