@nexusm/mcp-server 0.1.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 (44) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +13 -0
  3. package/RUNBOOK.md +190 -0
  4. package/dist/auth.d.ts +49 -0
  5. package/dist/auth.d.ts.map +1 -0
  6. package/dist/auth.js +62 -0
  7. package/dist/auth.js.map +1 -0
  8. package/dist/errors.d.ts +211 -0
  9. package/dist/errors.d.ts.map +1 -0
  10. package/dist/errors.js +245 -0
  11. package/dist/errors.js.map +1 -0
  12. package/dist/index.d.ts +28 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +146 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/metrics.d.ts +146 -0
  17. package/dist/metrics.d.ts.map +1 -0
  18. package/dist/metrics.js +245 -0
  19. package/dist/metrics.js.map +1 -0
  20. package/dist/tools/context.d.ts +48 -0
  21. package/dist/tools/context.d.ts.map +1 -0
  22. package/dist/tools/context.js +229 -0
  23. package/dist/tools/context.js.map +1 -0
  24. package/dist/tools/index.d.ts +12 -0
  25. package/dist/tools/index.d.ts.map +1 -0
  26. package/dist/tools/index.js +19 -0
  27. package/dist/tools/index.js.map +1 -0
  28. package/dist/tools/memory_create.d.ts +37 -0
  29. package/dist/tools/memory_create.d.ts.map +1 -0
  30. package/dist/tools/memory_create.js +242 -0
  31. package/dist/tools/memory_create.js.map +1 -0
  32. package/dist/tools/memory_feedback.d.ts +44 -0
  33. package/dist/tools/memory_feedback.d.ts.map +1 -0
  34. package/dist/tools/memory_feedback.js +259 -0
  35. package/dist/tools/memory_feedback.js.map +1 -0
  36. package/dist/tools/memory_search.d.ts +44 -0
  37. package/dist/tools/memory_search.d.ts.map +1 -0
  38. package/dist/tools/memory_search.js +160 -0
  39. package/dist/tools/memory_search.js.map +1 -0
  40. package/dist/tools/types.d.ts +36 -0
  41. package/dist/tools/types.d.ts.map +1 -0
  42. package/dist/tools/types.js +29 -0
  43. package/dist/tools/types.js.map +1 -0
  44. package/package.json +50 -0
