@mpowr/nexus-mcp 0.5.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 (119) hide show
  1. package/README.md +59 -0
  2. package/dist/auth.d.ts +39 -0
  3. package/dist/auth.d.ts.map +1 -0
  4. package/dist/auth.js +47 -0
  5. package/dist/auth.js.map +1 -0
  6. package/dist/nexus-api.d.ts +29 -0
  7. package/dist/nexus-api.d.ts.map +1 -0
  8. package/dist/nexus-api.js +76 -0
  9. package/dist/nexus-api.js.map +1 -0
  10. package/dist/server.d.ts +65 -0
  11. package/dist/server.d.ts.map +1 -0
  12. package/dist/server.js +183 -0
  13. package/dist/server.js.map +1 -0
  14. package/dist/tools/add-task-note.d.ts +34 -0
  15. package/dist/tools/add-task-note.d.ts.map +1 -0
  16. package/dist/tools/add-task-note.js +39 -0
  17. package/dist/tools/add-task-note.js.map +1 -0
  18. package/dist/tools/append-session-entry.d.ts +53 -0
  19. package/dist/tools/append-session-entry.d.ts.map +1 -0
  20. package/dist/tools/append-session-entry.js +67 -0
  21. package/dist/tools/append-session-entry.js.map +1 -0
  22. package/dist/tools/create-task.d.ts +52 -0
  23. package/dist/tools/create-task.d.ts.map +1 -0
  24. package/dist/tools/create-task.js +51 -0
  25. package/dist/tools/create-task.js.map +1 -0
  26. package/dist/tools/decision-comments.d.ts +54 -0
  27. package/dist/tools/decision-comments.d.ts.map +1 -0
  28. package/dist/tools/decision-comments.js +80 -0
  29. package/dist/tools/decision-comments.js.map +1 -0
  30. package/dist/tools/get-document.d.ts +47 -0
  31. package/dist/tools/get-document.d.ts.map +1 -0
  32. package/dist/tools/get-document.js +68 -0
  33. package/dist/tools/get-document.js.map +1 -0
  34. package/dist/tools/get-project-memory.d.ts +47 -0
  35. package/dist/tools/get-project-memory.d.ts.map +1 -0
  36. package/dist/tools/get-project-memory.js +53 -0
  37. package/dist/tools/get-project-memory.js.map +1 -0
  38. package/dist/tools/get-related-entities.d.ts +44 -0
  39. package/dist/tools/get-related-entities.d.ts.map +1 -0
  40. package/dist/tools/get-related-entities.js +60 -0
  41. package/dist/tools/get-related-entities.js.map +1 -0
  42. package/dist/tools/governance.d.ts +90 -0
  43. package/dist/tools/governance.d.ts.map +1 -0
  44. package/dist/tools/governance.js +124 -0
  45. package/dist/tools/governance.js.map +1 -0
  46. package/dist/tools/ingest-document.d.ts +40 -0
  47. package/dist/tools/ingest-document.d.ts.map +1 -0
  48. package/dist/tools/ingest-document.js +48 -0
  49. package/dist/tools/ingest-document.js.map +1 -0
  50. package/dist/tools/letter-inbox.d.ts +80 -0
  51. package/dist/tools/letter-inbox.d.ts.map +1 -0
  52. package/dist/tools/letter-inbox.js +118 -0
  53. package/dist/tools/letter-inbox.js.map +1 -0
  54. package/dist/tools/letters.d.ts +91 -0
  55. package/dist/tools/letters.d.ts.map +1 -0
  56. package/dist/tools/letters.js +112 -0
  57. package/dist/tools/letters.js.map +1 -0
  58. package/dist/tools/project-list.d.ts +28 -0
  59. package/dist/tools/project-list.d.ts.map +1 -0
  60. package/dist/tools/project-list.js +43 -0
  61. package/dist/tools/project-list.js.map +1 -0
  62. package/dist/tools/reviews.d.ts +145 -0
  63. package/dist/tools/reviews.d.ts.map +1 -0
  64. package/dist/tools/reviews.js +216 -0
  65. package/dist/tools/reviews.js.map +1 -0
  66. package/dist/tools/search-knowledge.d.ts +48 -0
  67. package/dist/tools/search-knowledge.d.ts.map +1 -0
  68. package/dist/tools/search-knowledge.js +54 -0
  69. package/dist/tools/search-knowledge.js.map +1 -0
  70. package/dist/tools/sessions.d.ts +81 -0
  71. package/dist/tools/sessions.d.ts.map +1 -0
  72. package/dist/tools/sessions.js +120 -0
  73. package/dist/tools/sessions.js.map +1 -0
  74. package/dist/tools/skill-assign.d.ts +77 -0
  75. package/dist/tools/skill-assign.d.ts.map +1 -0
  76. package/dist/tools/skill-assign.js +108 -0
  77. package/dist/tools/skill-assign.js.map +1 -0
  78. package/dist/tools/skills.d.ts +138 -0
  79. package/dist/tools/skills.d.ts.map +1 -0
  80. package/dist/tools/skills.js +192 -0
  81. package/dist/tools/skills.js.map +1 -0
  82. package/dist/tools/update-task-status.d.ts +48 -0
  83. package/dist/tools/update-task-status.d.ts.map +1 -0
  84. package/dist/tools/update-task-status.js +51 -0
  85. package/dist/tools/update-task-status.js.map +1 -0
  86. package/package.json +30 -0
  87. package/src/__tests__/auth.test.ts +162 -0
  88. package/src/__tests__/decision-comments.test.ts +173 -0
  89. package/src/__tests__/helpers.ts +58 -0
  90. package/src/__tests__/layer1-knowledge.test.ts +302 -0
  91. package/src/__tests__/layer2-coordination.test.ts +775 -0
  92. package/src/__tests__/layer3-governance.test.ts +205 -0
  93. package/src/__tests__/project-list-and-skill-assign.test.ts +282 -0
  94. package/src/__tests__/reviews.test.ts +420 -0
  95. package/src/__tests__/server.test.ts +238 -0
  96. package/src/__tests__/setup.ts +15 -0
  97. package/src/auth.ts +81 -0
  98. package/src/nexus-api.ts +110 -0
  99. package/src/server.ts +499 -0
  100. package/src/tools/add-task-note.ts +50 -0
  101. package/src/tools/append-session-entry.ts +83 -0
  102. package/src/tools/create-task.ts +66 -0
  103. package/src/tools/decision-comments.ts +102 -0
  104. package/src/tools/get-document.ts +80 -0
  105. package/src/tools/get-project-memory.ts +65 -0
  106. package/src/tools/get-related-entities.ts +73 -0
  107. package/src/tools/governance.ts +162 -0
  108. package/src/tools/ingest-document.ts +64 -0
  109. package/src/tools/letter-inbox.ts +157 -0
  110. package/src/tools/letters.ts +144 -0
  111. package/src/tools/project-list.ts +52 -0
  112. package/src/tools/reviews.ts +277 -0
  113. package/src/tools/search-knowledge.ts +68 -0
  114. package/src/tools/sessions.ts +154 -0
  115. package/src/tools/skill-assign.ts +142 -0
  116. package/src/tools/skills.ts +252 -0
  117. package/src/tools/update-task-status.ts +64 -0
  118. package/tsconfig.json +20 -0
  119. package/vitest.config.ts +8 -0
