@relentlessbuild/decs-mcp 0.2.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.
- package/README.md +54 -0
- package/codex/AGENTS.md +7 -0
- package/codex/prompts/decs-context.md +5 -0
- package/codex/prompts/decs-init.md +9 -0
- package/codex/prompts/decs-log.md +6 -0
- package/codex/prompts/decs-update.md +7 -0
- package/dist/cli/args.js +47 -0
- package/dist/cli/output.js +12 -0
- package/dist/cli/prompt.js +43 -0
- package/dist/commands/doctor.js +58 -0
- package/dist/commands/init.js +35 -0
- package/dist/commands/setup-claude-desktop.js +64 -0
- package/dist/commands/setup-codex.js +92 -0
- package/dist/commands/shared.js +35 -0
- package/dist/config.js +93 -0
- package/dist/decision-context.js +71 -0
- package/dist/errors.js +19 -0
- package/dist/index.js +85 -0
- package/dist/install/codex-assets.js +98 -0
- package/dist/install/fs-utils.js +29 -0
- package/dist/install/json-merge.js +28 -0
- package/dist/install/paths.js +27 -0
- package/dist/install/toml-merge.js +31 -0
- package/dist/relentless-api.js +107 -0
- package/dist/repo-config.js +71 -0
- package/dist/server.js +16 -0
- package/dist/tools.js +304 -0
- package/dist/types.js +1 -0
- package/docs/codex-quickstart.md +59 -0
- package/docs/legacy-claude-code-hooks.md +37 -0
- package/docs/mcp-clients.md +121 -0
- package/package.json +42 -0
- package/templates/claude_desktop_config.example.json +17 -0
- package/templates/codex-config.example.toml +10 -0
- package/templates/decs-config.example.json +5 -0
package/dist/tools.js
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
import { resolveCredentials } from "./config.js";
|
|
5
|
+
import { partitionDecisions, renderDecisionContextMarkdown, toDecisionSummary } from "./decision-context.js";
|
|
6
|
+
import { DecsError, toDecsError } from "./errors.js";
|
|
7
|
+
import { readRepoDecsConfig, writeRepoDecsConfig } from "./repo-config.js";
|
|
8
|
+
import { RelentlessApiClient } from "./relentless-api.js";
|
|
9
|
+
const LIST_SCHEMA = z.object({
|
|
10
|
+
cwd: z.string().trim().min(1).optional(),
|
|
11
|
+
spaceId: z.string().trim().min(1).optional(),
|
|
12
|
+
isKeyDecision: z.boolean().optional(),
|
|
13
|
+
limit: z.number().int().positive().max(100).optional()
|
|
14
|
+
});
|
|
15
|
+
const GET_CONTEXT_SCHEMA = z.object({
|
|
16
|
+
cwd: z.string().trim().min(1).optional(),
|
|
17
|
+
spaceId: z.string().trim().min(1).optional(),
|
|
18
|
+
includeRecentLimit: z.number().int().positive().max(100).optional()
|
|
19
|
+
});
|
|
20
|
+
const CREATE_DECISION_SCHEMA = z.object({
|
|
21
|
+
cwd: z.string().trim().min(1).optional(),
|
|
22
|
+
parentId: z.string().trim().min(1).optional(),
|
|
23
|
+
title: z.string().trim().min(1),
|
|
24
|
+
what: z.string().trim().min(1),
|
|
25
|
+
why: z.string().trim().min(1),
|
|
26
|
+
purpose: z.string().trim().min(1),
|
|
27
|
+
constraints: z.string().trim().min(1),
|
|
28
|
+
isKeyDecision: z.boolean().optional()
|
|
29
|
+
});
|
|
30
|
+
const UPDATE_DECISION_SCHEMA = z
|
|
31
|
+
.object({
|
|
32
|
+
decisionId: z.string().trim().min(1),
|
|
33
|
+
title: z.string().trim().min(1).optional(),
|
|
34
|
+
what: z.string().trim().min(1).optional(),
|
|
35
|
+
why: z.string().trim().min(1).optional(),
|
|
36
|
+
purpose: z.string().trim().min(1).optional(),
|
|
37
|
+
constraints: z.string().trim().min(1).optional(),
|
|
38
|
+
isKeyDecision: z.boolean().optional()
|
|
39
|
+
})
|
|
40
|
+
.refine((data) => data.title !== undefined ||
|
|
41
|
+
data.what !== undefined ||
|
|
42
|
+
data.why !== undefined ||
|
|
43
|
+
data.purpose !== undefined ||
|
|
44
|
+
data.constraints !== undefined ||
|
|
45
|
+
data.isKeyDecision !== undefined, "At least one field must be provided for update");
|
|
46
|
+
const INIT_SPACE_SCHEMA = z.object({
|
|
47
|
+
projectNodeId: z.string().trim().min(1),
|
|
48
|
+
projectName: z.string().trim().min(1).optional(),
|
|
49
|
+
cwd: z.string().trim().min(1).optional()
|
|
50
|
+
});
|
|
51
|
+
const WRITE_REPO_CONFIG_SCHEMA = z.object({
|
|
52
|
+
spaceId: z.string().trim().min(1),
|
|
53
|
+
cwd: z.string().trim().min(1).optional()
|
|
54
|
+
});
|
|
55
|
+
const TOOLS = [
|
|
56
|
+
{
|
|
57
|
+
name: "decs_get_context",
|
|
58
|
+
description: "Fetches decision history from Relentless and returns key decisions plus recent decisions formatted for prompt context.",
|
|
59
|
+
inputSchema: {
|
|
60
|
+
type: "object",
|
|
61
|
+
properties: {
|
|
62
|
+
cwd: { type: "string" },
|
|
63
|
+
spaceId: { type: "string" },
|
|
64
|
+
includeRecentLimit: { type: "number", minimum: 1, maximum: 100 }
|
|
65
|
+
},
|
|
66
|
+
additionalProperties: false
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: "decs_list",
|
|
71
|
+
description: "Lists decisions from the configured Relentless decisions space or a supplied spaceId.",
|
|
72
|
+
inputSchema: {
|
|
73
|
+
type: "object",
|
|
74
|
+
properties: {
|
|
75
|
+
cwd: { type: "string" },
|
|
76
|
+
spaceId: { type: "string" },
|
|
77
|
+
isKeyDecision: { type: "boolean" },
|
|
78
|
+
limit: { type: "number", minimum: 1, maximum: 100 }
|
|
79
|
+
},
|
|
80
|
+
additionalProperties: false
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: "decs_create",
|
|
85
|
+
description: "Creates a decision node in Relentless. Use for architectural decisions after explicit user confirmation.",
|
|
86
|
+
inputSchema: {
|
|
87
|
+
type: "object",
|
|
88
|
+
required: ["title", "what", "why", "purpose", "constraints"],
|
|
89
|
+
properties: {
|
|
90
|
+
cwd: { type: "string" },
|
|
91
|
+
parentId: { type: "string" },
|
|
92
|
+
title: { type: "string" },
|
|
93
|
+
what: { type: "string" },
|
|
94
|
+
why: { type: "string" },
|
|
95
|
+
purpose: { type: "string" },
|
|
96
|
+
constraints: { type: "string" },
|
|
97
|
+
isKeyDecision: { type: "boolean" }
|
|
98
|
+
},
|
|
99
|
+
additionalProperties: false
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: "decs_update",
|
|
104
|
+
description: "Updates an existing decision node by id. Missing fields are preserved from the existing node.",
|
|
105
|
+
inputSchema: {
|
|
106
|
+
type: "object",
|
|
107
|
+
required: ["decisionId"],
|
|
108
|
+
properties: {
|
|
109
|
+
decisionId: { type: "string" },
|
|
110
|
+
title: { type: "string" },
|
|
111
|
+
what: { type: "string" },
|
|
112
|
+
why: { type: "string" },
|
|
113
|
+
purpose: { type: "string" },
|
|
114
|
+
constraints: { type: "string" },
|
|
115
|
+
isKeyDecision: { type: "boolean" }
|
|
116
|
+
},
|
|
117
|
+
additionalProperties: false
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: "decs_init_space",
|
|
122
|
+
description: "Creates a '<projectName> - Decisions' collection inside the supplied Relentless project node.",
|
|
123
|
+
inputSchema: {
|
|
124
|
+
type: "object",
|
|
125
|
+
required: ["projectNodeId"],
|
|
126
|
+
properties: {
|
|
127
|
+
projectNodeId: { type: "string" },
|
|
128
|
+
projectName: { type: "string" },
|
|
129
|
+
cwd: { type: "string" }
|
|
130
|
+
},
|
|
131
|
+
additionalProperties: false
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: "decs_write_repo_config",
|
|
136
|
+
description: "Writes a .decs.json file at repo root (or cwd) pointing to a Relentless decisions space id.",
|
|
137
|
+
inputSchema: {
|
|
138
|
+
type: "object",
|
|
139
|
+
required: ["spaceId"],
|
|
140
|
+
properties: {
|
|
141
|
+
spaceId: { type: "string" },
|
|
142
|
+
cwd: { type: "string" }
|
|
143
|
+
},
|
|
144
|
+
additionalProperties: false
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
];
|
|
148
|
+
function jsonResult(data) {
|
|
149
|
+
return {
|
|
150
|
+
content: [
|
|
151
|
+
{
|
|
152
|
+
type: "text",
|
|
153
|
+
text: JSON.stringify(data, null, 2)
|
|
154
|
+
}
|
|
155
|
+
]
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
function errorResult(error) {
|
|
159
|
+
const normalized = toDecsError(error);
|
|
160
|
+
return {
|
|
161
|
+
isError: true,
|
|
162
|
+
content: [
|
|
163
|
+
{
|
|
164
|
+
type: "text",
|
|
165
|
+
text: JSON.stringify({
|
|
166
|
+
code: normalized.code,
|
|
167
|
+
message: normalized.message,
|
|
168
|
+
details: normalized.details
|
|
169
|
+
}, null, 2)
|
|
170
|
+
}
|
|
171
|
+
]
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
function resolveSpaceId(input) {
|
|
175
|
+
if (input.spaceId) {
|
|
176
|
+
return input.spaceId;
|
|
177
|
+
}
|
|
178
|
+
const repoConfig = readRepoDecsConfig(input.cwd ?? process.cwd());
|
|
179
|
+
return repoConfig.config.relentlessSpaceId;
|
|
180
|
+
}
|
|
181
|
+
function getClient() {
|
|
182
|
+
return new RelentlessApiClient(resolveCredentials());
|
|
183
|
+
}
|
|
184
|
+
export function registerToolHandlers(server) {
|
|
185
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
186
|
+
tools: TOOLS
|
|
187
|
+
}));
|
|
188
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
189
|
+
try {
|
|
190
|
+
const toolName = request.params.name;
|
|
191
|
+
const rawArgs = request.params.arguments ?? {};
|
|
192
|
+
const client = getClient();
|
|
193
|
+
if (toolName === "decs_get_context") {
|
|
194
|
+
const args = GET_CONTEXT_SCHEMA.parse(rawArgs);
|
|
195
|
+
const spaceId = resolveSpaceId(args);
|
|
196
|
+
const decisions = (await client.listDecisions(spaceId)).map(toDecisionSummary);
|
|
197
|
+
const { keyDecisions, recentDecisions } = partitionDecisions(decisions, args.includeRecentLimit ?? 10);
|
|
198
|
+
return jsonResult({
|
|
199
|
+
spaceId,
|
|
200
|
+
counts: {
|
|
201
|
+
total: decisions.length,
|
|
202
|
+
key: keyDecisions.length,
|
|
203
|
+
recent: recentDecisions.length
|
|
204
|
+
},
|
|
205
|
+
keyDecisions,
|
|
206
|
+
recentDecisions,
|
|
207
|
+
markdown: renderDecisionContextMarkdown({ keyDecisions, recentDecisions })
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
if (toolName === "decs_list") {
|
|
211
|
+
const args = LIST_SCHEMA.parse(rawArgs);
|
|
212
|
+
const spaceId = resolveSpaceId(args);
|
|
213
|
+
let decisions = (await client.listDecisions(spaceId)).map(toDecisionSummary);
|
|
214
|
+
if (args.isKeyDecision !== undefined) {
|
|
215
|
+
decisions = decisions.filter((decision) => decision.isKeyDecision === args.isKeyDecision);
|
|
216
|
+
}
|
|
217
|
+
const sorted = decisions.sort((a, b) => {
|
|
218
|
+
const aTs = a.updatedAt ? Date.parse(a.updatedAt) : 0;
|
|
219
|
+
const bTs = b.updatedAt ? Date.parse(b.updatedAt) : 0;
|
|
220
|
+
return bTs - aTs;
|
|
221
|
+
});
|
|
222
|
+
const limit = args.limit ?? 25;
|
|
223
|
+
return jsonResult({
|
|
224
|
+
spaceId,
|
|
225
|
+
total: sorted.length,
|
|
226
|
+
decisions: sorted.slice(0, limit)
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
if (toolName === "decs_create") {
|
|
230
|
+
const args = CREATE_DECISION_SCHEMA.parse(rawArgs);
|
|
231
|
+
const parentId = args.parentId ?? resolveSpaceId(args);
|
|
232
|
+
const created = await client.createNode({
|
|
233
|
+
kind: "decision",
|
|
234
|
+
title: args.title,
|
|
235
|
+
parentId,
|
|
236
|
+
content: {
|
|
237
|
+
what: args.what,
|
|
238
|
+
why: args.why,
|
|
239
|
+
purpose: args.purpose,
|
|
240
|
+
constraints: args.constraints,
|
|
241
|
+
isKeyDecision: args.isKeyDecision ?? false
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
return jsonResult({
|
|
245
|
+
created: true,
|
|
246
|
+
decision: toDecisionSummary(created)
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
if (toolName === "decs_update") {
|
|
250
|
+
const args = UPDATE_DECISION_SCHEMA.parse(rawArgs);
|
|
251
|
+
const current = await client.getNode(args.decisionId);
|
|
252
|
+
const currentContent = (current.content ?? {});
|
|
253
|
+
const updated = await client.patchNode(args.decisionId, {
|
|
254
|
+
title: args.title ?? current.title,
|
|
255
|
+
content: {
|
|
256
|
+
what: args.what ?? String(currentContent.what ?? ""),
|
|
257
|
+
why: args.why ?? String(currentContent.why ?? ""),
|
|
258
|
+
purpose: args.purpose ?? String(currentContent.purpose ?? ""),
|
|
259
|
+
constraints: args.constraints ?? String(currentContent.constraints ?? ""),
|
|
260
|
+
isKeyDecision: args.isKeyDecision ?? Boolean(currentContent.isKeyDecision === true)
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
return jsonResult({
|
|
264
|
+
updated: true,
|
|
265
|
+
decision: toDecisionSummary(updated)
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
if (toolName === "decs_init_space") {
|
|
269
|
+
const args = INIT_SPACE_SCHEMA.parse(rawArgs);
|
|
270
|
+
await client.getNode(args.projectNodeId);
|
|
271
|
+
const cwd = args.cwd ?? process.cwd();
|
|
272
|
+
const projectName = args.projectName ?? path.basename(path.resolve(cwd));
|
|
273
|
+
const createdSpace = await client.createNode({
|
|
274
|
+
kind: "collection",
|
|
275
|
+
title: `${projectName} - Decisions`,
|
|
276
|
+
parentId: args.projectNodeId
|
|
277
|
+
});
|
|
278
|
+
if (!createdSpace.id) {
|
|
279
|
+
throw new DecsError("UPSTREAM_ERROR", "Relentless returned a collection without an id");
|
|
280
|
+
}
|
|
281
|
+
return jsonResult({
|
|
282
|
+
projectNodeId: args.projectNodeId,
|
|
283
|
+
projectName,
|
|
284
|
+
spaceId: createdSpace.id,
|
|
285
|
+
spaceTitle: createdSpace.title
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
if (toolName === "decs_write_repo_config") {
|
|
289
|
+
const args = WRITE_REPO_CONFIG_SCHEMA.parse(rawArgs);
|
|
290
|
+
const result = writeRepoDecsConfig(args.spaceId, args.cwd ?? process.cwd());
|
|
291
|
+
return jsonResult({
|
|
292
|
+
saved: true,
|
|
293
|
+
relentlessSpaceId: args.spaceId,
|
|
294
|
+
configPath: result.path,
|
|
295
|
+
repoRoot: result.repoRoot
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
throw new DecsError("VALIDATION_ERROR", `Unknown tool: ${toolName}`);
|
|
299
|
+
}
|
|
300
|
+
catch (error) {
|
|
301
|
+
return errorResult(error);
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Codex Quickstart (DECS)
|
|
2
|
+
|
|
3
|
+
This is the simplest path for Codex users.
|
|
4
|
+
|
|
5
|
+
## One-Time Setup
|
|
6
|
+
|
|
7
|
+
Run:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx @relentless/decs-mcp setup codex --repo /path/to/your/repo
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The setup command will:
|
|
14
|
+
|
|
15
|
+
1. Prompt for your Relentless credentials (API key, URL, buildspace id).
|
|
16
|
+
2. Save them to `~/.relentless/decs-config.json`.
|
|
17
|
+
3. Add/update DECS MCP config in `~/.codex/config.toml`.
|
|
18
|
+
4. Install DECS prompt shortcuts in `~/.codex/prompts/`.
|
|
19
|
+
5. Merge DECS guidance into your repo `AGENTS.md`.
|
|
20
|
+
|
|
21
|
+
If you cloned this repository locally, you can also run:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
./install-codex.sh /path/to/your/repo
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The local script writes Codex MCP config pointing to your local `dist/index.js`.
|
|
28
|
+
|
|
29
|
+
## Enable DECS in a Repo
|
|
30
|
+
|
|
31
|
+
After setup, in Codex run:
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
npx @relentless/decs-mcp init <project-node-id> [project-name] --repo /path/to/your/repo
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
This creates a decisions collection in Relentless and writes `.decs.json` in your repo.
|
|
38
|
+
|
|
39
|
+
Optional in-chat prompt command:
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
/prompts:decs-init <project-node-id> [project-name]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Daily Usage
|
|
46
|
+
|
|
47
|
+
- `/prompts:decs-context`: read key/recent decisions
|
|
48
|
+
- `/prompts:decs-log`: create a new decision (asks confirmation before write)
|
|
49
|
+
- `/prompts:decs-update`: update an existing decision (asks confirmation before write)
|
|
50
|
+
|
|
51
|
+
## Validation
|
|
52
|
+
|
|
53
|
+
Run:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npx @relentless/decs-mcp doctor --repo /path/to/your/repo
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
It checks credentials, Codex MCP wiring, and `.decs.json`.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Legacy Claude Code Hooks Setup
|
|
2
|
+
|
|
3
|
+
This is the original hook-based workflow for Claude Code CLI.
|
|
4
|
+
|
|
5
|
+
## One-Time Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git clone https://github.com/RelentlessToph/relentless-decs.git
|
|
9
|
+
cd relentless-decs
|
|
10
|
+
./install.sh
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Create `~/.claude/decs-config.json`:
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"relentlessApiKey": "rlnt_...",
|
|
18
|
+
"relentlessUrl": "https://relentless.build",
|
|
19
|
+
"buildspaceId": "..."
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Enable in a Repository
|
|
24
|
+
|
|
25
|
+
In Claude Code, run:
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
/init-decs-project <project-node-id> [project-name]
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
This writes `.decs.json` and links the repository to a Relentless decisions collection.
|
|
32
|
+
|
|
33
|
+
## Hook Behavior
|
|
34
|
+
|
|
35
|
+
- Session start: loads prior decisions
|
|
36
|
+
- Prompt submit: refreshes when decision-related keywords appear
|
|
37
|
+
- Session stop: prompts decision hygiene and recording
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Relentless DECS MCP Client Guide
|
|
2
|
+
|
|
3
|
+
This guide covers general MCP usage plus client-specific setup.
|
|
4
|
+
|
|
5
|
+
## Shared Prerequisites
|
|
6
|
+
|
|
7
|
+
- Node.js 18+
|
|
8
|
+
- A Relentless API key
|
|
9
|
+
- Buildspace id (UUID shown in your Relentless URL)
|
|
10
|
+
|
|
11
|
+
Credentials are stored in:
|
|
12
|
+
|
|
13
|
+
- `~/.relentless/decs-config.json` (preferred)
|
|
14
|
+
- `~/.claude/decs-config.json` (legacy fallback)
|
|
15
|
+
|
|
16
|
+
Format:
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"relentlessApiKey": "rlnt_...",
|
|
21
|
+
"relentlessUrl": "https://relentless.build",
|
|
22
|
+
"buildspaceId": "..."
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Codex
|
|
27
|
+
|
|
28
|
+
Recommended:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npx @relentless/decs-mcp setup codex --repo /path/to/your/repo
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Local clone alternative:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
./install-codex.sh /path/to/your/repo
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Manual target file:
|
|
41
|
+
|
|
42
|
+
- `~/.codex/config.toml`
|
|
43
|
+
|
|
44
|
+
Expected MCP block:
|
|
45
|
+
|
|
46
|
+
```toml
|
|
47
|
+
[mcp_servers.relentless_decs]
|
|
48
|
+
command = "npx"
|
|
49
|
+
args = ["-y", "@relentless/decs-mcp", "serve"]
|
|
50
|
+
|
|
51
|
+
[mcp_servers.relentless_decs.env]
|
|
52
|
+
RELENTLESS_API_KEY = "rlnt_..."
|
|
53
|
+
RELENTLESS_URL = "https://relentless.build"
|
|
54
|
+
RELENTLESS_BUILDSPACE_ID = "..."
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Claude Desktop
|
|
58
|
+
|
|
59
|
+
### Manual setup
|
|
60
|
+
|
|
61
|
+
Use the correct platform path:
|
|
62
|
+
|
|
63
|
+
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
64
|
+
- Windows: `%APPDATA%\\Claude\\claude_desktop_config.json`
|
|
65
|
+
|
|
66
|
+
Add:
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"mcpServers": {
|
|
71
|
+
"relentless-decs": {
|
|
72
|
+
"command": "npx",
|
|
73
|
+
"args": ["-y", "@relentless/decs-mcp", "serve"],
|
|
74
|
+
"env": {
|
|
75
|
+
"RELENTLESS_API_KEY": "rlnt_...",
|
|
76
|
+
"RELENTLESS_URL": "https://relentless.build",
|
|
77
|
+
"RELENTLESS_BUILDSPACE_ID": "..."
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Optional automation
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
npx @relentless/decs-mcp setup claude-desktop
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Local clone alternative:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
./install-claude-desktop-mcp.sh
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Optional flags:
|
|
97
|
+
|
|
98
|
+
- `--platform macos|windows`
|
|
99
|
+
- `--dry-run`
|
|
100
|
+
- `--yes`
|
|
101
|
+
- `--use-local-server` (for local clone installs)
|
|
102
|
+
|
|
103
|
+
The command performs idempotent merge and writes a `.bak` backup before changes.
|
|
104
|
+
|
|
105
|
+
## MCP Tools Exposed
|
|
106
|
+
|
|
107
|
+
- `decs_get_context`
|
|
108
|
+
- `decs_list`
|
|
109
|
+
- `decs_create`
|
|
110
|
+
- `decs_update`
|
|
111
|
+
- `decs_init_space`
|
|
112
|
+
- `decs_write_repo_config`
|
|
113
|
+
|
|
114
|
+
## Troubleshooting
|
|
115
|
+
|
|
116
|
+
- `doctor` command:
|
|
117
|
+
- `npx @relentless/decs-mcp doctor --repo /path/to/your/repo`
|
|
118
|
+
- If Codex/Claude does not show tools:
|
|
119
|
+
- restart client fully
|
|
120
|
+
- confirm config path and JSON/TOML validity
|
|
121
|
+
- verify `npx -y @relentless/decs-mcp serve` runs
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@relentlessbuild/decs-mcp",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Relentless DECS MCP server for Codex, Claude Desktop, and other MCP clients.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"relentless-decs-mcp": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"codex",
|
|
12
|
+
"docs",
|
|
13
|
+
"templates",
|
|
14
|
+
"README.md"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc -p tsconfig.json",
|
|
18
|
+
"clean": "rm -rf dist coverage",
|
|
19
|
+
"dev": "tsx src/index.ts",
|
|
20
|
+
"setup:codex": "tsx src/index.ts setup codex",
|
|
21
|
+
"setup:claude-desktop": "tsx src/index.ts setup claude-desktop",
|
|
22
|
+
"doctor": "tsx src/index.ts doctor",
|
|
23
|
+
"start": "node dist/index.js",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"test:watch": "vitest",
|
|
26
|
+
"typecheck": "tsc --noEmit"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18.18.0"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@iarna/toml": "^2.2.5",
|
|
33
|
+
"@modelcontextprotocol/sdk": "^1.17.3",
|
|
34
|
+
"zod": "^3.24.1"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/node": "^22.12.0",
|
|
38
|
+
"tsx": "^4.19.2",
|
|
39
|
+
"typescript": "^5.7.3",
|
|
40
|
+
"vitest": "^2.1.8"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mcpServers": {
|
|
3
|
+
"relentless-decs": {
|
|
4
|
+
"command": "npx",
|
|
5
|
+
"args": [
|
|
6
|
+
"-y",
|
|
7
|
+
"@relentless/decs-mcp",
|
|
8
|
+
"serve"
|
|
9
|
+
],
|
|
10
|
+
"env": {
|
|
11
|
+
"RELENTLESS_API_KEY": "rlnt_your_api_key",
|
|
12
|
+
"RELENTLESS_URL": "https://relentless.build",
|
|
13
|
+
"RELENTLESS_BUILDSPACE_ID": "your-buildspace-id"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# ~/.codex/config.toml
|
|
2
|
+
|
|
3
|
+
[mcp_servers.relentless_decs]
|
|
4
|
+
command = "npx"
|
|
5
|
+
args = ["-y", "@relentless/decs-mcp", "serve"]
|
|
6
|
+
|
|
7
|
+
[mcp_servers.relentless_decs.env]
|
|
8
|
+
RELENTLESS_API_KEY = "rlnt_your_api_key"
|
|
9
|
+
RELENTLESS_URL = "https://relentless.build"
|
|
10
|
+
RELENTLESS_BUILDSPACE_ID = "your-buildspace-id"
|