@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.
Files changed (53) hide show
  1. package/dist/server.js +1 -1
  2. package/dist/tools/create-task.d.ts +1 -1
  3. package/dist/tools/create-task.js +3 -3
  4. package/dist/tools/directives.d.ts +2 -2
  5. package/dist/tools/letters.d.ts +1 -1
  6. package/dist/tools/update-task-status.d.ts +1 -1
  7. package/package.json +8 -2
  8. package/.github/workflows/ci.yml +0 -38
  9. package/.github/workflows/release.yml +0 -57
  10. package/src/__tests__/auth.test.ts +0 -162
  11. package/src/__tests__/decision-comments.test.ts +0 -173
  12. package/src/__tests__/e2e-integration.test.ts +0 -443
  13. package/src/__tests__/helpers.ts +0 -58
  14. package/src/__tests__/layer1-knowledge.test.ts +0 -302
  15. package/src/__tests__/layer2-coordination.test.ts +0 -918
  16. package/src/__tests__/layer3-governance.test.ts +0 -205
  17. package/src/__tests__/machine-id.test.ts +0 -136
  18. package/src/__tests__/nexus-api.test.ts +0 -127
  19. package/src/__tests__/project-list-and-skill-assign.test.ts +0 -282
  20. package/src/__tests__/reviews.test.ts +0 -420
  21. package/src/__tests__/server.test.ts +0 -251
  22. package/src/__tests__/setup.ts +0 -15
  23. package/src/__tests__/task-list-and-doc-list.test.ts +0 -226
  24. package/src/auth.ts +0 -81
  25. package/src/machine-id.ts +0 -60
  26. package/src/nexus-api.ts +0 -149
  27. package/src/server.ts +0 -629
  28. package/src/tools/add-task-note.ts +0 -50
  29. package/src/tools/append-session-entry.ts +0 -113
  30. package/src/tools/classify-document.ts +0 -55
  31. package/src/tools/create-task.ts +0 -66
  32. package/src/tools/decision-comments.ts +0 -103
  33. package/src/tools/delete-document.ts +0 -43
  34. package/src/tools/directives.ts +0 -303
  35. package/src/tools/get-document.ts +0 -80
  36. package/src/tools/get-project-memory.ts +0 -65
  37. package/src/tools/get-related-entities.ts +0 -73
  38. package/src/tools/governance.ts +0 -165
  39. package/src/tools/ingest-document.ts +0 -65
  40. package/src/tools/letter-inbox.ts +0 -157
  41. package/src/tools/letters.ts +0 -144
  42. package/src/tools/list-documents.ts +0 -58
  43. package/src/tools/list-tasks.ts +0 -57
  44. package/src/tools/project-list.ts +0 -52
  45. package/src/tools/reviews.ts +0 -281
  46. package/src/tools/search-knowledge.ts +0 -68
  47. package/src/tools/sessions.ts +0 -157
  48. package/src/tools/skill-assign.ts +0 -142
  49. package/src/tools/skills.ts +0 -255
  50. package/src/tools/update-document.ts +0 -66
  51. package/src/tools/update-task-status.ts +0 -64
  52. package/tsconfig.json +0 -21
  53. 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.7',
124
+ version: '0.8.9',
125
125
  }, {
126
126
  capabilities: {
127
127
  tools: {},
@@ -11,7 +11,7 @@ export declare const createTaskSchema: {
11
11
  description: z.ZodOptional<z.ZodString>;
12
12
  priority: z.ZodDefault<z.ZodEnum<{
13
13
  low: "low";
14
- normal: "normal";
14
+ medium: "medium";
15
15
  high: "high";
16
16
  urgent: "urgent";
17
17
  }>>;
@@ -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', 'normal', 'high', 'urgent'])
15
- .default('normal')
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 ?? 'normal',
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
  };
@@ -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.7",
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
  }
@@ -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
- })