@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 +25 -0
- package/dist/tools/append-session-entry.d.ts +2 -0
- package/dist/tools/append-session-entry.d.ts.map +1 -1
- package/dist/tools/append-session-entry.js +24 -0
- package/dist/tools/append-session-entry.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/layer2-coordination.test.ts +76 -0
- package/src/__tests__/server.test.ts +1 -0
- package/src/tools/append-session-entry.ts +27 -0
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
|
|
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;
|
|
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
|
@@ -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) {
|