@@ -0,0 +1,50 @@
1
+ /**
2
+ * task_note -- Layer 2 Coordination
3
+ *
4
+ * Appends a note to an existing task. Notes are append-only and
5
+ * maintain a chronological history/timeline.
6
+ * Delegates to POST /api/mcp/tasks (action: task_note).
7
+ */
8
+
9
+ import { z } from 'zod'
10
+ import { nexusPost } from '../nexus-api.js'
11
+
12
+ export const addTaskNoteSchema = {
13
+ task_id: z.string().uuid().describe('Task UUID to add a note to'),
14
+ note: z.string().describe('Note content to append'),
15
+ agent_id: z.string().optional().describe('Agent identifier if applicable'),
16
+ }
17
+
18
+ type AddTaskNoteArgs = {
19
+ task_id: string
20
+ note: string
21
+ user_id: string
22
+ agent_id?: string
23
+ }
24
+
25
+ export async function addTaskNote(args: AddTaskNoteArgs) {
26
+ const result = await nexusPost('/api/mcp/tasks', {
27
+ action: 'task_note',
28
+ task_id: args.task_id,
29
+ note: args.note,
30
+ agent_id: args.agent_id,
31
+ })
32
+
33
+ if (!result.ok) {
34
+ return {
35
+ content: [
36
+ {
37
+ type: 'text' as const,
38
+ text: JSON.stringify({ error: result.error }, null, 2),
39
+ },
40
+ ],
41
+ isError: true,
42
+ }
43
+ }
44
+
45
+ return {
46
+ content: [
47
+ { type: 'text' as const, text: JSON.stringify(result.data, null, 2) },
48
+ ],
49
+ }
50
+ }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * session_append -- Layer 2 Coordination
3
+ *
4
+ * Appends an entry to an existing session.
5
+ * Enforces append-only semantics and session write isolation.
6
+ * Delegates to POST /api/mcp/sessions (action: session_append).
7
+ */
8
+
9
+ import { z } from 'zod'
10
+ import { nexusPost } from '../nexus-api.js'
11
+
12
+ export const appendSessionEntrySchema = {
13
+ session_id: z.string().uuid().describe('Session UUID to append to'),
14
+ entry_type: z
15
+ .enum([
16
+ 'decision_referenced',
17
+ 'task_created',
18
+ 'task_updated',
19
+ 'letter_sent',
20
+ 'letter_replied',
21
+ 'research_added',
22
+ 'conflict_detected',
23
+ 'adr_drafted',
24
+ 'adr_accepted',
25
+ 'handoff_recorded',
26
+ 'note',
27
+ 'correction',
28
+ ])
29
+ .describe('Type of session entry'),
30
+ summary: z.string().describe('Entry content / summary text'),
31
+ linked_entity_type: z
32
+ .string()
33
+ .optional()
34
+ .describe(
35
+ 'Type of linked entity (e.g., "task", "decision", "letter", "research_link")',
36
+ ),
37
+ linked_entity_id: z
38
+ .string()
39
+ .uuid()
40
+ .optional()
41
+ .describe('UUID of the linked entity'),
42
+ agent_id: z.string().optional().describe('Agent identifier if applicable'),
43
+ }
44
+
45
+ type AppendSessionEntryArgs = {
46
+ session_id: string
47
+ entry_type: string
48
+ summary: string
49
+ linked_entity_type?: string
50
+ linked_entity_id?: string
51
+ user_id: string
52
+ agent_id?: string
53
+ }
54
+
55
+ export async function appendSessionEntry(args: AppendSessionEntryArgs) {
56
+ const result = await nexusPost('/api/mcp/sessions', {
57
+ action: 'session_append',
58
+ session_id: args.session_id,
59
+ entry_type: args.entry_type,
60
+ summary: args.summary,
61
+ agent_id: args.agent_id,
62
+ linked_entity_type: args.linked_entity_type,
63
+ linked_entity_id: args.linked_entity_id,
64
+ })
65
+
66
+ if (!result.ok) {
67
+ return {
68
+ content: [
69
+ {
70
+ type: 'text' as const,
71
+ text: JSON.stringify({ error: result.error }, null, 2),
72
+ },
73
+ ],
74
+ isError: true,
75
+ }
76
+ }
77
+
78
+ return {
79
+ content: [
80
+ { type: 'text' as const, text: JSON.stringify(result.data, null, 2) },
81
+ ],
82
+ }
83
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * task_create -- Layer 2 Coordination
3
+ *
4
+ * Creates a new task within a project scope.
5
+ * Delegates to POST /api/mcp/tasks (action: task_create).
6
+ */
7
+
8
+ import { z } from 'zod'
9
+ import { nexusPost } from '../nexus-api.js'
10
+
11
+ export const createTaskSchema = {
12
+ project_id: z.string().uuid().describe('Project UUID'),
13
+ title: z.string().describe('Task title'),
14
+ description: z.string().optional().describe('Task description'),
15
+ priority: z
16
+ .enum(['low', 'normal', 'high', 'urgent'])
17
+ .default('normal')
18
+ .describe('Task priority'),
19
+ assignee: z.string().uuid().optional().describe('UUID of the assigned user'),
20
+ status: z
21
+ .enum(['open', 'in_progress', 'blocked', 'done', 'cancelled'])
22
+ .default('open')
23
+ .describe('Initial task status'),
24
+ agent_id: z.string().optional().describe('Agent identifier if applicable'),
25
+ }
26
+
27
+ type CreateTaskArgs = {
28
+ project_id: string
29
+ title: string
30
+ description?: string
31
+ priority?: string
32
+ assignee?: string
33
+ status?: string
34
+ user_id: string
35
+ agent_id?: string
36
+ }
37
+
38
+ export async function createTask(args: CreateTaskArgs) {
39
+ const result = await nexusPost('/api/mcp/tasks', {
40
+ action: 'task_create',
41
+ project_id: args.project_id,
42
+ title: args.title,
43
+ description: args.description,
44
+ priority: args.priority ?? 'normal',
45
+ assignee: args.assignee,
46
+ status: args.status ?? 'open',
47
+ })
48
+
49
+ if (!result.ok) {
50
+ return {
51
+ content: [
52
+ {
53
+ type: 'text' as const,
54
+ text: JSON.stringify({ error: result.error }, null, 2),
55
+ },
56
+ ],
57
+ isError: true,
58
+ }
59
+ }
60
+
61
+ return {
62
+ content: [
63
+ { type: 'text' as const, text: JSON.stringify(result.data, null, 2) },
64
+ ],
65
+ }
66
+ }
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Decision comment tools -- Layer 2 Coordination
3
+ *
4
+ * Provides append-only comment threads for ADR decisions.
5
+ * Delegates to POST /api/mcp/governance (actions: dc_add, dc_list).
6
+ */
7
+
8
+ import { z } from 'zod'
9
+ import { nexusPost } from '../nexus-api.js'
10
+
11
+ // ---------------------------------------------------------------------------
12
+ // add_decision_comment -> dc_add
13
+ // ---------------------------------------------------------------------------
14
+
15
+ export const addDecisionCommentSchema = {
16
+ decision_id: z.string().uuid().describe('Decision UUID to comment on'),
17
+ body: z.string().describe('Comment body (markdown supported)'),
18
+ agent_id: z
19
+ .string()
20
+ .optional()
21
+ .describe('Agent identifier if comment is posted by an agent'),
22
+ }
23
+
24
+ type AddDecisionCommentArgs = {
25
+ decision_id: string
26
+ body: string
27
+ agent_id?: string
28
+ user_id: string
29
+ }
30
+
31
+ export async function addDecisionComment(args: AddDecisionCommentArgs) {
32
+ const result = await nexusPost('/api/mcp/governance', {
33
+ action: 'dc_add',
34
+ decision_id: args.decision_id,
35
+ body: args.body,
36
+ agent_id: args.agent_id,
37
+ })
38
+
39
+ if (!result.ok) {
40
+ return {
41
+ content: [
42
+ {
43
+ type: 'text' as const,
44
+ text: JSON.stringify({ error: result.error }, null, 2),
45
+ },
46
+ ],
47
+ isError: true,
48
+ }
49
+ }
50
+
51
+ return {
52
+ content: [
53
+ { type: 'text' as const, text: JSON.stringify(result.data, null, 2) },
54
+ ],
55
+ }
56
+ }
57
+
58
+ // ---------------------------------------------------------------------------
59
+ // list_decision_comments -> dc_list
60
+ // ---------------------------------------------------------------------------
61
+
62
+ export const listDecisionCommentsSchema = {
63
+ decision_id: z.string().uuid().describe('Decision UUID'),
64
+ limit: z
65
+ .number()
66
+ .int()
67
+ .min(1)
68
+ .max(100)
69
+ .default(50)
70
+ .describe('Maximum comments to return'),
71
+ }
72
+
73
+ type ListDecisionCommentsArgs = {
74
+ decision_id: string
75
+ limit?: number
76
+ }
77
+
78
+ export async function listDecisionComments(args: ListDecisionCommentsArgs) {
79
+ const result = await nexusPost('/api/mcp/governance', {
80
+ action: 'dc_list',
81
+ decision_id: args.decision_id,
82
+ limit: args.limit ?? 50,
83
+ })
84
+
85
+ if (!result.ok) {
86
+ return {
87
+ content: [
88
+ {
89
+ type: 'text' as const,
90
+ text: JSON.stringify({ error: result.error }, null, 2),
91
+ },
92
+ ],
93
+ isError: true,
94
+ }
95
+ }
96
+
97
+ return {
98
+ content: [
99
+ { type: 'text' as const, text: JSON.stringify(result.data, null, 2) },
100
+ ],
101
+ }
102
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * kb_get -- Layer 1 Knowledge Access
3
+ *
4
+ * Fetches a single knowledge object in canonical form.
5
+ * Returns metadata, current version, body, and linked references.
6
+ * Delegates to POST /api/mcp/documents (action: kb_get).
7
+ *
8
+ * ADR-0001 spec: kb_get(entity_type, entity_id, render_mode)
9
+ */
10
+
11
+ import { z } from 'zod'
12
+ import { nexusPost } from '../nexus-api.js'
13
+
14
+ export const getDocumentSchema = {
15
+ entity_type: z
16
+ .enum([
17
+ 'session',
18
+ 'decision',
19
+ 'letter',
20
+ 'task',
21
+ 'research_note',
22
+ 'planning_item',
23
+ 'ingest_item',
24
+ ])
25
+ .describe('Type of the knowledge object'),
26
+ entity_id: z.string().uuid().describe('UUID of the entity'),
27
+ render_mode: z
28
+ .enum(['structured', 'markdown', 'summary'])
29
+ .default('structured')
30
+ .describe('Output format: structured JSON, markdown, or summary'),
31
+ }
32
+
33
+ type GetDocumentArgs = {
34
+ entity_type: string
35
+ entity_id: string
36
+ render_mode?: string
37
+ }
38
+
39
+ export async function getDocument(args: GetDocumentArgs) {
40
+ const result = await nexusPost('/api/mcp/documents', {
41
+ action: 'kb_get',
42
+ entity_type: args.entity_type,
43
+ entity_id: args.entity_id,
44
+ render_mode: args.render_mode ?? 'structured',
45
+ })
46
+
47
+ if (!result.ok) {
48
+ return {
49
+ content: [
50
+ {
51
+ type: 'text' as const,
52
+ text: JSON.stringify({ error: result.error }, null, 2),
53
+ },
54
+ ],
55
+ isError: true,
56
+ }
57
+ }
58
+
59
+ // For markdown and summary modes, the API returns { content: "..." }
60
+ // For structured mode, the API returns the full object
61
+ const data = result.data as Record<string, unknown>
62
+ const renderMode = args.render_mode ?? 'structured'
63
+
64
+ if (renderMode === 'markdown' || renderMode === 'summary') {
65
+ return {
66
+ content: [
67
+ {
68
+ type: 'text' as const,
69
+ text: (data.content as string) ?? JSON.stringify(data, null, 2),
70
+ },
71
+ ],
72
+ }
73
+ }
74
+
75
+ return {
76
+ content: [
77
+ { type: 'text' as const, text: JSON.stringify(data, null, 2) },
78
+ ],
79
+ }
80
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * kb_memory -- Layer 1 Knowledge Access
3
+ *
4
+ * Returns curated project context for agent bootstrapping.
5
+ * Includes ADRs, active tasks, recent sessions, open letters, etc.
6
+ * Delegates to POST /api/mcp/memory.
7
+ *
8
+ * ADR-0001 spec: kb_memory(project_id, include[], depth)
9
+ */
10
+
11
+ import { z } from 'zod'
12
+ import { nexusPost } from '../nexus-api.js'
13
+
14
+ export const getProjectMemorySchema = {
15
+ project_id: z.string().uuid().describe('Project UUID'),
16
+ include: z
17
+ .array(
18
+ z.enum([
19
+ 'adrs',
20
+ 'rules',
21
+ 'active_tasks',
22
+ 'recent_sessions',
23
+ 'open_letters',
24
+ 'planning',
25
+ 'research',
26
+ ]),
27
+ )
28
+ .describe('Categories of knowledge to include'),
29
+ depth: z
30
+ .enum(['light', 'standard', 'deep'])
31
+ .default('standard')
32
+ .describe('Detail level: light (summaries), standard, deep (full bodies)'),
33
+ }
34
+
35
+ type GetProjectMemoryArgs = {
36
+ project_id: string
37
+ include: string[]
38
+ depth?: string
39
+ }
40
+
41
+ export async function getProjectMemory(args: GetProjectMemoryArgs) {
42
+ const result = await nexusPost('/api/mcp/memory', {
43
+ project_id: args.project_id,
44
+ include: args.include,
45
+ depth: args.depth ?? 'standard',
46
+ })
47
+
48
+ if (!result.ok) {
49
+ return {
50
+ content: [
51
+ {
52
+ type: 'text' as const,
53
+ text: JSON.stringify({ error: result.error }, null, 2),
54
+ },
55
+ ],
56
+ isError: true,
57
+ }
58
+ }
59
+
60
+ return {
61
+ content: [
62
+ { type: 'text' as const, text: JSON.stringify(result.data, null, 2) },
63
+ ],
64
+ }
65
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * kb_related -- Layer 1 Knowledge Access
3
+ *
4
+ * Returns graph-neighbor entities related to a given entity.
5
+ * Delegates to POST /api/mcp/related.
6
+ *
7
+ * ADR-0001 spec: kb_related(entity_type, entity_id, relation_types[], limit)
8
+ */
9
+
10
+ import { z } from 'zod'
11
+ import { nexusPost } from '../nexus-api.js'
12
+
13
+ export const getRelatedEntitiesSchema = {
14
+ entity_type: z
15
+ .enum([
16
+ 'session',
17
+ 'decision',
18
+ 'letter',
19
+ 'task',
20
+ 'research_note',
21
+ 'planning_item',
22
+ 'ingest_item',
23
+ ])
24
+ .describe('Type of the source entity'),
25
+ entity_id: z.string().uuid().describe('UUID of the source entity'),
26
+ relation_types: z
27
+ .array(z.string())
28
+ .optional()
29
+ .describe(
30
+ 'Filter by specific relation types (e.g., "references", "created_in", "supersedes")',
31
+ ),
32
+ limit: z
33
+ .number()
34
+ .int()
35
+ .min(1)
36
+ .max(50)
37
+ .default(20)
38
+ .describe('Maximum related entities to return'),
39
+ }
40
+
41
+ type GetRelatedEntitiesArgs = {
42
+ entity_type: string
43
+ entity_id: string
44
+ relation_types?: string[]
45
+ limit?: number
46
+ }
47
+
48
+ export async function getRelatedEntities(args: GetRelatedEntitiesArgs) {
49
+ const result = await nexusPost('/api/mcp/related', {
50
+ entity_type: args.entity_type,
51
+ entity_id: args.entity_id,
52
+ relation_types: args.relation_types,
53
+ limit: args.limit ?? 20,
54
+ })
55
+
56
+ if (!result.ok) {
57
+ return {
58
+ content: [
59
+ {
60
+ type: 'text' as const,
61
+ text: JSON.stringify({ error: result.error }, null, 2),
62
+ },
63
+ ],
64
+ isError: true,
65
+ }
66
+ }
67
+
68
+ return {
69
+ content: [
70
+ { type: 'text' as const, text: JSON.stringify(result.data, null, 2) },
71
+ ],
72
+ }
73
+ }
@@ -0,0 +1,162 @@
1
+ /**
2
+ * ADR Governance tools -- Layer 3
3
+ *
4
+ * Manages the ADR lifecycle:
5
+ * draft -> under_review -> accepted | rejected -> superseded -> archived
6
+ *
7
+ * Delegates to POST /api/mcp/governance.
8
+ */
9
+
10
+ import { z } from 'zod'
11
+ import { nexusPost } from '../nexus-api.js'
12
+
13
+ // ---------------------------------------------------------------------------
14
+ // create_adr_draft -> adr_create
15
+ // ---------------------------------------------------------------------------
16
+
17
+ export const createAdrDraftSchema = {
18
+ project_id: z.string().uuid().describe('Project UUID'),
19
+ title: z.string().describe('ADR title'),
20
+ context: z
21
+ .string()
22
+ .describe('Context / motivation for the decision (markdown)'),
23
+ decision: z.string().describe('The decision content (markdown)'),
24
+ consequences: z
25
+ .string()
26
+ .optional()
27
+ .describe('Expected consequences of the decision (markdown)'),
28
+ supersedes: z
29
+ .string()
30
+ .uuid()
31
+ .optional()
32
+ .describe('UUID of the ADR this one supersedes'),
33
+ agent_id: z.string().optional().describe('Agent identifier if applicable'),
34
+ }
35
+
36
+ type CreateAdrDraftArgs = {
37
+ project_id: string
38
+ title: string
39
+ context: string
40
+ decision: string
41
+ consequences?: string
42
+ supersedes?: string
43
+ user_id: string
44
+ agent_id?: string
45
+ }
46
+
47
+ export async function createAdrDraft(args: CreateAdrDraftArgs) {
48
+ const result = await nexusPost('/api/mcp/governance', {
49
+ action: 'adr_create',
50
+ project_id: args.project_id,
51
+ title: args.title,
52
+ context: args.context,
53
+ decision: args.decision,
54
+ consequences: args.consequences,
55
+ supersedes: args.supersedes,
56
+ })
57
+
58
+ if (!result.ok) {
59
+ return {
60
+ content: [
61
+ {
62
+ type: 'text' as const,
63
+ text: JSON.stringify({ error: result.error }, null, 2),
64
+ },
65
+ ],
66
+ isError: true,
67
+ }
68
+ }
69
+
70
+ return {
71
+ content: [
72
+ { type: 'text' as const, text: JSON.stringify(result.data, null, 2) },
73
+ ],
74
+ }
75
+ }
76
+
77
+ // ---------------------------------------------------------------------------
78
+ // submit_adr_review -> adr_submit
79
+ // ---------------------------------------------------------------------------
80
+
81
+ export const submitAdrReviewSchema = {
82
+ adr_id: z.string().uuid().describe('ADR UUID to submit for review'),
83
+ }
84
+
85
+ type SubmitAdrReviewArgs = {
86
+ adr_id: string
87
+ user_id: string
88
+ }
89
+
90
+ export async function submitAdrReview(args: SubmitAdrReviewArgs) {
91
+ const result = await nexusPost('/api/mcp/governance', {
92
+ action: 'adr_submit',
93
+ adr_id: args.adr_id,
94
+ })
95
+
96
+ if (!result.ok) {
97
+ return {
98
+ content: [
99
+ {
100
+ type: 'text' as const,
101
+ text: JSON.stringify({ error: result.error }, null, 2),
102
+ },
103
+ ],
104
+ isError: true,
105
+ }
106
+ }
107
+
108
+ return {
109
+ content: [
110
+ { type: 'text' as const, text: JSON.stringify(result.data, null, 2) },
111
+ ],
112
+ }
113
+ }
114
+
115
+ // ---------------------------------------------------------------------------
116
+ // record_adr_decision -> adr_decide
117
+ // ---------------------------------------------------------------------------
118
+
119
+ export const recordAdrDecisionSchema = {
120
+ adr_id: z.string().uuid().describe('ADR UUID'),
121
+ decision: z
122
+ .enum(['accepted', 'rejected'])
123
+ .describe('Decision outcome: accept or reject'),
124
+ rationale: z
125
+ .string()
126
+ .optional()
127
+ .describe('Optional rationale for the decision'),
128
+ }
129
+
130
+ type RecordAdrDecisionArgs = {
131
+ adr_id: string
132
+ decision: string
133
+ rationale?: string
134
+ user_id: string
135
+ }
136
+
137
+ export async function recordAdrDecision(args: RecordAdrDecisionArgs) {
138
+ const result = await nexusPost('/api/mcp/governance', {
139
+ action: 'adr_decide',
140
+ adr_id: args.adr_id,
141
+ decision: args.decision,
142
+ rationale: args.rationale,
143
+ })
144
+
145
+ if (!result.ok) {
146
+ return {
147
+ content: [
148
+ {
149
+ type: 'text' as const,
150
+ text: JSON.stringify({ error: result.error }, null, 2),
151
+ },
152
+ ],
153
+ isError: true,
154
+ }
155
+ }
156
+
157
+ return {
158
+ content: [
159
+ { type: 'text' as const, text: JSON.stringify(result.data, null, 2) },
160
+ ],
161
+ }
162
+ }