package/dist/errors.js ADDED
@@ -0,0 +1,245 @@
1
+ /**
2
+ * Error contract and HTTP→MCP mapping for the Nexusm MCP server.
3
+ *
4
+ * Wave 1 (TASK-007): declared the type surface — NexusError, McpErrorCode,
5
+ * interface stubs for AuthError / NetworkError / CancelError.
6
+ * Wave 2B (TASK-013): implements the full HTTP-status → MCP-error-code
7
+ * mapping (proposal §M-3) via `mapHttpStatusToMcpError` and
8
+ * `isAxiosLikeError`.
9
+ *
10
+ * SECURITY (matches auth.ts discipline):
11
+ * `toJSON()` deliberately omits `cause` and `stack`. An axios-style error
12
+ * attached as `cause` typically carries the original request config
13
+ * including the `Authorization: Bearer <token>` header. Leaking that via a
14
+ * JSON.stringify of a NexusError would defeat the token-redaction guarantee
15
+ * of auth.ts. If callers need to inspect the cause they must do so
16
+ * explicitly, not via serialization.
17
+ */
18
+ /**
19
+ * MCP / JSON-RPC error codes surfaced by this server.
20
+ *
21
+ * Values mirror `@modelcontextprotocol/sdk` `ErrorCode` enum
22
+ * (`dist/esm/types.d.ts`). We re-declare locally rather than re-export
23
+ * the SDK enum so that:
24
+ * 1. errors.ts has zero runtime import from the SDK (keeps the
25
+ * contract layer independent of SDK version churn)
26
+ * 2. TASK-013's mapping logic and tests have a single source of truth
27
+ * for which codes this server is allowed to emit
28
+ *
29
+ * Scope decision (resolved ambiguity from spec):
30
+ * We enumerate the four JSON-RPC standard codes required by §M-3
31
+ * (`InvalidRequest`, `MethodNotFound`, `InvalidParams`, `InternalError`),
32
+ * plus `ParseError` (-32700) for completeness of the JSON-RPC base
33
+ * set, plus `ConnectionClosed` (-32000) and `RequestTimeout` (-32001)
34
+ * which the SDK defines and which `NetworkError` / `CancelError`
35
+ * downstream mappings will need. UrlElicitationRequired (-32042) is
36
+ * intentionally omitted — not in scope for Wave 1 / Wave 2.
37
+ */
38
+ export var McpErrorCode;
39
+ (function (McpErrorCode) {
40
+ // JSON-RPC standard (https://www.jsonrpc.org/specification#error_object)
41
+ McpErrorCode[McpErrorCode["ParseError"] = -32700] = "ParseError";
42
+ McpErrorCode[McpErrorCode["InvalidRequest"] = -32600] = "InvalidRequest";
43
+ McpErrorCode[McpErrorCode["MethodNotFound"] = -32601] = "MethodNotFound";
44
+ McpErrorCode[McpErrorCode["InvalidParams"] = -32602] = "InvalidParams";
45
+ McpErrorCode[McpErrorCode["InternalError"] = -32603] = "InternalError";
46
+ // MCP SDK extensions used by this server's error taxonomy
47
+ McpErrorCode[McpErrorCode["ConnectionClosed"] = -32000] = "ConnectionClosed";
48
+ McpErrorCode[McpErrorCode["RequestTimeout"] = -32001] = "RequestTimeout";
49
+ /**
50
+ * TASK-013 additions: custom codes in the application-defined range
51
+ * (-32099..-32000 is reserved for implementation; we use the next
52
+ * available slots above -32000 for semantic clarity).
53
+ *
54
+ * Unauthorized (-32011): 401 / 403 from Nexus REST — semantically distinct
55
+ * from InvalidRequest (-32600) so clients can detect auth failures without
56
+ * parsing the message string.
57
+ * RateLimited (-32012): 429 Retry-After. Clients should honor
58
+ * `data.retry_after_seconds` before retrying.
59
+ */
60
+ McpErrorCode[McpErrorCode["Unauthorized"] = -32011] = "Unauthorized";
61
+ McpErrorCode[McpErrorCode["RateLimited"] = -32012] = "RateLimited";
62
+ })(McpErrorCode || (McpErrorCode = {}));
63
+ /**
64
+ * Base error for all errors this MCP server emits.
65
+ *
66
+ * Carries enough structured context to translate a thrown `NexusError` into a
67
+ * JSON-RPC error response without re-inspecting the underlying axios / SDK
68
+ * error.
69
+ */
70
+ export class NexusError extends Error {
71
+ /**
72
+ * Upstream HTTP status (Nexus REST), or `null` when the error did not
73
+ * originate from an HTTP response (e.g. DNS failure, abort, internal
74
+ * invariant violation).
75
+ */
76
+ httpStatus;
77
+ /** MCP/JSON-RPC error code that this error will surface as. */
78
+ mcpErrorCode;
79
+ /**
80
+ * Whether the MCP client may safely retry this request.
81
+ * Populated by `mapHttpStatusToMcpError` and the NLI/network helpers.
82
+ */
83
+ retryable;
84
+ /**
85
+ * Additional structured data surfaced in the JSON-RPC `error.data` field.
86
+ * Safe to serialize — must never contain auth tokens or raw SDK internals.
87
+ * Populated by the mapping layer (e.g. `retry_after_seconds`, `network`,
88
+ * `timeout`).
89
+ */
90
+ data;
91
+ /**
92
+ * Underlying cause. Per ES2022 `Error.cause`. **Not serialized** by
93
+ * `toJSON()` — see file header SECURITY note.
94
+ */
95
+ cause;
96
+ constructor(message, mcpErrorCode, httpStatus = null, cause, options) {
97
+ super(message);
98
+ this.name = 'NexusError';
99
+ this.mcpErrorCode = mcpErrorCode;
100
+ this.httpStatus = httpStatus;
101
+ this.retryable = options?.retryable ?? false;
102
+ if (options?.data !== undefined) {
103
+ this.data = options.data;
104
+ }
105
+ if (cause !== undefined) {
106
+ this.cause = cause;
107
+ }
108
+ // Restore prototype chain — required when extending Error under
109
+ // some TS target/module combinations (defensive; cheap).
110
+ Object.setPrototypeOf(this, new.target.prototype);
111
+ }
112
+ /**
113
+ * Safe serialization. Deliberately omits `cause` and `stack` to
114
+ * prevent accidental token leakage if a caller logs the JSON form.
115
+ *
116
+ * `data` IS included — it is caller-controlled structured metadata that
117
+ * must never contain raw SDK objects (that would be caught during review
118
+ * of `mapHttpStatusToMcpError` callers).
119
+ */
120
+ toJSON() {
121
+ const base = {
122
+ name: this.name,
123
+ message: this.message,
124
+ httpStatus: this.httpStatus,
125
+ mcpErrorCode: this.mcpErrorCode,
126
+ };
127
+ if (this.data !== undefined) {
128
+ base.data = this.data;
129
+ }
130
+ return base;
131
+ }
132
+ }
133
+ /**
134
+ * Type guard for axios-compatible errors thrown by `@nexusm/sdk`.
135
+ *
136
+ * Matches any object with `isAxiosError === true`, which is the canonical
137
+ * axios duck-type flag. This guard intentionally does NOT import axios — it
138
+ * keeps `errors.ts` free of SDK runtime dependencies.
139
+ */
140
+ export function isAxiosLikeError(err) {
141
+ return (typeof err === 'object' &&
142
+ err !== null &&
143
+ err['isAxiosError'] === true);
144
+ }
145
+ /**
146
+ * Parse a Retry-After header value into seconds.
147
+ *
148
+ * Handles both integer-seconds form ("60") and HTTP-date form
149
+ * ("Wed, 21 Oct 2026 07:28:00 GMT"). Returns `undefined` if the header
150
+ * is absent or unparseable — callers should degrade gracefully.
151
+ */
152
+ function parseRetryAfterSeconds(headers) {
153
+ if (headers === undefined)
154
+ return undefined;
155
+ const raw = headers['retry-after'] ?? headers['Retry-After'];
156
+ const value = Array.isArray(raw) ? raw[0] : raw;
157
+ if (value === undefined)
158
+ return undefined;
159
+ // Integer seconds
160
+ const asInt = parseInt(value, 10);
161
+ if (!Number.isNaN(asInt) && String(asInt) === value.trim()) {
162
+ return asInt;
163
+ }
164
+ // HTTP-date: compute delta from now
165
+ const ts = Date.parse(value);
166
+ if (!Number.isNaN(ts)) {
167
+ const delta = Math.ceil((ts - Date.now()) / 1000);
168
+ return delta > 0 ? delta : 0;
169
+ }
170
+ return undefined;
171
+ }
172
+ /**
173
+ * Canonical entrypoint for converting an upstream HTTP response (or SDK
174
+ * network/timeout error) into a typed `NexusError` with the correct
175
+ * `McpErrorCode`, `retryable` flag, and `data` extras.
176
+ *
177
+ * Proposal §M-3 mapping table:
178
+ *
179
+ * | httpStatus | McpErrorCode | retryable | data extras |
180
+ * |-------------------------|-------------------|-----------|--------------------------|
181
+ * | 401, 403 | Unauthorized | false | — |
182
+ * | 404 | MethodNotFound | false | — |
183
+ * | 422 | InvalidParams | false | — |
184
+ * | 429 | RateLimited | true* | retry_after_seconds?: n |
185
+ * | 503 | ConnectionClosed | true | — |
186
+ * | 5xx (else) | InternalError | true | — |
187
+ * | null + network=true | InternalError | true | network: true |
188
+ * | null + timeout=true | RequestTimeout | true | timeout: true |
189
+ *
190
+ * *429: retryable "after Retry-After header elapses" — we set retryable=true
191
+ * and populate `data.retry_after_seconds` so clients can honour the window.
192
+ *
193
+ * Note: HTTP 200 + body.errors != null is NOT an error; that is the
194
+ * partial-degradation path handled in tool handlers (see context.ts). This
195
+ * function is only invoked on non-2xx responses or SDK error throws.
196
+ *
197
+ * @param httpStatus HTTP status code, or `null` for non-HTTP errors.
198
+ * @param body Raw response body (typed `unknown`; we do not parse it).
199
+ * @param headers Response headers, used only to extract Retry-After on 429.
200
+ */
201
+ export function mapHttpStatusToMcpError(httpStatus, body, headers) {
202
+ // Non-HTTP origin: distinguish timeout from generic network failure by
203
+ // inspecting whether the caller passed { timeout: true } in body (we
204
+ // treat `body` as a hint bag for non-HTTP paths).
205
+ if (httpStatus === null) {
206
+ const hint = body;
207
+ if (hint?.['timeout'] === true) {
208
+ return new NexusError('Request timed out', McpErrorCode.RequestTimeout, null, undefined, {
209
+ retryable: true,
210
+ data: { timeout: true },
211
+ });
212
+ }
213
+ return new NexusError('Network error', McpErrorCode.InternalError, null, undefined, {
214
+ retryable: true,
215
+ data: { network: true },
216
+ });
217
+ }
218
+ switch (true) {
219
+ case httpStatus === 401 || httpStatus === 403:
220
+ return new NexusError(`Unauthorized (HTTP ${httpStatus})`, McpErrorCode.Unauthorized, httpStatus, undefined, { retryable: false });
221
+ case httpStatus === 404:
222
+ return new NexusError('Resource not found (HTTP 404)', McpErrorCode.MethodNotFound, 404, undefined, { retryable: false });
223
+ case httpStatus === 422:
224
+ return new NexusError('Invalid parameters (HTTP 422)', McpErrorCode.InvalidParams, 422, undefined, { retryable: false });
225
+ case httpStatus === 429: {
226
+ const retryAfterSeconds = parseRetryAfterSeconds(headers);
227
+ const data = {};
228
+ if (retryAfterSeconds !== undefined) {
229
+ data['retry_after_seconds'] = retryAfterSeconds;
230
+ }
231
+ return new NexusError('Rate limited (HTTP 429)', McpErrorCode.RateLimited, 429, undefined, {
232
+ retryable: true,
233
+ data: Object.keys(data).length > 0 ? data : undefined,
234
+ });
235
+ }
236
+ case httpStatus === 503:
237
+ return new NexusError('Service unavailable (HTTP 503)', McpErrorCode.ConnectionClosed, 503, undefined, { retryable: true });
238
+ case httpStatus >= 500:
239
+ return new NexusError(`Internal server error (HTTP ${httpStatus})`, McpErrorCode.InternalError, httpStatus, undefined, { retryable: true });
240
+ default:
241
+ // Catch-all for unexpected non-2xx codes not in the table.
242
+ return new NexusError(`Unexpected HTTP error (status=${httpStatus})`, McpErrorCode.InternalError, httpStatus, undefined, { retryable: false });
243
+ }
244
+ }
245
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @nexusm/mcp-server — MCP server entry point.
4
+ *
5
+ * Exposes 4 MVP tools (nexus.context_retrieve, nexus.memory_search,
6
+ * nexus.memory_create, nexus.memory_feedback) over the Model Context
7
+ * Protocol. Schemas are locked in
8
+ * `openspec/changes/us-037-mcp-server-exposure/proposal.md` §"R2 工具 Schema 锁定".
9
+ *
10
+ * Transport selection (proposal §M-14 dual transport):
11
+ * - Default: stdio (StdioServerTransport)
12
+ * - Optional: Streamable HTTP via env var `NEXUS_MCP_TRANSPORT=http`
13
+ *
14
+ * SDK package note: package.json depends on `@modelcontextprotocol/sdk`
15
+ * ^1.29 (v1 single package). The proposal §52 mentions a future v2 split
16
+ * (`@modelcontextprotocol/server` + `@modelcontextprotocol/node`); when the
17
+ * SDK migrates, update the import subpaths below — schema definitions and
18
+ * handler shapes are insulated in `src/tools/`.
19
+ *
20
+ * Logging discipline: stdio transport multiplexes JSON-RPC over stdin/stdout,
21
+ * so ALL diagnostic output must go to stderr (`console.error`). Never write
22
+ * to stdout outside the transport.
23
+ *
24
+ * Wave 1 (TASK-003): tool handlers return NOT_IMPLEMENTED. Wave 1+ (TASK-007..010)
25
+ * wires each handler to nexus-sdk-js. Auth (TASK-004) lives in src/auth.ts.
26
+ */
27
+ export {};
28
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG"}
package/dist/index.js ADDED
@@ -0,0 +1,146 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @nexusm/mcp-server — MCP server entry point.
4
+ *
5
+ * Exposes 4 MVP tools (nexus.context_retrieve, nexus.memory_search,
6
+ * nexus.memory_create, nexus.memory_feedback) over the Model Context
7
+ * Protocol. Schemas are locked in
8
+ * `openspec/changes/us-037-mcp-server-exposure/proposal.md` §"R2 工具 Schema 锁定".
9
+ *
10
+ * Transport selection (proposal §M-14 dual transport):
11
+ * - Default: stdio (StdioServerTransport)
12
+ * - Optional: Streamable HTTP via env var `NEXUS_MCP_TRANSPORT=http`
13
+ *
14
+ * SDK package note: package.json depends on `@modelcontextprotocol/sdk`
15
+ * ^1.29 (v1 single package). The proposal §52 mentions a future v2 split
16
+ * (`@modelcontextprotocol/server` + `@modelcontextprotocol/node`); when the
17
+ * SDK migrates, update the import subpaths below — schema definitions and
18
+ * handler shapes are insulated in `src/tools/`.
19
+ *
20
+ * Logging discipline: stdio transport multiplexes JSON-RPC over stdin/stdout,
21
+ * so ALL diagnostic output must go to stderr (`console.error`). Never write
22
+ * to stdout outside the transport.
23
+ *
24
+ * Wave 1 (TASK-003): tool handlers return NOT_IMPLEMENTED. Wave 1+ (TASK-007..010)
25
+ * wires each handler to nexus-sdk-js. Auth (TASK-004) lives in src/auth.ts.
26
+ */
27
+ import { createRequire } from 'node:module';
28
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
29
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
30
+ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
31
+ import { tools, toolsByName } from './tools/index.js';
32
+ import { startMetricsServer, emitToolCall, emitToolDuration, emitToolsList } from './metrics.js';
33
+ /**
34
+ * Extract the calling MCP client identifier from a request, with fallback.
35
+ *
36
+ * MCP `request._meta.clientInfo.name` is the standard JSON-RPC carrier when
37
+ * the client cooperates (Claude Code, Cursor, mcp-cli all set it). When the
38
+ * field is missing, fall back to env var `NEXUS_MCP_CLIENT_NAME` (set by
39
+ * launchers that wrap nexusm-mcp-server) and finally `unknown`. Cardinality
40
+ * is guarded inside `metrics.ts` via a whitelist (R2 ai D-5).
41
+ */
42
+ function extractClient(request) {
43
+ const fromMeta = request._meta?.clientInfo?.name;
44
+ if (typeof fromMeta === 'string' && fromMeta.length > 0) {
45
+ return fromMeta;
46
+ }
47
+ const fromEnv = process.env.NEXUS_MCP_CLIENT_NAME;
48
+ return fromEnv && fromEnv.length > 0 ? fromEnv : 'unknown';
49
+ }
50
+ // Read version from package.json without `import assert` (Node 18+ compat).
51
+ const require = createRequire(import.meta.url);
52
+ const pkg = require('../package.json');
53
+ const SERVER_NAME = 'nexus';
54
+ const SERVER_VERSION = pkg.version;
55
+ function createServer() {
56
+ const server = new Server({ name: SERVER_NAME, version: SERVER_VERSION }, { capabilities: { tools: {} } });
57
+ // tools/list — return the locked input/output schemas verbatim.
58
+ server.setRequestHandler(ListToolsRequestSchema, async (request) => {
59
+ emitToolsList(extractClient(request));
60
+ return {
61
+ tools: tools.map((t) => ({
62
+ name: t.name,
63
+ description: t.description,
64
+ inputSchema: t.inputSchema,
65
+ outputSchema: t.outputSchema,
66
+ })),
67
+ };
68
+ });
69
+ // tools/call — dispatch to handler by tool name. Unknown tool names surface
70
+ // as protocol-level errors (per CallToolResult spec: "errors in _finding_
71
+ // the tool ... should be reported as an MCP error response").
72
+ // Wraps emitToolCall + emitToolDuration around the handler so metrics fire
73
+ // on both success + failure (R1 mid_audit C-2 + tech-lead Important #1).
74
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
75
+ const name = request.params.name;
76
+ const client = extractClient(request);
77
+ const tool = toolsByName.get(name);
78
+ if (!tool) {
79
+ emitToolCall(name, 'unknown_tool', client);
80
+ throw new Error(`Unknown tool: ${name}`);
81
+ }
82
+ const args = (request.params.arguments ?? {});
83
+ const startNs = process.hrtime.bigint();
84
+ try {
85
+ const result = await tool.handler(args);
86
+ emitToolCall(name, 'success', client);
87
+ return result;
88
+ }
89
+ catch (err) {
90
+ emitToolCall(name, 'error', client);
91
+ throw err;
92
+ }
93
+ finally {
94
+ const durNs = process.hrtime.bigint() - startNs;
95
+ emitToolDuration(name, Number(durNs) / 1e9);
96
+ }
97
+ });
98
+ return server;
99
+ }
100
+ async function startStdio(server) {
101
+ const transport = new StdioServerTransport();
102
+ await server.connect(transport);
103
+ console.error(`nexusm-mcp-server ${SERVER_VERSION} listening on stdio`);
104
+ }
105
+ async function startHttp(server) {
106
+ // Lazy import so stdio installs aren't forced to bundle the HTTP transport.
107
+ const { StreamableHTTPServerTransport } = await import('@modelcontextprotocol/sdk/server/streamableHttp.js');
108
+ const { createServer: createHttpServer } = await import('node:http');
109
+ const port = Number.parseInt(process.env.NEXUS_MCP_HTTP_PORT ?? '3000', 10);
110
+ // TODO(wave-2): per-request `server.connect()` + per-request transport
111
+ // is scaffold-only. Production HTTP transport needs session-keyed Server
112
+ // lifecycle per MCP spec (see TASK-014 integration tests + TASK-018
113
+ // middleware in detailed-tasks.yaml).
114
+ const httpServer = createHttpServer(async (req, res) => {
115
+ const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
116
+ await server.connect(transport);
117
+ await transport.handleRequest(req, res);
118
+ });
119
+ httpServer.listen(port, () => {
120
+ console.error(`nexusm-mcp-server ${SERVER_VERSION} listening on http://0.0.0.0:${port}/`);
121
+ });
122
+ }
123
+ async function main() {
124
+ const server = createServer();
125
+ const transportMode = (process.env.NEXUS_MCP_TRANSPORT ?? 'stdio').toLowerCase();
126
+ // Start Prometheus metrics server on separate port (TASK-014, R2 m-6).
127
+ // Coexists with stdio transport — different fd / no stdin/stdout contention.
128
+ await startMetricsServer();
129
+ if (transportMode === 'http') {
130
+ await startHttp(server);
131
+ }
132
+ else if (transportMode === 'stdio') {
133
+ await startStdio(server);
134
+ }
135
+ else {
136
+ console.error(`Unknown NEXUS_MCP_TRANSPORT="${transportMode}" (expected "stdio" or "http")`);
137
+ process.exit(1);
138
+ }
139
+ }
140
+ main().catch((err) => {
141
+ // Never let an unhandled error leak credentials. stderr only.
142
+ const msg = err instanceof Error ? err.message : String(err);
143
+ console.error(`nexusm-mcp-server fatal: ${msg}`);
144
+ process.exit(1);
145
+ });
146
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Prometheus metrics for the Nexus MCP server (US-037 Wave 2 TASK-014).
3
+ *
4
+ * Layer
5
+ * =====
6
+ * This file measures the **MCP protocol dispatch layer** — every `tools/call`
7
+ * and `tools/list` JSON-RPC request the MCP server handles. It is NOT the
8
+ * same as the Python-side `src/nexus/observability/metrics/mcp.py` which
9
+ * measures the **backend REST attribution layer** (post-MCP-dispatch).
10
+ *
11
+ * Both metric families share the `nexus_mcp_*` prefix but observe different
12
+ * events at different layers, so:
13
+ * - TS metric names use `nexus_mcp_tool_*` (this file)
14
+ * - Python metric names use `nexus_mcp_backend_*` (the backend mirror)
15
+ * - Alert rules and Grafana panels must NOT sum across both — they would
16
+ * double-count a single user action
17
+ * - See ADR-001 + Wave 2 mid_audit tech-lead Important #2 (2026-05-22)
18
+ *
19
+ * Design notes
20
+ * ============
21
+ * - Spins up an **independent** HTTP listener on `NEXUS_METRICS_PORT` (default
22
+ * 9090) so the Prometheus scrape endpoint never touches stdin/stdout — the
23
+ * stdio MCP transport monopolises those streams.
24
+ * - Uses `prom-client` for registry management; all metric instances are
25
+ * module-level singletons (CollectorRegistry deduplication).
26
+ * - Cardinality guard (R2 ai D-5): the `client` label is limited to a fixed
27
+ * allowlist; anything outside the list is coerced to `'unknown'` and a
28
+ * separate debug counter tracks the raw string so operators can promote a
29
+ * client to the allowlist without restarting the server.
30
+ *
31
+ * Startup integration
32
+ * ===================
33
+ * Call `startMetricsServer()` once from `src/index.ts` main() — it is a
34
+ * fire-and-forget async function that opens the HTTP port and logs to stderr.
35
+ * (Parent session must wire this call; see TASK-014 note.)
36
+ *
37
+ * prom-client npm dep note
38
+ * ========================
39
+ * Add `"prom-client": "^15.1.0"` to `dependencies` in `package.json`.
40
+ * This subagent does not modify package.json per task constraints.
41
+ */
42
+ import { Registry, Counter, Histogram, Gauge } from 'prom-client';
43
+ export declare const registry: Registry<"text/plain; version=0.0.4; charset=utf-8">;
44
+ /**
45
+ * nexus_mcp_tool_calls_total — counts every tools/call dispatch.
46
+ *
47
+ * Labels:
48
+ * tool — MCP tool name (e.g. `nexus.memory_search`)
49
+ * status — `'success'` | `'error'` | `'not_implemented'`
50
+ * client — normalised client name from KNOWN_CLIENTS (or `'unknown'`)
51
+ */
52
+ export declare const MCP_TOOL_CALLS_TOTAL: Counter<"status" | "tool" | "client">;
53
+ /**
54
+ * nexus_mcp_tool_duration_seconds — latency histogram per tool.
55
+ *
56
+ * Buckets cover sub-millisecond to 10 s to capture both local (fast) and
57
+ * remote Nexus API (network-bound) call distributions.
58
+ *
59
+ * Labels:
60
+ * tool — MCP tool name
61
+ */
62
+ export declare const MCP_TOOL_DURATION_SECONDS: Histogram<"tool">;
63
+ /**
64
+ * nexus_mcp_tools_list_calls_total — counts every tools/list request.
65
+ *
66
+ * Labels:
67
+ * client — normalised client name
68
+ */
69
+ export declare const MCP_TOOLS_LIST_CALLS_TOTAL: Counter<"client">;
70
+ /**
71
+ * nexus_mcp_tool_description_version — Info-style gauge exposing the schema hash.
72
+ *
73
+ * Set to 1.0 with a `hash` label containing the first 8 characters of the
74
+ * SHA-256 of the concatenated tool description strings. Alerts fire when
75
+ * the hash drifts between replicas or between server restarts.
76
+ *
77
+ * Labels:
78
+ * hash — 8-char hex prefix of SHA-256(all tool descriptions)
79
+ */
80
+ export declare const MCP_TOOL_DESCRIPTION_VERSION: Gauge<"hash">;
81
+ /**
82
+ * nexus_mcp_unknown_client_total — debug counter for cardinality guard.
83
+ *
84
+ * Incremented when a raw client name is not in KNOWN_CLIENTS. The `raw`
85
+ * label carries the original string so operators can identify clients to
86
+ * add to the allowlist.
87
+ *
88
+ * Labels:
89
+ * raw — the raw client identifier as received (verbatim)
90
+ */
91
+ export declare const MCP_UNKNOWN_CLIENT_TOTAL: Counter<"raw">;
92
+ /**
93
+ * Record a tool/call dispatch.
94
+ *
95
+ * @param tool - MCP tool name (e.g. `'nexus.memory_search'`)
96
+ * @param status - outcome string (`'success'`, `'error'`, `'not_implemented'`)
97
+ * @param client - raw client identifier; normalised internally against the allowlist
98
+ */
99
+ export declare function emitToolCall(tool: string, status: string, client: string): void;
100
+ /**
101
+ * Record a tools/list request.
102
+ *
103
+ * @param client - raw client identifier; normalised internally against the allowlist
104
+ */
105
+ export declare function emitToolsList(client: string): void;
106
+ /**
107
+ * Record an observation in the tool duration histogram.
108
+ *
109
+ * Call this with `tool` and the elapsed time in seconds. Designed so callers
110
+ * wrap their handler with `Date.now()` before/after and pass
111
+ * `(end - start) / 1000`.
112
+ *
113
+ * @param tool - MCP tool name
114
+ * @param durationSec - elapsed time in seconds
115
+ */
116
+ export declare function emitToolDuration(tool: string, durationSec: number): void;
117
+ /**
118
+ * Increment the debug counter for an unknown client.
119
+ *
120
+ * Called internally by `emitToolCall` and `emitToolsList`; exposed so callers
121
+ * can also emit directly if they detect the anomaly before dispatching.
122
+ *
123
+ * @param rawName - raw client string that was not in the allowlist
124
+ */
125
+ export declare function emitUnknownClient(rawName: string): void;
126
+ /**
127
+ * Register the tool-description schema hash in the Info gauge.
128
+ *
129
+ * Set this once during startup after the tool registry is built. Pass the
130
+ * first 8 hex characters of SHA-256(concatenated tool descriptions) as `hash`.
131
+ *
132
+ * @param hash - 8-char hex prefix of the schema hash
133
+ */
134
+ export declare function setDescriptionHash(hash: string): void;
135
+ /**
136
+ * Start the Prometheus metrics scrape endpoint.
137
+ *
138
+ * Opens an HTTP server on `process.env.NEXUS_METRICS_PORT ?? 9090`. The
139
+ * server only handles `GET /metrics`; all other paths return 404.
140
+ *
141
+ * This function must be called from `src/index.ts` main() (parent session
142
+ * wires the call). It is intentionally fire-and-forget — it does not compete
143
+ * with the MCP transport for stdin/stdout.
144
+ */
145
+ export declare function startMetricsServer(): Promise<void>;
146
+ //# sourceMappingURL=metrics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../src/metrics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAGH,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAyB,MAAM,aAAa,CAAC;AAQzF,eAAO,MAAM,QAAQ,sDAAiB,CAAC;AAuBvC;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,uCAK/B,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,mBAMpC,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,mBAKrC,CAAC;AAEH;;;;;;;;;GASG;AACH,eAAO,MAAM,4BAA4B,eAKvC,CAAC;AAEH;;;;;;;;;GASG;AACH,eAAO,MAAM,wBAAwB,gBAKnC,CAAC;AAMH;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAM/E;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAMlD;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAExE;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAEvD;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAErD;AAMD;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CA2BxD"}