@gwdn/nexus-mcp 0.8.1 → 0.8.3

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.
package/README.md CHANGED
@@ -78,6 +78,31 @@ src/
78
78
  __tests__/ # Unit tests (112) and E2E integration tests (34)
79
79
  ```
80
80
 
81
+ ## Known Issues
82
+
83
+ ### npx cache prevents version upgrade
84
+
85
+ When using `npx` to run the MCP server, npm caches the resolved package in
86
+ `~/.npm/_npx/`. Subsequent invocations may continue to use the cached
87
+ (outdated) version even after a new release is published — especially in
88
+ terminals that were started before the update.
89
+
90
+ **Symptoms:** MCP tools report an older version, or new tools/fixes are missing.
91
+
92
+ **Fix:** Before starting OpenCode, clear stale npx caches:
93
+
94
+ ```bash
95
+ find ~/.npm/_npx -path "*/node_modules/@gwdn/nexus-mcp/package.json" \
96
+ -exec node -e "if(require('{}').version!=='0.8.1') console.log(require('path').resolve('{}','../../..'))" \; \
97
+ | xargs rm -rf
98
+ ```
99
+
100
+ Then start OpenCode normally. npx will fetch the latest version into a fresh
101
+ cache entry.
102
+
103
+ > **Tip:** To avoid this entirely, pin `@latest` in your MCP config args:
104
+ > `["--yes", "@gwdn/nexus-mcp@latest"]`
105
+
81
106
  ## Related
82
107
 
83
108
  - [nexus](https://github.com/gwnexus/nexus-hub) — Backend + Frontend (Next.js/Supabase/Netlify)
@@ -26,6 +26,7 @@ export declare const appendSessionEntrySchema: {
26
26
  linked_entity_type: z.ZodOptional<z.ZodString>;
27
27
  linked_entity_id: z.ZodOptional<z.ZodString>;
28
28
  agent_id: z.ZodOptional<z.ZodString>;
29
+ metadata: z.ZodOptional<z.ZodString>;
29
30
  };
30
31
  type AppendSessionEntryArgs = {
31
32
  session_id: string;
@@ -35,6 +36,7 @@ type AppendSessionEntryArgs = {
35
36
  linked_entity_id?: string;
36
37
  user_id: string;
37
38
  agent_id?: string;
39
+ metadata?: string;
38
40
  };
39
41
  export declare function appendSessionEntry(args: AppendSessionEntryArgs): Promise<{
40
42
  content: {
@@ -1 +1 @@
1
- {"version":3,"file":"append-session-entry.d.ts","sourceRoot":"","sources":["../../src/tools/append-session-entry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;CAgCpC,CAAA;AAED,KAAK,sBAAsB,GAAG;IAC5B,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,sBAAsB;;;;;;;;;;;;GA4BpE"}
1
+ {"version":3,"file":"append-session-entry.d.ts","sourceRoot":"","sources":["../../src/tools/append-session-entry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;CAwCpC,CAAA;AAED,KAAK,sBAAsB,GAAG;IAC5B,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,sBAAsB;;;;;;;;;;;;GA8CpE"}
@@ -37,8 +37,31 @@ export const appendSessionEntrySchema = {
37
37
  .optional()
38
38
  .describe('UUID of the linked entity'),
39
39
  agent_id: z.string().max(200).optional().describe('Agent identifier if applicable'),
40
+ metadata: z
41
+ .string()
42
+ .max(10_000)
43
+ .optional()
44
+ .describe('JSON string of metadata to merge into the session entry. ' +
45
+ 'Example: \'{"model": "claude-opus-4", "toolstack": "opencode", "machine_id": "..."}\''),
40
46
  };
41
47
  export async function appendSessionEntry(args) {
48
+ let parsedMetadata;
49
+ if (args.metadata) {
50
+ try {
51
+ parsedMetadata = JSON.parse(args.metadata);
52
+ }
53
+ catch {
54
+ return {
55
+ content: [
56
+ {
57
+ type: 'text',
58
+ text: JSON.stringify({ error: 'metadata must be a valid JSON string' }, null, 2),
59
+ },
60
+ ],
61
+ isError: true,
62
+ };
63
+ }
64
+ }
42
65
  const result = await nexusPost('/api/mcp/sessions', {
43
66
  action: 'session_append',
44
67
  session_id: args.session_id,
@@ -47,6 +70,7 @@ export async function appendSessionEntry(args) {
47
70
  agent_id: args.agent_id,
48
71
  linked_entity_type: args.linked_entity_type,
49
72
  linked_entity_id: args.linked_entity_id,
73
+ ...(parsedMetadata ? { metadata: parsedMetadata } : {}),
50
74
  });
51
75
  if (!result.ok) {
52
76
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"append-session-entry.js","sourceRoot":"","sources":["../../src/tools/append-session-entry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE3C,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,2BAA2B,CAAC;IACnE,UAAU,EAAE,CAAC;SACV,IAAI,CAAC;QACJ,qBAAqB;QACrB,cAAc;QACd,cAAc;QACd,aAAa;QACb,gBAAgB;QAChB,gBAAgB;QAChB,mBAAmB;QACnB,aAAa;QACb,cAAc;QACd,kBAAkB;QAClB,MAAM;QACN,YAAY;KACb,CAAC;SACD,QAAQ,CAAC,uBAAuB,CAAC;IACpC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,8BAA8B,CAAC;IACzE,kBAAkB,EAAE,CAAC;SAClB,MAAM,EAAE;SACR,GAAG,CAAC,EAAE,CAAC;SACP,QAAQ,EAAE;SACV,QAAQ,CACP,6EAA6E,CAC9E;IACH,gBAAgB,EAAE,CAAC;SAChB,MAAM,EAAE;SACR,IAAI,EAAE;SACN,QAAQ,EAAE;SACV,QAAQ,CAAC,2BAA2B,CAAC;IACxC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,gCAAgC,CAAC;CACpF,CAAA;AAYD,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,IAA4B;IACnE,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,mBAAmB,EAAE;QAClD,MAAM,EAAE,gBAAgB;QACxB,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,kBAAkB,EAAE,IAAI,CAAC,kBAAkB;QAC3C,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;KACxC,CAAC,CAAA;IAEF,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;iBACvD;aACF;YACD,OAAO,EAAE,IAAI;SACd,CAAA;IACH,CAAC;IAED,OAAO;QACL,OAAO,EAAE;YACP,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;SACtE;KACF,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"append-session-entry.js","sourceRoot":"","sources":["../../src/tools/append-session-entry.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gwdn/nexus-mcp",
3
- "version": "0.8.1",
3
+ "version": "0.8.3",
4
4
  "description": "MCP server for the Nexus platform",
5
5
  "type": "module",
6
6
  "main": "dist/server.js",
@@ -206,6 +206,82 @@ describe('Layer 2: session_append', () => {
206
206
 
207
207
  expect(result.isError).toBe(true)
208
208
  })
209
+
210
+ it('should forward parsed metadata to the API', async () => {
211
+ vi.mocked(nexusPost).mockResolvedValue(
212
+ mockApiSuccess({
213
+ action: 'session_append',
214
+ entry_id: TEST_IDS.entryId,
215
+ session_id: TEST_IDS.sessionId,
216
+ entry_type: 'note',
217
+ project_id: TEST_IDS.projectId,
218
+ }),
219
+ )
220
+
221
+ const { appendSessionEntry } = await import('../tools/append-session-entry.js')
222
+ const metadata = JSON.stringify({
223
+ model: 'claude-opus-4',
224
+ toolstack: 'opencode',
225
+ machine_id: '00000000-0000-0000-0000-000000000001',
226
+ })
227
+ const result = await appendSessionEntry({
228
+ session_id: TEST_IDS.sessionId,
229
+ entry_type: 'note',
230
+ summary: 'Entry with metadata',
231
+ user_id: TEST_IDS.userId,
232
+ metadata,
233
+ })
234
+
235
+ expect(result.isError).toBeUndefined()
236
+ expect(vi.mocked(nexusPost)).toHaveBeenCalledWith(
237
+ '/api/mcp/sessions',
238
+ expect.objectContaining({
239
+ metadata: {
240
+ model: 'claude-opus-4',
241
+ toolstack: 'opencode',
242
+ machine_id: '00000000-0000-0000-0000-000000000001',
243
+ },
244
+ }),
245
+ )
246
+ })
247
+
248
+ it('should return error for invalid metadata JSON', async () => {
249
+ const { appendSessionEntry } = await import('../tools/append-session-entry.js')
250
+ const result = await appendSessionEntry({
251
+ session_id: TEST_IDS.sessionId,
252
+ entry_type: 'note',
253
+ summary: 'Bad metadata',
254
+ user_id: TEST_IDS.userId,
255
+ metadata: '{invalid json',
256
+ })
257
+
258
+ expect(result.isError).toBe(true)
259
+ const parsed = parseToolResponse(result)
260
+ expect(parsed.error).toContain('valid JSON')
261
+ })
262
+
263
+ it('should omit metadata key when not provided', async () => {
264
+ vi.mocked(nexusPost).mockResolvedValue(
265
+ mockApiSuccess({
266
+ action: 'session_append',
267
+ entry_id: TEST_IDS.entryId,
268
+ session_id: TEST_IDS.sessionId,
269
+ entry_type: 'note',
270
+ project_id: TEST_IDS.projectId,
271
+ }),
272
+ )
273
+
274
+ const { appendSessionEntry } = await import('../tools/append-session-entry.js')
275
+ await appendSessionEntry({
276
+ session_id: TEST_IDS.sessionId,
277
+ entry_type: 'note',
278
+ summary: 'No metadata',
279
+ user_id: TEST_IDS.userId,
280
+ })
281
+
282
+ const call = vi.mocked(nexusPost).mock.calls[0][1] as Record<string, unknown>
283
+ expect(call).not.toHaveProperty('metadata')
284
+ })
209
285
  })
210
286
 
211
287
  // ---------------------------------------------------------------------------
@@ -146,6 +146,7 @@ describe('MCP Server: Schema exports', () => {
146
146
 
147
147
  const appendEntry = await import('../tools/append-session-entry.js')
148
148
  expect(appendEntry.appendSessionEntrySchema).toBeDefined()
149
+ expect(appendEntry.appendSessionEntrySchema.metadata).toBeDefined()
149
150
  expect(typeof appendEntry.appendSessionEntry).toBe('function')
150
151
 
151
152
  const letters = await import('../tools/letters.js')
@@ -41,6 +41,14 @@ export const appendSessionEntrySchema = {
41
41
  .optional()
42
42
  .describe('UUID of the linked entity'),
43
43
  agent_id: z.string().max(200).optional().describe('Agent identifier if applicable'),
44
+ metadata: z
45
+ .string()
46
+ .max(10_000)
47
+ .optional()
48
+ .describe(
49
+ 'JSON string of metadata to merge into the session entry. ' +
50
+ 'Example: \'{"model": "claude-opus-4", "toolstack": "opencode", "machine_id": "..."}\'',
51
+ ),
44
52
  }
45
53
 
46
54
  type AppendSessionEntryArgs = {
@@ -51,9 +59,27 @@ type AppendSessionEntryArgs = {
51
59
  linked_entity_id?: string
52
60
  user_id: string
53
61
  agent_id?: string
62
+ metadata?: string
54
63
  }
55
64
 
56
65
  export async function appendSessionEntry(args: AppendSessionEntryArgs) {
66
+ let parsedMetadata: Record<string, unknown> | undefined
67
+ if (args.metadata) {
68
+ try {
69
+ parsedMetadata = JSON.parse(args.metadata)
70
+ } catch {
71
+ return {
72
+ content: [
73
+ {
74
+ type: 'text' as const,
75
+ text: JSON.stringify({ error: 'metadata must be a valid JSON string' }, null, 2),
76
+ },
77
+ ],
78
+ isError: true,
79
+ }
80
+ }
81
+ }
82
+
57
83
  const result = await nexusPost('/api/mcp/sessions', {
58
84
  action: 'session_append',
59
85
  session_id: args.session_id,
@@ -62,6 +88,7 @@ export async function appendSessionEntry(args: AppendSessionEntryArgs) {
62
88
  agent_id: args.agent_id,
63
89
  linked_entity_type: args.linked_entity_type,
64
90
  linked_entity_id: args.linked_entity_id,
91
+ ...(parsedMetadata ? { metadata: parsedMetadata } : {}),
65
92
  })
66
93
 
67
94
  if (!result.ok) {