@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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 10CG
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,13 @@
1
+ # nexusm-mcp-server
2
+
3
+ Nexusm MCP Server — generic MCP server exposing Nexusm core capabilities (memory, conversation, knowledge, feedback, context) to MCP clients via `@modelcontextprotocol/sdk` stdio + Streamable HTTP transports.
4
+
5
+ Published to npm as **`@nexusm/mcp-server`**.
6
+
7
+ > Tracked in Nexusm main repo: [`packages/nexusm-mcp-server`](https://forgejo.10cg.pub/10CG/nexus/src/branch/main/packages/nexusm-mcp-server) (US-037 v7.0).
8
+
9
+ ## Status
10
+
11
+ **Wave 2 done (2026-05-22)**: 4 tools fully wired to `@nexusm/sdk` (context_retrieve / memory_search / memory_create / memory_feedback) + full §M-3 HTTP→MCP error mapping (`mapHttpStatusToMcpError` + 2 new error codes Unauthorized/RateLimited) + Prometheus metrics with cardinality guard + cross-substory + E2E + schema_sync integration tests. Wave 3 (nexus-claude-plugin Anthropic marketplace) pending — see [proposal](https://forgejo.10cg.pub/10CG/nexus/src/branch/main/openspec/changes/us-037-mcp-server-exposure/proposal.md).
12
+
13
+ **Known Wave 2 limitations**: (1) CI red until Gate-1 — `@nexusm/sdk@1.3.0` npm publish pending (user action; SDK rename merged at `1fbdd69` in `nexus-sdk-js` main); (2) integration tests env-gated (require `NEXUS_TEST_API_URL/TOKEN/TENANT_ID` Forgejo secrets, currently dormant); (3) Python backend `mcp.py` metrics defined but emit-site wiring deferred to Wave 3 (FU-MCP-BACKEND-EMIT-WIRING); (4) HTTP transport per-request `server.connect()` is scaffold-only — Wave 3 entry condition (FU-MCP-HTTP-SESSION) before plugin TASK-019 E2E runs.
package/RUNBOOK.md ADDED
@@ -0,0 +1,190 @@
1
+ # nexusm-mcp-server RUNBOOK
2
+
3
+ > **Status**: in place at `packages/nexusm-mcp-server/RUNBOOK.md`; moved into this submodule at Wave 0 close (2026-05-21).
4
+ > **Owner**: 10CG Backend / DevOps
5
+ > **Created**: 2026-05-09 (US-037 Phase B Wave 0)
6
+ > **Last revised**: 2026-05-22 (Wave 1 R1 audit amendments — removed `description-clean` row from §5 until Wave 2 CI matrix expansion)
7
+ > **Related**: [proposal.md §C-5](https://forgejo.10cg.pub/10CG/nexus/src/branch/main/openspec/changes/us-037-mcp-server-exposure/proposal.md) + [detailed-tasks.yaml TASK-002 / TASK-008](https://forgejo.10cg.pub/10CG/nexus/src/branch/main/openspec/changes/us-037-mcp-server-exposure/detailed-tasks.yaml)
8
+
9
+ ---
10
+
11
+ ## 1. npm publish Failure Modes
12
+
13
+ ### 1.1 Rate Limited (HTTP 429)
14
+
15
+ **Symptom**: `npm publish` fails with `429 Too Many Requests`.
16
+
17
+ **Cause**: npm registry has anti-abuse rate limits per token.
18
+
19
+ **Recovery**:
20
+ 1. Wait 5-10 min before retry.
21
+ 2. If persistent (> 30 min), check npm status page: https://status.npmjs.org/
22
+ 3. Workaround: use `--registry https://registry.npmjs.org/` (force public, sometimes mirrors lag).
23
+
24
+ **Avoid**: Don't retry in tight loop — will trigger longer ban.
25
+
26
+ ---
27
+
28
+ ### 1.2 Token Expired / Unauthorized (HTTP 401)
29
+
30
+ **Symptom**: `npm publish` fails with `401 Unauthorized` or `EAUTHIP`.
31
+
32
+ **Cause**: `NPM_TOKEN` granular access token expired (max 90-day lifetime since 2025 npm policy) or revoked.
33
+
34
+ **Recovery**:
35
+ 1. Login to npmjs.com with org account.
36
+ 2. Profile → Access Tokens → Granular Access Tokens → "Generate New Token". Configure:
37
+ - Bypass 2FA: ✅ (required for CI automation)
38
+ - Organizations → `nexusm` → Read and write
39
+ - Expiration: 90 days (max)
40
+ 3. Update Forgejo repo secret (note: `PUT` method + `/actions/secrets/` path):
41
+ ```bash
42
+ forgejo PUT /repos/10CG/nexusm-mcp-server/actions/secrets/NPM_TOKEN -d '{"data":"<new-token>"}'
43
+ ```
44
+ 4. Re-run failed CI workflow (Forgejo Actions → re-run).
45
+
46
+ **Rotation cadence**: Quarterly (Jan / Apr / Jul / Oct, 1st Monday) — required because npm granular tokens cap at 90 days. Schedule in calendar.
47
+
48
+ ---
49
+
50
+ ### 1.3 Namespace Not Registered / Forbidden (HTTP 403)
51
+
52
+ **Symptom**: `npm publish` fails with `403 Forbidden — You do not have permission to publish "@nexusm/mcp-server"`.
53
+
54
+ **Cause**: `@nexusm` scoped namespace not registered to org account, or token doesn't have publish scope on this namespace.
55
+
56
+ **Recovery**:
57
+ 1. Verify namespace ownership: `npm access ls-packages @nexusm`.
58
+ 2. If namespace not owned, register via npmjs.com → "Add Organization" → name: `nexusm`, type: Free Open Source.
59
+ 3. Re-link granular access token to org: Profile → Access Tokens → edit token → Organizations → `nexusm` → Read and write.
60
+ 4. Retry publish.
61
+
62
+ **Long-term safeguard**: `@nexusm` scope is owned by 10CG since 2026-05-21 (Wave 0 closure). The original A2-D-1 unscoped fallback (`nexus-mcp-server`) is no longer available — that name is taken on npm by an unrelated party. If `@nexusm` is ever lost, immediately reclaim via npmjs.com support; do NOT fall back to unscoped.
63
+
64
+ ---
65
+
66
+ ### 1.4 Disaster Recovery: Bad Version Published
67
+
68
+ **Symptom**: Critical bug shipped in `0.x.y`, users hitting it.
69
+
70
+ **Recovery options** (in priority order):
71
+
72
+ 1. **Hotfix `0.x.(y+1)`** (preferred): bump patch, fix bug, publish. Existing users get update via npm update.
73
+ 2. **`npm deprecate @nexusm/mcp-server@0.x.y "reason"`**: marks version as deprecated; users see warning on install but version stays available.
74
+ 3. **`npm unpublish @nexusm/mcp-server@0.x.y --force`**: removes from registry. **DANGEROUS** — only allowed within 72h of publish per npm policy. Will break any user who has it pinned.
75
+
76
+ **Decision matrix**:
77
+ | Severity | Action |
78
+ |----------|--------|
79
+ | Cosmetic / non-functional bug | Hotfix `0.x.(y+1)` |
80
+ | Security vuln (CVE level) | Hotfix + deprecate `0.x.y` |
81
+ | Critical breakage (data loss / DoS) | Hotfix + deprecate; consider unpublish if < 24h |
82
+ | Wrong file shipped (e.g., secret in tarball) | Unpublish IMMEDIATELY (within 72h window) + rotate any leaked credentials |
83
+
84
+ **Reference**: https://docs.npmjs.com/policies/unpublish
85
+
86
+ ---
87
+
88
+ ## 2. NPM_TOKEN Rotation
89
+
90
+ ### Schedule
91
+
92
+ - **Quarterly**: Jan / Apr / Jul / Oct (1st Monday) — required because npm granular tokens cap at 90 days
93
+ - **Ad-hoc**: when leak suspected, when team member leaves, when 90-day expiration ≤ 2 weeks away
94
+
95
+ ### Procedure
96
+
97
+ 1. Generate new granular access token (npmjs.com → Profile → Access Tokens → Granular Access Tokens → "Generate New Token"). Settings:
98
+ - Bypass 2FA: ✅
99
+ - Organizations → `nexusm` → Read and write
100
+ - Expiration: 90 days
101
+ 2. Test new token locally:
102
+ ```bash
103
+ NPM_TOKEN=<new> npm whoami --registry https://registry.npmjs.org/
104
+ ```
105
+ 3. Update Forgejo repo secret (PUT + `/actions/secrets/`):
106
+ ```bash
107
+ forgejo PUT /repos/10CG/nexusm-mcp-server/actions/secrets/NPM_TOKEN -d '{"data":"<new>"}'
108
+ ```
109
+ 4. Trigger a no-op CI run (push empty commit) to verify CI auth works with new token.
110
+ 5. Revoke old token (npmjs.com → Access Tokens → Delete).
111
+ 6. Update calendar reminder for next rotation (+3 months).
112
+
113
+ ### Audit Trail
114
+
115
+ Log each rotation in `nexusm-mcp-server/CHANGELOG.md` under `## Operations`:
116
+ ```
117
+ - 2026-04-XX: NPM_TOKEN rotated (rotated_by=<name>, old_token_revoked=2026-04-XX)
118
+ ```
119
+
120
+ ---
121
+
122
+ ## 3. Disaster Recovery — Forgejo Repo Loss
123
+
124
+ ### Scenario: `10CG/nexusm-mcp-server` Forgejo repo deleted / corrupted
125
+
126
+ **Recovery**:
127
+
128
+ 1. **Local clones still have full history** — any team member with a local clone can restore.
129
+ 2. Rebuild from local:
130
+ ```bash
131
+ # In team member's local clone:
132
+ git remote rename origin forgejo-old # save old remote ref just in case
133
+ forgejo POST /orgs/10CG/repos -d '{"name":"nexusm-mcp-server",...}'
134
+ git remote add origin ssh://forgejo@forgejo.10cg.pub/10CG/nexusm-mcp-server.git
135
+ git push -u origin main --tags
136
+ ```
137
+ 3. Re-add Forgejo Actions secrets (NPM_TOKEN) per §2.
138
+ 4. Update main nexus repo `.gitmodules` if URL changed.
139
+ 5. Notify all submodule consumers to re-init: `git submodule update --init`.
140
+
141
+ ### Scenario: GitHub mirror (`simonfishgit/nexus-claude-plugin`) lost
142
+
143
+ GitHub mirror is auto-synced from Forgejo via `.forgejo/workflows/mirror.yml` (TASK-024). If mirror disappears:
144
+
145
+ 1. Recreate GitHub repo (any team member with PAT).
146
+ 2. Re-add `GITHUB_PAT` secret in Forgejo `nexus-claude-plugin` repo.
147
+ 3. Push current Forgejo HEAD: `git push github main`.
148
+
149
+ This is the Anthropic marketplace submission path (`plugin.json author=10CG`, mirror is just for discovery via simonfishgit).
150
+
151
+ ---
152
+
153
+ ## 4. Namespace Ownership
154
+
155
+ - npm: `@nexusm/*` scoped namespace owned by 10CG organization account on npmjs.com.
156
+ - Forgejo: `10CG/nexusm-mcp-server` repo, admin = 10CG org admins.
157
+ - GitHub mirror: `simonfishgit/nexus-claude-plugin` (personal account, bus factor mitigation via Forgejo Actions auto-sync per A2-D-3).
158
+
159
+ If 10CG npm org access lost (admin departure / account compromise):
160
+ 1. Contact npm support (support@npmjs.com) with org admin proof.
161
+ 2. Worst case: fallback to unscoped `nexusm-mcp-server` package, update all references (proposal A2-D-1 fallback).
162
+
163
+ ---
164
+
165
+ ## 5. CI Workflow Reference
166
+
167
+ `.forgejo/workflows/ci.yml` (TASK-008 / TASK-017 / TASK-025):
168
+
169
+ | Step | Trigger | Purpose | Failure → Action |
170
+ |------|---------|---------|-----------------|
171
+ | `lint` | push, PR | eslint + prettier | Fix code style; check `.eslintrc` |
172
+ | `tsc` | push, PR | TypeScript type-check | Fix type errors; SDK Zod schema drift may indicate need to bump @nexusm/sdk |
173
+ | `test:unit` | push, PR | vitest unit tests | Local repro: `npm test` |
174
+ | `test:integration` | push, PR | mcp-cli E2E (3 platform matrix Linux × Node 18/20 + macOS × Node 20) | Check matrix log; Aether runner Windows OOS Phase 1 |
175
+ | `schema_sync` | push, PR | MCP inputSchema vs @nexusm/sdk Zod schema | Update one or the other to match |
176
+ | `publish` | tag `v*` | npm publish + Forgejo release | Per §1 failure modes |
177
+
178
+ > **Wave 1 caveat (2026-05-22)**: Only `lint` + `tsc` (build) are currently implemented in `.forgejo/workflows/ci.yml`. `test:unit` / `test:integration` / `schema_sync` / `publish` are planned for Wave 2 TASK-014 (test matrix) and Wave 4 TASK-026 (publish). A `description-clean` step (grep `R[0-9]+|C-[0-9]+|M-[0-9]+` audit-marker leakage in `src/tools/*.ts` `description:` fields) was originally listed but deferred — for Wave 1 it is enforced via PR code review (R1 audit caught + fixed 2 leaks 2026-05-22).
179
+
180
+ ---
181
+
182
+ ## 6. Operations Log
183
+
184
+ (to be appended chronologically)
185
+
186
+ ```
187
+ 2026-XX-XX Initial repo created by <ops>; NPM_TOKEN automation token configured
188
+ 2026-XX-XX Phase B Wave 1 first commit (TASK-003 MCP scaffold)
189
+ ...
190
+ ```
package/dist/auth.d.ts ADDED
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Auth / env config loader for Nexusm MCP server (US-037b).
3
+ *
4
+ * Loads required env vars on startup and fails fast if any is missing.
5
+ *
6
+ * Per R2 C-1 (proposal §283): `NEXUS_USER_ID` is intentionally NOT loaded
7
+ * here — `user_id` is supplied per-call via MCP tool input args.
8
+ *
9
+ * SECURITY:
10
+ * - The Bearer token (NEXUS_API_TOKEN) MUST NEVER appear in stdout
11
+ * (would pollute MCP stdio transport, breaking the client),
12
+ * nor in stderr error messages, nor in any log line.
13
+ * - All diagnostic output uses env var *names* only, never values.
14
+ */
15
+ /**
16
+ * Loaded, validated auth configuration.
17
+ *
18
+ * Exported as a named interface so downstream modules (notably
19
+ * `errors.ts` per TASK-007 / TASK-013) can import the contract
20
+ * without re-declaring it.
21
+ */
22
+ export interface AuthConfig {
23
+ /** Base URL of the Nexus HTTP API (no trailing slash enforced). */
24
+ readonly apiUrl: string;
25
+ /** Bearer token for Nexus API. NEVER log this value. */
26
+ readonly apiToken: string;
27
+ /** Tenant identifier for multi-tenant routing. */
28
+ readonly tenantId: string;
29
+ }
30
+ /**
31
+ * Stream sink for diagnostics. Injectable for testing only; defaults to
32
+ * `process.stderr` so that no diagnostic ever lands on stdout (which is
33
+ * reserved for the MCP stdio transport).
34
+ */
35
+ export interface AuthIO {
36
+ stderr: NodeJS.WritableStream;
37
+ exit: (code: number) => never;
38
+ }
39
+ /**
40
+ * Load and validate auth config from `process.env`.
41
+ *
42
+ * On any missing required var, writes a clear, token-free error message to
43
+ * `stderr` and calls `process.exit(1)`. Empty string and whitespace-only
44
+ * values are treated as missing (env var inheritance from parent shells
45
+ * frequently produces empty values, which would otherwise produce a
46
+ * confusing "401 Unauthorized" downstream).
47
+ */
48
+ export declare function loadAuthConfig(env?: NodeJS.ProcessEnv, io?: AuthIO): AuthConfig;
49
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAOH;;;;;;GAMG;AACH,MAAM,WAAW,UAAU;IACzB,mEAAmE;IACnE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,wDAAwD;IACxD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,kDAAkD;IAClD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED;;;;GAIG;AACH,MAAM,WAAW,MAAM;IACrB,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;IAC9B,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,KAAK,CAAC;CAC/B;AAQD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,EAAE,GAAE,MAAkB,GACrB,UAAU,CAiCZ"}
package/dist/auth.js ADDED
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Auth / env config loader for Nexusm MCP server (US-037b).
3
+ *
4
+ * Loads required env vars on startup and fails fast if any is missing.
5
+ *
6
+ * Per R2 C-1 (proposal §283): `NEXUS_USER_ID` is intentionally NOT loaded
7
+ * here — `user_id` is supplied per-call via MCP tool input args.
8
+ *
9
+ * SECURITY:
10
+ * - The Bearer token (NEXUS_API_TOKEN) MUST NEVER appear in stdout
11
+ * (would pollute MCP stdio transport, breaking the client),
12
+ * nor in stderr error messages, nor in any log line.
13
+ * - All diagnostic output uses env var *names* only, never values.
14
+ */
15
+ /** Required env var keys. Order is preserved when reporting missing vars. */
16
+ const REQUIRED_ENV_VARS = ['NEXUS_API_URL', 'NEXUS_API_TOKEN', 'NEXUS_TENANT_ID'];
17
+ const defaultIO = {
18
+ stderr: process.stderr,
19
+ // Cast: process.exit is typed as `(code?) => never` but TS sometimes widens.
20
+ exit: ((code) => process.exit(code)),
21
+ };
22
+ /**
23
+ * Load and validate auth config from `process.env`.
24
+ *
25
+ * On any missing required var, writes a clear, token-free error message to
26
+ * `stderr` and calls `process.exit(1)`. Empty string and whitespace-only
27
+ * values are treated as missing (env var inheritance from parent shells
28
+ * frequently produces empty values, which would otherwise produce a
29
+ * confusing "401 Unauthorized" downstream).
30
+ */
31
+ export function loadAuthConfig(env = process.env, io = defaultIO) {
32
+ const missing = [];
33
+ const values = {};
34
+ for (const key of REQUIRED_ENV_VARS) {
35
+ const raw = env[key];
36
+ if (raw === undefined || raw.trim() === '') {
37
+ missing.push(key);
38
+ }
39
+ else {
40
+ values[key] = raw.trim();
41
+ }
42
+ }
43
+ if (missing.length > 0) {
44
+ // Build message from var *names* only — never values. This guarantees
45
+ // a leaked token cannot reach stderr via this path even if a future
46
+ // refactor accidentally passes `env` into the message.
47
+ const list = missing.join(', ');
48
+ const noun = missing.length === 1 ? 'variable is' : 'variables are';
49
+ const msg = `[nexusm-mcp-server] Required environment ${noun} missing: ${list}. ` +
50
+ `Set them before starting the server. See RUNBOOK.md.\n`;
51
+ io.stderr.write(msg);
52
+ io.exit(1);
53
+ // `exit` is typed `never`; unreachable, but satisfies control-flow analysis.
54
+ throw new Error('unreachable');
55
+ }
56
+ return {
57
+ apiUrl: values.NEXUS_API_URL,
58
+ apiToken: values.NEXUS_API_TOKEN,
59
+ tenantId: values.NEXUS_TENANT_ID,
60
+ };
61
+ }
62
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,6EAA6E;AAC7E,MAAM,iBAAiB,GAAG,CAAC,eAAe,EAAE,iBAAiB,EAAE,iBAAiB,CAAU,CAAC;AA8B3F,MAAM,SAAS,GAAW;IACxB,MAAM,EAAE,OAAO,CAAC,MAAM;IACtB,6EAA6E;IAC7E,IAAI,EAAE,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAA4B;CACxE,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAC5B,MAAyB,OAAO,CAAC,GAAG,EACpC,KAAa,SAAS;IAEtB,MAAM,OAAO,GAAqB,EAAE,CAAC;IACrC,MAAM,MAAM,GAA4C,EAAE,CAAC;IAE3D,KAAK,MAAM,GAAG,IAAI,iBAAiB,EAAE,CAAC;QACpC,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;QACrB,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YAC3C,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACpB,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QAC3B,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,sEAAsE;QACtE,oEAAoE;QACpE,uDAAuD;QACvD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChC,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,eAAe,CAAC;QACpE,MAAM,GAAG,GACP,4CAA4C,IAAI,aAAa,IAAI,IAAI;YACrE,wDAAwD,CAAC;QAC3D,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACrB,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACX,6EAA6E;QAC7E,MAAM,IAAI,KAAK,CAAC,aAAa,CAAC,CAAC;IACjC,CAAC;IAED,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,aAAuB;QACtC,QAAQ,EAAE,MAAM,CAAC,eAAyB;QAC1C,QAAQ,EAAE,MAAM,CAAC,eAAyB;KAC3C,CAAC;AACJ,CAAC"}
@@ -0,0 +1,211 @@
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
+ import type { AuthConfig } from './auth.js';
19
+ /**
20
+ * MCP / JSON-RPC error codes surfaced by this server.
21
+ *
22
+ * Values mirror `@modelcontextprotocol/sdk` `ErrorCode` enum
23
+ * (`dist/esm/types.d.ts`). We re-declare locally rather than re-export
24
+ * the SDK enum so that:
25
+ * 1. errors.ts has zero runtime import from the SDK (keeps the
26
+ * contract layer independent of SDK version churn)
27
+ * 2. TASK-013's mapping logic and tests have a single source of truth
28
+ * for which codes this server is allowed to emit
29
+ *
30
+ * Scope decision (resolved ambiguity from spec):
31
+ * We enumerate the four JSON-RPC standard codes required by §M-3
32
+ * (`InvalidRequest`, `MethodNotFound`, `InvalidParams`, `InternalError`),
33
+ * plus `ParseError` (-32700) for completeness of the JSON-RPC base
34
+ * set, plus `ConnectionClosed` (-32000) and `RequestTimeout` (-32001)
35
+ * which the SDK defines and which `NetworkError` / `CancelError`
36
+ * downstream mappings will need. UrlElicitationRequired (-32042) is
37
+ * intentionally omitted — not in scope for Wave 1 / Wave 2.
38
+ */
39
+ export declare enum McpErrorCode {
40
+ ParseError = -32700,
41
+ InvalidRequest = -32600,
42
+ MethodNotFound = -32601,
43
+ InvalidParams = -32602,
44
+ InternalError = -32603,
45
+ ConnectionClosed = -32000,
46
+ RequestTimeout = -32001,
47
+ /**
48
+ * TASK-013 additions: custom codes in the application-defined range
49
+ * (-32099..-32000 is reserved for implementation; we use the next
50
+ * available slots above -32000 for semantic clarity).
51
+ *
52
+ * Unauthorized (-32011): 401 / 403 from Nexus REST — semantically distinct
53
+ * from InvalidRequest (-32600) so clients can detect auth failures without
54
+ * parsing the message string.
55
+ * RateLimited (-32012): 429 Retry-After. Clients should honor
56
+ * `data.retry_after_seconds` before retrying.
57
+ */
58
+ Unauthorized = -32011,
59
+ RateLimited = -32012
60
+ }
61
+ /**
62
+ * Base error for all errors this MCP server emits.
63
+ *
64
+ * Carries enough structured context to translate a thrown `NexusError` into a
65
+ * JSON-RPC error response without re-inspecting the underlying axios / SDK
66
+ * error.
67
+ */
68
+ export declare class NexusError extends Error {
69
+ /**
70
+ * Upstream HTTP status (Nexus REST), or `null` when the error did not
71
+ * originate from an HTTP response (e.g. DNS failure, abort, internal
72
+ * invariant violation).
73
+ */
74
+ readonly httpStatus: number | null;
75
+ /** MCP/JSON-RPC error code that this error will surface as. */
76
+ readonly mcpErrorCode: McpErrorCode;
77
+ /**
78
+ * Whether the MCP client may safely retry this request.
79
+ * Populated by `mapHttpStatusToMcpError` and the NLI/network helpers.
80
+ */
81
+ readonly retryable: boolean;
82
+ /**
83
+ * Additional structured data surfaced in the JSON-RPC `error.data` field.
84
+ * Safe to serialize — must never contain auth tokens or raw SDK internals.
85
+ * Populated by the mapping layer (e.g. `retry_after_seconds`, `network`,
86
+ * `timeout`).
87
+ */
88
+ readonly data?: Record<string, unknown>;
89
+ /**
90
+ * Underlying cause. Per ES2022 `Error.cause`. **Not serialized** by
91
+ * `toJSON()` — see file header SECURITY note.
92
+ */
93
+ readonly cause?: unknown;
94
+ constructor(message: string, mcpErrorCode: McpErrorCode, httpStatus?: number | null, cause?: unknown, options?: {
95
+ retryable?: boolean;
96
+ data?: Record<string, unknown>;
97
+ });
98
+ /**
99
+ * Safe serialization. Deliberately omits `cause` and `stack` to
100
+ * prevent accidental token leakage if a caller logs the JSON form.
101
+ *
102
+ * `data` IS included — it is caller-controlled structured metadata that
103
+ * must never contain raw SDK objects (that would be caught during review
104
+ * of `mapHttpStatusToMcpError` callers).
105
+ */
106
+ toJSON(): {
107
+ name: string;
108
+ message: string;
109
+ httpStatus: number | null;
110
+ mcpErrorCode: McpErrorCode;
111
+ data?: Record<string, unknown>;
112
+ };
113
+ }
114
+ /**
115
+ * Canonical type for a function that maps an HTTP status + response body to
116
+ * a `McpErrorCode`. `headers` is optional — only needed for 429 Retry-After
117
+ * extraction. This type is the public contract; the concrete implementation
118
+ * is `mapHttpStatusToMcpError`.
119
+ */
120
+ export type ErrorMapping = (httpStatus: number | null, body: unknown, headers?: Record<string, string | string[] | undefined>) => NexusError;
121
+ /**
122
+ * Shape of an axios-like error thrown by `@nexusm/sdk`.
123
+ * We cannot import axios types here (would add a hard dep); instead we use
124
+ * structural duck-typing checked by `isAxiosLikeError`.
125
+ */
126
+ interface AxiosLikeError {
127
+ isAxiosError: true;
128
+ message: string;
129
+ response?: {
130
+ status: number;
131
+ data?: unknown;
132
+ headers?: Record<string, string | string[] | undefined>;
133
+ };
134
+ code?: string;
135
+ }
136
+ /**
137
+ * Type guard for axios-compatible errors thrown by `@nexusm/sdk`.
138
+ *
139
+ * Matches any object with `isAxiosError === true`, which is the canonical
140
+ * axios duck-type flag. This guard intentionally does NOT import axios — it
141
+ * keeps `errors.ts` free of SDK runtime dependencies.
142
+ */
143
+ export declare function isAxiosLikeError(err: unknown): err is AxiosLikeError;
144
+ /**
145
+ * Canonical entrypoint for converting an upstream HTTP response (or SDK
146
+ * network/timeout error) into a typed `NexusError` with the correct
147
+ * `McpErrorCode`, `retryable` flag, and `data` extras.
148
+ *
149
+ * Proposal §M-3 mapping table:
150
+ *
151
+ * | httpStatus | McpErrorCode | retryable | data extras |
152
+ * |-------------------------|-------------------|-----------|--------------------------|
153
+ * | 401, 403 | Unauthorized | false | — |
154
+ * | 404 | MethodNotFound | false | — |
155
+ * | 422 | InvalidParams | false | — |
156
+ * | 429 | RateLimited | true* | retry_after_seconds?: n |
157
+ * | 503 | ConnectionClosed | true | — |
158
+ * | 5xx (else) | InternalError | true | — |
159
+ * | null + network=true | InternalError | true | network: true |
160
+ * | null + timeout=true | RequestTimeout | true | timeout: true |
161
+ *
162
+ * *429: retryable "after Retry-After header elapses" — we set retryable=true
163
+ * and populate `data.retry_after_seconds` so clients can honour the window.
164
+ *
165
+ * Note: HTTP 200 + body.errors != null is NOT an error; that is the
166
+ * partial-degradation path handled in tool handlers (see context.ts). This
167
+ * function is only invoked on non-2xx responses or SDK error throws.
168
+ *
169
+ * @param httpStatus HTTP status code, or `null` for non-HTTP errors.
170
+ * @param body Raw response body (typed `unknown`; we do not parse it).
171
+ * @param headers Response headers, used only to extract Retry-After on 429.
172
+ */
173
+ export declare function mapHttpStatusToMcpError(httpStatus: number | null, body: unknown, headers?: Record<string, string | string[] | undefined>): NexusError;
174
+ /**
175
+ * Auth-origin error (401 / 403 from Nexus REST, or local auth-config
176
+ * issues surfaced after `loadAuthConfig`).
177
+ *
178
+ * `authConfigKey` is optional because not every auth failure points at
179
+ * a specific config field (e.g. a token that was valid at load but
180
+ * since revoked has no `AuthConfig` key to blame).
181
+ */
182
+ export interface AuthError extends NexusError {
183
+ readonly authConfigKey?: keyof AuthConfig;
184
+ }
185
+ /**
186
+ * Network-origin error (DNS failure, connection refused, TLS error,
187
+ * read timeout). Per proposal §M-3, these map to `InternalError` with
188
+ * `error.data.network = true`, and the MCP client may retry.
189
+ *
190
+ * `retryable` is a hint to the client; the server itself does not
191
+ * retry (avoids double-counting under quota / Retry-After).
192
+ */
193
+ export interface NetworkError extends NexusError {
194
+ readonly retryable: boolean;
195
+ }
196
+ /**
197
+ * Cancellation error — fired when an in-flight tool call is aborted.
198
+ *
199
+ * - `client_cancel`: MCP cancel notification from the client
200
+ * - `timeout`: server-side deadline elapsed
201
+ * - `signal`: AbortSignal propagated from a higher layer
202
+ *
203
+ * Per proposal §M-3, the server does **not** return a result when a
204
+ * cancel arrives; this error type exists so handlers can distinguish
205
+ * cancel from other failures in logs / metrics.
206
+ */
207
+ export interface CancelError extends NexusError {
208
+ readonly reason: 'client_cancel' | 'timeout' | 'signal';
209
+ }
210
+ export {};
211
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}