@gwdn/nexus-mcp 0.8.7 → 0.8.9
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/dist/server.js +1 -1
- package/dist/tools/create-task.d.ts +1 -1
- package/dist/tools/create-task.js +3 -3
- package/dist/tools/directives.d.ts +2 -2
- package/dist/tools/letters.d.ts +1 -1
- package/dist/tools/update-task-status.d.ts +1 -1
- package/package.json +8 -2
- package/.github/workflows/ci.yml +0 -38
- package/.github/workflows/release.yml +0 -57
- package/src/__tests__/auth.test.ts +0 -162
- package/src/__tests__/decision-comments.test.ts +0 -173
- package/src/__tests__/e2e-integration.test.ts +0 -443
- package/src/__tests__/helpers.ts +0 -58
- package/src/__tests__/layer1-knowledge.test.ts +0 -302
- package/src/__tests__/layer2-coordination.test.ts +0 -918
- package/src/__tests__/layer3-governance.test.ts +0 -205
- package/src/__tests__/machine-id.test.ts +0 -136
- package/src/__tests__/nexus-api.test.ts +0 -127
- package/src/__tests__/project-list-and-skill-assign.test.ts +0 -282
- package/src/__tests__/reviews.test.ts +0 -420
- package/src/__tests__/server.test.ts +0 -251
- package/src/__tests__/setup.ts +0 -15
- package/src/__tests__/task-list-and-doc-list.test.ts +0 -226
- package/src/auth.ts +0 -81
- package/src/machine-id.ts +0 -60
- package/src/nexus-api.ts +0 -149
- package/src/server.ts +0 -629
- package/src/tools/add-task-note.ts +0 -50
- package/src/tools/append-session-entry.ts +0 -113
- package/src/tools/classify-document.ts +0 -55
- package/src/tools/create-task.ts +0 -66
- package/src/tools/decision-comments.ts +0 -103
- package/src/tools/delete-document.ts +0 -43
- package/src/tools/directives.ts +0 -303
- package/src/tools/get-document.ts +0 -80
- package/src/tools/get-project-memory.ts +0 -65
- package/src/tools/get-related-entities.ts +0 -73
- package/src/tools/governance.ts +0 -165
- package/src/tools/ingest-document.ts +0 -65
- package/src/tools/letter-inbox.ts +0 -157
- package/src/tools/letters.ts +0 -144
- package/src/tools/list-documents.ts +0 -58
- package/src/tools/list-tasks.ts +0 -57
- package/src/tools/project-list.ts +0 -52
- package/src/tools/reviews.ts +0 -281
- package/src/tools/search-knowledge.ts +0 -68
- package/src/tools/sessions.ts +0 -157
- package/src/tools/skill-assign.ts +0 -142
- package/src/tools/skills.ts +0 -255
- package/src/tools/update-document.ts +0 -66
- package/src/tools/update-task-status.ts +0 -64
- package/tsconfig.json +0 -21
- package/vitest.config.ts +0 -8
package/dist/server.js
CHANGED
|
@@ -121,7 +121,7 @@ function withIdentity(handler) {
|
|
|
121
121
|
// ---------------------------------------------------------------------------
|
|
122
122
|
const server = new McpServer({
|
|
123
123
|
name: 'nexus-mcp',
|
|
124
|
-
version: '0.8.
|
|
124
|
+
version: '0.8.9',
|
|
125
125
|
}, {
|
|
126
126
|
capabilities: {
|
|
127
127
|
tools: {},
|
|
@@ -11,8 +11,8 @@ export const createTaskSchema = {
|
|
|
11
11
|
title: z.string().max(500).describe('Task title'),
|
|
12
12
|
description: z.string().max(100_000).optional().describe('Task description'),
|
|
13
13
|
priority: z
|
|
14
|
-
.enum(['low', '
|
|
15
|
-
.default('
|
|
14
|
+
.enum(['low', 'medium', 'high', 'urgent'])
|
|
15
|
+
.default('medium')
|
|
16
16
|
.describe('Task priority'),
|
|
17
17
|
assignee: z.string().uuid().optional().describe('UUID of the assigned user'),
|
|
18
18
|
status: z
|
|
@@ -27,7 +27,7 @@ export async function createTask(args) {
|
|
|
27
27
|
project_id: args.project_id,
|
|
28
28
|
title: args.title,
|
|
29
29
|
description: args.description,
|
|
30
|
-
priority: args.priority ?? '
|
|
30
|
+
priority: args.priority ?? 'medium',
|
|
31
31
|
assignee: args.assignee,
|
|
32
32
|
status: args.status ?? 'open',
|
|
33
33
|
});
|
|
@@ -43,8 +43,8 @@ export declare const pdCreateSchema: {
|
|
|
43
43
|
category: z.ZodDefault<z.ZodString>;
|
|
44
44
|
priority: z.ZodDefault<z.ZodEnum<{
|
|
45
45
|
low: "low";
|
|
46
|
-
high: "high";
|
|
47
46
|
medium: "medium";
|
|
47
|
+
high: "high";
|
|
48
48
|
}>>;
|
|
49
49
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
50
50
|
};
|
|
@@ -70,8 +70,8 @@ export declare const pdUpdateSchema: {
|
|
|
70
70
|
category: z.ZodOptional<z.ZodString>;
|
|
71
71
|
priority: z.ZodOptional<z.ZodEnum<{
|
|
72
72
|
low: "low";
|
|
73
|
-
high: "high";
|
|
74
73
|
medium: "medium";
|
|
74
|
+
high: "high";
|
|
75
75
|
}>>;
|
|
76
76
|
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
77
77
|
};
|
package/dist/tools/letters.d.ts
CHANGED
|
@@ -13,9 +13,9 @@ export declare const createLetterSchema: {
|
|
|
13
13
|
body: z.ZodString;
|
|
14
14
|
priority: z.ZodDefault<z.ZodEnum<{
|
|
15
15
|
low: "low";
|
|
16
|
-
normal: "normal";
|
|
17
16
|
high: "high";
|
|
18
17
|
urgent: "urgent";
|
|
18
|
+
normal: "normal";
|
|
19
19
|
}>>;
|
|
20
20
|
blocking: z.ZodDefault<z.ZodBoolean>;
|
|
21
21
|
thread_id: z.ZodOptional<z.ZodString>;
|
|
@@ -16,9 +16,9 @@ export declare const updateTaskStatusSchema: {
|
|
|
16
16
|
}>;
|
|
17
17
|
priority: z.ZodOptional<z.ZodEnum<{
|
|
18
18
|
low: "low";
|
|
19
|
+
medium: "medium";
|
|
19
20
|
high: "high";
|
|
20
21
|
urgent: "urgent";
|
|
21
|
-
medium: "medium";
|
|
22
22
|
}>>;
|
|
23
23
|
assignee: z.ZodOptional<z.ZodString>;
|
|
24
24
|
agent_id: z.ZodOptional<z.ZodString>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gwdn/nexus-mcp",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.9",
|
|
4
4
|
"description": "MCP server for the Nexus platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/server.js",
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"nexus-mcp": "dist/server.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
|
-
"build": "tsc",
|
|
11
|
+
"build": "tsc && chmod +x dist/server.js",
|
|
12
|
+
"prepublishOnly": "npm run build",
|
|
12
13
|
"dev": "tsx src/server.ts",
|
|
13
14
|
"start": "node dist/server.js",
|
|
14
15
|
"test": "vitest run",
|
|
@@ -25,6 +26,11 @@
|
|
|
25
26
|
"typescript": "^5.8.3",
|
|
26
27
|
"vitest": "^4.1.4"
|
|
27
28
|
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"README.md",
|
|
32
|
+
"LICENSE"
|
|
33
|
+
],
|
|
28
34
|
"engines": {
|
|
29
35
|
"node": ">=20"
|
|
30
36
|
}
|
package/.github/workflows/ci.yml
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
name: CI
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [main]
|
|
6
|
-
pull_request:
|
|
7
|
-
branches: [main]
|
|
8
|
-
|
|
9
|
-
jobs:
|
|
10
|
-
test:
|
|
11
|
-
name: Test (Node ${{ matrix.node-version }})
|
|
12
|
-
runs-on: blacksmith-2vcpu-ubuntu-2204
|
|
13
|
-
strategy:
|
|
14
|
-
fail-fast: false
|
|
15
|
-
matrix:
|
|
16
|
-
node-version: [20, 22]
|
|
17
|
-
|
|
18
|
-
steps:
|
|
19
|
-
- name: Checkout
|
|
20
|
-
uses: actions/checkout@v4
|
|
21
|
-
|
|
22
|
-
- name: Setup Node.js ${{ matrix.node-version }}
|
|
23
|
-
uses: actions/setup-node@v4
|
|
24
|
-
with:
|
|
25
|
-
node-version: ${{ matrix.node-version }}
|
|
26
|
-
cache: npm
|
|
27
|
-
|
|
28
|
-
- name: Install dependencies
|
|
29
|
-
run: npm ci
|
|
30
|
-
|
|
31
|
-
- name: Typecheck
|
|
32
|
-
run: npm run typecheck
|
|
33
|
-
|
|
34
|
-
- name: Test
|
|
35
|
-
run: npm test
|
|
36
|
-
|
|
37
|
-
- name: Build
|
|
38
|
-
run: npm run build
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
name: Release
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
tags:
|
|
6
|
-
- "v*"
|
|
7
|
-
|
|
8
|
-
permissions:
|
|
9
|
-
contents: write
|
|
10
|
-
|
|
11
|
-
jobs:
|
|
12
|
-
release:
|
|
13
|
-
name: Publish to npm
|
|
14
|
-
runs-on: blacksmith-2vcpu-ubuntu-2204
|
|
15
|
-
|
|
16
|
-
steps:
|
|
17
|
-
- name: Checkout
|
|
18
|
-
uses: actions/checkout@v4
|
|
19
|
-
|
|
20
|
-
- name: Setup Node.js
|
|
21
|
-
uses: actions/setup-node@v4
|
|
22
|
-
with:
|
|
23
|
-
node-version: 22
|
|
24
|
-
cache: npm
|
|
25
|
-
registry-url: https://registry.npmjs.org
|
|
26
|
-
|
|
27
|
-
- name: Install dependencies
|
|
28
|
-
run: npm ci
|
|
29
|
-
|
|
30
|
-
- name: Typecheck
|
|
31
|
-
run: npm run typecheck
|
|
32
|
-
|
|
33
|
-
- name: Test
|
|
34
|
-
run: npm test
|
|
35
|
-
|
|
36
|
-
- name: Build
|
|
37
|
-
run: npm run build
|
|
38
|
-
|
|
39
|
-
- name: Verify version matches tag
|
|
40
|
-
run: |
|
|
41
|
-
PKG_VERSION="v$(node -p "require('./package.json').version")"
|
|
42
|
-
TAG_VERSION="${GITHUB_REF#refs/tags/}"
|
|
43
|
-
if [ "$PKG_VERSION" != "$TAG_VERSION" ]; then
|
|
44
|
-
echo "ERROR: package.json version ($PKG_VERSION) does not match tag ($TAG_VERSION)"
|
|
45
|
-
exit 1
|
|
46
|
-
fi
|
|
47
|
-
echo "Version verified: $TAG_VERSION"
|
|
48
|
-
|
|
49
|
-
- name: Publish to npm
|
|
50
|
-
run: npm publish --access public
|
|
51
|
-
env:
|
|
52
|
-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
53
|
-
|
|
54
|
-
- name: Create GitHub Release
|
|
55
|
-
uses: softprops/action-gh-release@v2
|
|
56
|
-
with:
|
|
57
|
-
generate_release_notes: true
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for src/auth.ts
|
|
3
|
-
*
|
|
4
|
-
* Covers:
|
|
5
|
-
* - initIdentity() with valid/invalid tokens
|
|
6
|
-
* - getIdentity() before/after init
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
10
|
-
|
|
11
|
-
// Mock the nexus-api module before importing auth
|
|
12
|
-
vi.mock('../nexus-api.js', () => ({
|
|
13
|
-
nexusGet: vi.fn(),
|
|
14
|
-
}))
|
|
15
|
-
|
|
16
|
-
import { nexusGet } from '../nexus-api.js'
|
|
17
|
-
import { TEST_IDS } from './helpers'
|
|
18
|
-
|
|
19
|
-
describe('MCP Auth', () => {
|
|
20
|
-
beforeEach(() => {
|
|
21
|
-
vi.resetModules()
|
|
22
|
-
vi.restoreAllMocks()
|
|
23
|
-
// Suppress console.error
|
|
24
|
-
vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
describe('initIdentity', () => {
|
|
28
|
-
it('should resolve identity from a valid token', async () => {
|
|
29
|
-
vi.mocked(nexusGet).mockResolvedValue({
|
|
30
|
-
ok: true,
|
|
31
|
-
status: 200,
|
|
32
|
-
data: {
|
|
33
|
-
userId: TEST_IDS.userId,
|
|
34
|
-
email: 'test@example.com',
|
|
35
|
-
displayName: 'Test User',
|
|
36
|
-
isPlatformAdmin: true,
|
|
37
|
-
isPlatformOwner: false,
|
|
38
|
-
tenantId: TEST_IDS.tenantId,
|
|
39
|
-
memberships: [],
|
|
40
|
-
agentAssignments: [],
|
|
41
|
-
},
|
|
42
|
-
error: null,
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
process.env.NEXUS_PRIVATE_TOKEN =
|
|
46
|
-
'nxs_pat_valid-test-token-1234567890abcde'
|
|
47
|
-
|
|
48
|
-
const { initIdentity } = await import('../auth.js')
|
|
49
|
-
const identity = await initIdentity()
|
|
50
|
-
|
|
51
|
-
expect(identity).toBeDefined()
|
|
52
|
-
expect(identity.userId).toBe(TEST_IDS.userId)
|
|
53
|
-
expect(identity.displayName).toBe('Test User')
|
|
54
|
-
expect(identity.email).toBe('test@example.com')
|
|
55
|
-
expect(identity.isPlatformAdmin).toBe(true)
|
|
56
|
-
expect(identity.isPlatformOwner).toBe(false)
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
it('should throw if NEXUS_PRIVATE_TOKEN is not set', async () => {
|
|
60
|
-
delete process.env.NEXUS_PRIVATE_TOKEN
|
|
61
|
-
|
|
62
|
-
const { initIdentity } = await import('../auth.js')
|
|
63
|
-
await expect(initIdentity()).rejects.toThrow(
|
|
64
|
-
'NEXUS_PRIVATE_TOKEN is not set',
|
|
65
|
-
)
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
it('should throw if API returns an error', async () => {
|
|
69
|
-
vi.mocked(nexusGet).mockResolvedValue({
|
|
70
|
-
ok: false,
|
|
71
|
-
status: 401,
|
|
72
|
-
data: null,
|
|
73
|
-
error: 'Invalid token',
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
process.env.NEXUS_PRIVATE_TOKEN = 'nxs_pat_invalid-token-123456789012345'
|
|
77
|
-
|
|
78
|
-
const { initIdentity } = await import('../auth.js')
|
|
79
|
-
await expect(initIdentity()).rejects.toThrow(
|
|
80
|
-
'Identity resolution failed',
|
|
81
|
-
)
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
it('should throw if API returns null data', async () => {
|
|
85
|
-
vi.mocked(nexusGet).mockResolvedValue({
|
|
86
|
-
ok: true,
|
|
87
|
-
status: 200,
|
|
88
|
-
data: null,
|
|
89
|
-
error: null,
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
process.env.NEXUS_PRIVATE_TOKEN = 'nxs_pat_null-data-token-12345678901234'
|
|
93
|
-
|
|
94
|
-
const { initIdentity } = await import('../auth.js')
|
|
95
|
-
await expect(initIdentity()).rejects.toThrow(
|
|
96
|
-
'Identity resolution failed',
|
|
97
|
-
)
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
it('should resolve platform_owner identity with both admin and owner flags', async () => {
|
|
101
|
-
vi.mocked(nexusGet).mockResolvedValue({
|
|
102
|
-
ok: true,
|
|
103
|
-
status: 200,
|
|
104
|
-
data: {
|
|
105
|
-
userId: TEST_IDS.userId,
|
|
106
|
-
email: 'owner@example.com',
|
|
107
|
-
displayName: 'Platform Owner',
|
|
108
|
-
isPlatformAdmin: true,
|
|
109
|
-
isPlatformOwner: true,
|
|
110
|
-
tenantId: TEST_IDS.tenantId,
|
|
111
|
-
memberships: [],
|
|
112
|
-
agentAssignments: [],
|
|
113
|
-
},
|
|
114
|
-
error: null,
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
process.env.NEXUS_PRIVATE_TOKEN = 'nxs_pat_owner-test-token-12345678901'
|
|
118
|
-
|
|
119
|
-
const { initIdentity } = await import('../auth.js')
|
|
120
|
-
const identity = await initIdentity()
|
|
121
|
-
|
|
122
|
-
expect(identity.isPlatformAdmin).toBe(true)
|
|
123
|
-
expect(identity.isPlatformOwner).toBe(true)
|
|
124
|
-
expect(identity.displayName).toBe('Platform Owner')
|
|
125
|
-
expect(identity.email).toBe('owner@example.com')
|
|
126
|
-
})
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
describe('getIdentity', () => {
|
|
130
|
-
it('should throw if called before initIdentity', async () => {
|
|
131
|
-
const { getIdentity } = await import('../auth.js')
|
|
132
|
-
expect(() => getIdentity()).toThrow('Identity not initialized')
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
it('should return cached identity after init', async () => {
|
|
136
|
-
vi.mocked(nexusGet).mockResolvedValue({
|
|
137
|
-
ok: true,
|
|
138
|
-
status: 200,
|
|
139
|
-
data: {
|
|
140
|
-
userId: TEST_IDS.userId,
|
|
141
|
-
email: 'cached@example.com',
|
|
142
|
-
displayName: 'Cached User',
|
|
143
|
-
isPlatformAdmin: false,
|
|
144
|
-
isPlatformOwner: false,
|
|
145
|
-
tenantId: TEST_IDS.tenantId,
|
|
146
|
-
memberships: [],
|
|
147
|
-
agentAssignments: [],
|
|
148
|
-
},
|
|
149
|
-
error: null,
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
process.env.NEXUS_PRIVATE_TOKEN = 'nxs_pat_cached-test-token-1234567890'
|
|
153
|
-
|
|
154
|
-
const { initIdentity, getIdentity } = await import('../auth.js')
|
|
155
|
-
await initIdentity()
|
|
156
|
-
|
|
157
|
-
const identity = getIdentity()
|
|
158
|
-
expect(identity.userId).toBe(TEST_IDS.userId)
|
|
159
|
-
expect(identity.displayName).toBe('Cached User')
|
|
160
|
-
})
|
|
161
|
-
})
|
|
162
|
-
})
|
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for decision comment tools.
|
|
3
|
-
* All tools delegate to the Nexus API via nexusPost().
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
7
|
-
import { mockApiError, mockApiSuccess, parseToolResponse, TEST_IDS } from './helpers'
|
|
8
|
-
|
|
9
|
-
vi.mock('../nexus-api.js', () => ({
|
|
10
|
-
nexusPost: vi.fn(),
|
|
11
|
-
}))
|
|
12
|
-
|
|
13
|
-
import { nexusPost } from '../nexus-api.js'
|
|
14
|
-
|
|
15
|
-
describe('Decision Comments tools', () => {
|
|
16
|
-
beforeEach(() => {
|
|
17
|
-
vi.restoreAllMocks()
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
describe('addDecisionComment', () => {
|
|
21
|
-
it('should add a comment to an existing decision', async () => {
|
|
22
|
-
vi.mocked(nexusPost).mockResolvedValue(
|
|
23
|
-
mockApiSuccess({
|
|
24
|
-
action: 'dc_add',
|
|
25
|
-
comment_id: 'comment-id-1',
|
|
26
|
-
decision_id: TEST_IDS.adrId,
|
|
27
|
-
decision_title: 'Test ADR',
|
|
28
|
-
project_id: TEST_IDS.projectId,
|
|
29
|
-
}),
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
const { addDecisionComment } = await import('../tools/decision-comments.js')
|
|
33
|
-
const result = await addDecisionComment({
|
|
34
|
-
decision_id: TEST_IDS.adrId,
|
|
35
|
-
body: 'This is a comment',
|
|
36
|
-
user_id: TEST_IDS.userId,
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
expect(result.isError).toBeUndefined()
|
|
40
|
-
const parsed = parseToolResponse(result)
|
|
41
|
-
expect(parsed.action).toBe('dc_add')
|
|
42
|
-
expect(parsed.comment_id).toBe('comment-id-1')
|
|
43
|
-
expect(parsed.decision_id).toBe(TEST_IDS.adrId)
|
|
44
|
-
expect(parsed.decision_title).toBe('Test ADR')
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
it('should support agent_id in comment', async () => {
|
|
48
|
-
vi.mocked(nexusPost).mockResolvedValue(
|
|
49
|
-
mockApiSuccess({
|
|
50
|
-
action: 'dc_add',
|
|
51
|
-
comment_id: 'comment-id-2',
|
|
52
|
-
decision_id: TEST_IDS.adrId,
|
|
53
|
-
decision_title: 'ADR',
|
|
54
|
-
project_id: TEST_IDS.projectId,
|
|
55
|
-
}),
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
const { addDecisionComment } = await import('../tools/decision-comments.js')
|
|
59
|
-
const result = await addDecisionComment({
|
|
60
|
-
decision_id: TEST_IDS.adrId,
|
|
61
|
-
body: 'Agent comment',
|
|
62
|
-
agent_id: TEST_IDS.agentId,
|
|
63
|
-
user_id: TEST_IDS.userId,
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
expect(result.isError).toBeUndefined()
|
|
67
|
-
// Verify agent_id was passed to the API
|
|
68
|
-
expect(nexusPost).toHaveBeenCalledWith('/api/mcp/governance', {
|
|
69
|
-
action: 'dc_add',
|
|
70
|
-
decision_id: TEST_IDS.adrId,
|
|
71
|
-
body: 'Agent comment',
|
|
72
|
-
agent_id: TEST_IDS.agentId,
|
|
73
|
-
})
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
it('should return error if decision not found', async () => {
|
|
77
|
-
vi.mocked(nexusPost).mockResolvedValue(mockApiError('Decision not found', 404))
|
|
78
|
-
|
|
79
|
-
const { addDecisionComment } = await import('../tools/decision-comments.js')
|
|
80
|
-
const result = await addDecisionComment({
|
|
81
|
-
decision_id: TEST_IDS.adrId,
|
|
82
|
-
body: 'Comment',
|
|
83
|
-
user_id: TEST_IDS.userId,
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
expect(result.isError).toBe(true)
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
it('should return error on API failure', async () => {
|
|
90
|
-
vi.mocked(nexusPost).mockResolvedValue(mockApiError('Failed to add comment'))
|
|
91
|
-
|
|
92
|
-
const { addDecisionComment } = await import('../tools/decision-comments.js')
|
|
93
|
-
const result = await addDecisionComment({
|
|
94
|
-
decision_id: TEST_IDS.adrId,
|
|
95
|
-
body: 'Comment',
|
|
96
|
-
user_id: TEST_IDS.userId,
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
expect(result.isError).toBe(true)
|
|
100
|
-
})
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
describe('listDecisionComments', () => {
|
|
104
|
-
it('should list comments for a decision', async () => {
|
|
105
|
-
vi.mocked(nexusPost).mockResolvedValue(
|
|
106
|
-
mockApiSuccess({
|
|
107
|
-
decision_id: TEST_IDS.adrId,
|
|
108
|
-
decision_title: 'Test ADR',
|
|
109
|
-
total: 2,
|
|
110
|
-
comments: [
|
|
111
|
-
{
|
|
112
|
-
id: 'c1',
|
|
113
|
-
author_id: TEST_IDS.userId,
|
|
114
|
-
agent_id: null,
|
|
115
|
-
body: 'First comment',
|
|
116
|
-
created_at: '2026-01-01T00:00:00Z',
|
|
117
|
-
},
|
|
118
|
-
{
|
|
119
|
-
id: 'c2',
|
|
120
|
-
author_id: TEST_IDS.userId,
|
|
121
|
-
agent_id: TEST_IDS.agentId,
|
|
122
|
-
body: 'Second comment',
|
|
123
|
-
created_at: '2026-01-01T01:00:00Z',
|
|
124
|
-
},
|
|
125
|
-
],
|
|
126
|
-
}),
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
const { listDecisionComments } = await import('../tools/decision-comments.js')
|
|
130
|
-
const result = await listDecisionComments({
|
|
131
|
-
decision_id: TEST_IDS.adrId,
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
expect(result.isError).toBeUndefined()
|
|
135
|
-
const parsed = parseToolResponse(result)
|
|
136
|
-
expect(parsed.decision_title).toBe('Test ADR')
|
|
137
|
-
expect(parsed.total).toBe(2)
|
|
138
|
-
expect(parsed.comments).toHaveLength(2)
|
|
139
|
-
expect(parsed.comments[0].body).toBe('First comment')
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
it('should return empty list for decision with no comments', async () => {
|
|
143
|
-
vi.mocked(nexusPost).mockResolvedValue(
|
|
144
|
-
mockApiSuccess({
|
|
145
|
-
decision_id: TEST_IDS.adrId,
|
|
146
|
-
decision_title: 'ADR',
|
|
147
|
-
total: 0,
|
|
148
|
-
comments: [],
|
|
149
|
-
}),
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
const { listDecisionComments } = await import('../tools/decision-comments.js')
|
|
153
|
-
const result = await listDecisionComments({
|
|
154
|
-
decision_id: TEST_IDS.adrId,
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
const parsed = parseToolResponse(result)
|
|
158
|
-
expect(parsed.total).toBe(0)
|
|
159
|
-
expect(parsed.comments).toHaveLength(0)
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
it('should return error if decision not found', async () => {
|
|
163
|
-
vi.mocked(nexusPost).mockResolvedValue(mockApiError('Decision not found', 404))
|
|
164
|
-
|
|
165
|
-
const { listDecisionComments } = await import('../tools/decision-comments.js')
|
|
166
|
-
const result = await listDecisionComments({
|
|
167
|
-
decision_id: TEST_IDS.adrId,
|
|
168
|
-
})
|
|
169
|
-
|
|
170
|
-
expect(result.isError).toBe(true)
|
|
171
|
-
})
|
|
172
|
-
})
|
|
173
|
-
})
|