systematics-mcp 1.0.2 → 1.1.1
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 +9 -15
- package/dist/index.js +158 -37
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,26 +4,20 @@ Claude Code MCP server for the [Systematics](https://app.dovito.com) platform by
|
|
|
4
4
|
|
|
5
5
|
## Quick Start
|
|
6
6
|
|
|
7
|
-
### 1.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
{
|
|
13
|
-
"mcpServers": {
|
|
14
|
-
"systematics": {
|
|
15
|
-
"command": "npx",
|
|
16
|
-
"args": ["-y", "systematics-mcp"]
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
}
|
|
7
|
+
### 1. Install and Register
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g systematics-mcp
|
|
11
|
+
claude mcp add systematics --scope user -- systematics-mcp
|
|
20
12
|
```
|
|
21
13
|
|
|
14
|
+
Use `--scope user` for global access across all projects, or `--scope project` for a single project.
|
|
15
|
+
|
|
22
16
|
### 2. Authenticate
|
|
23
17
|
|
|
24
|
-
Restart Claude Code, then use `/mcp` to
|
|
18
|
+
Restart Claude Code, then use `/mcp` to verify Systematics shows as "Connected." The first time you use a Systematics tool, run the `authenticate` tool to sign in via your browser. Your token is saved locally at `~/.systematics/token` and reused automatically for 90 days.
|
|
25
19
|
|
|
26
|
-
That's it. No API keys, no repo access, no manual
|
|
20
|
+
That's it. No API keys, no repo access, no manual config files.
|
|
27
21
|
|
|
28
22
|
## How It Works
|
|
29
23
|
|
package/dist/index.js
CHANGED
|
@@ -21,35 +21,81 @@ function getToken() {
|
|
|
21
21
|
function handleAuthError() {
|
|
22
22
|
clearStoredToken();
|
|
23
23
|
api = null;
|
|
24
|
+
authenticated = false;
|
|
24
25
|
}
|
|
25
|
-
//
|
|
26
|
+
// Track auth state
|
|
27
|
+
let authenticated = false;
|
|
26
28
|
let api = null;
|
|
27
29
|
const token = getToken();
|
|
28
30
|
if (token) {
|
|
29
31
|
api = new ApiClient(BASE_URL, token, handleAuthError);
|
|
32
|
+
authenticated = true;
|
|
30
33
|
}
|
|
31
34
|
/**
|
|
32
|
-
* Get the API client
|
|
33
|
-
*
|
|
35
|
+
* Get the API client. Returns null if not authenticated.
|
|
36
|
+
* Does NOT auto-trigger browser auth -- use the authenticate tool instead.
|
|
34
37
|
*/
|
|
35
|
-
|
|
36
|
-
if (api)
|
|
37
|
-
return api;
|
|
38
|
-
const newToken = await authenticateViaBrowser(BASE_URL);
|
|
39
|
-
api = new ApiClient(BASE_URL, newToken, handleAuthError);
|
|
38
|
+
function getApi() {
|
|
40
39
|
return api;
|
|
41
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Returns an auth-required response for tools when not authenticated.
|
|
43
|
+
*/
|
|
44
|
+
function authRequiredResponse() {
|
|
45
|
+
return {
|
|
46
|
+
content: [{
|
|
47
|
+
type: "text",
|
|
48
|
+
text: "Not authenticated with Systematics. Please run the 'authenticate' tool first to connect your account.",
|
|
49
|
+
}],
|
|
50
|
+
isError: true,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
42
53
|
const server = new McpServer({
|
|
43
|
-
name: "
|
|
44
|
-
version: "1.0.
|
|
54
|
+
name: "systematics",
|
|
55
|
+
version: "1.0.2",
|
|
56
|
+
});
|
|
57
|
+
// ── Authentication ────────────────────────────────────────────────────────
|
|
58
|
+
server.tool("authenticate", `Sign in to Systematics via your browser. ${authenticated ? "(Currently authenticated)" : "(Not authenticated -- run this first)"}`, {}, async () => {
|
|
59
|
+
if (authenticated && api) {
|
|
60
|
+
return { content: [{ type: "text", text: "Already authenticated with Systematics. To re-authenticate, run 'reauthenticate'." }] };
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
const newToken = await authenticateViaBrowser(BASE_URL);
|
|
64
|
+
api = new ApiClient(BASE_URL, newToken, handleAuthError);
|
|
65
|
+
authenticated = true;
|
|
66
|
+
return { content: [{ type: "text", text: "Successfully authenticated with Systematics. You can now use all other tools." }] };
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
return { content: [{ type: "text", text: `Authentication failed: ${err instanceof Error ? err.message : "Unknown error"}` }], isError: true };
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
server.tool("reauthenticate", "Clear stored credentials and sign in again with a new token", {}, async () => {
|
|
73
|
+
clearStoredToken();
|
|
74
|
+
api = null;
|
|
75
|
+
authenticated = false;
|
|
76
|
+
try {
|
|
77
|
+
const newToken = await authenticateViaBrowser(BASE_URL);
|
|
78
|
+
api = new ApiClient(BASE_URL, newToken, handleAuthError);
|
|
79
|
+
authenticated = true;
|
|
80
|
+
return { content: [{ type: "text", text: "Re-authenticated successfully with a new token." }] };
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
return { content: [{ type: "text", text: `Authentication failed: ${err instanceof Error ? err.message : "Unknown error"}` }], isError: true };
|
|
84
|
+
}
|
|
45
85
|
});
|
|
46
86
|
// ── Businesses ─────────────────────────────────────────────────────────────
|
|
47
87
|
server.tool("list_businesses", "List all businesses (clients) in the Dovito portal", {}, async () => {
|
|
48
|
-
const
|
|
88
|
+
const client = getApi();
|
|
89
|
+
if (!client)
|
|
90
|
+
return authRequiredResponse();
|
|
91
|
+
const data = await client.get("/api/businesses");
|
|
49
92
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
50
93
|
});
|
|
51
94
|
server.tool("get_business", "Get a single business by ID", { businessId: z.string().describe("Business UUID") }, async ({ businessId }) => {
|
|
52
|
-
const
|
|
95
|
+
const client = getApi();
|
|
96
|
+
if (!client)
|
|
97
|
+
return authRequiredResponse();
|
|
98
|
+
const data = await client.get(`/api/businesses/${businessId}`);
|
|
53
99
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
54
100
|
});
|
|
55
101
|
server.tool("create_business", "Create a new business (client company)", {
|
|
@@ -62,7 +108,10 @@ server.tool("create_business", "Create a new business (client company)", {
|
|
|
62
108
|
status: z.enum(["pending", "approved", "active"]).optional(),
|
|
63
109
|
ownerUserId: z.string().optional().describe("User ID of the business owner, or 'none'"),
|
|
64
110
|
}, async (params) => {
|
|
65
|
-
const
|
|
111
|
+
const client = getApi();
|
|
112
|
+
if (!client)
|
|
113
|
+
return authRequiredResponse();
|
|
114
|
+
const data = await client.post("/api/businesses", params);
|
|
66
115
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
67
116
|
});
|
|
68
117
|
server.tool("update_business", "Update an existing business", {
|
|
@@ -77,16 +126,25 @@ server.tool("update_business", "Update an existing business", {
|
|
|
77
126
|
status: z.enum(["pending", "approved", "active"]).optional(),
|
|
78
127
|
assignedAmId: z.string().nullable().optional().describe("Account manager user ID"),
|
|
79
128
|
}, async ({ businessId, ...updates }) => {
|
|
80
|
-
const
|
|
129
|
+
const client = getApi();
|
|
130
|
+
if (!client)
|
|
131
|
+
return authRequiredResponse();
|
|
132
|
+
const data = await client.patch(`/api/businesses/${businessId}`, updates);
|
|
81
133
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
82
134
|
});
|
|
83
135
|
// ── Projects ───────────────────────────────────────────────────────────────
|
|
84
136
|
server.tool("list_projects", "List all projects across businesses", {}, async () => {
|
|
85
|
-
const
|
|
137
|
+
const client = getApi();
|
|
138
|
+
if (!client)
|
|
139
|
+
return authRequiredResponse();
|
|
140
|
+
const data = await client.get("/api/projects");
|
|
86
141
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
87
142
|
});
|
|
88
143
|
server.tool("get_project", "Get a single project by ID", { projectId: z.string().describe("Project UUID") }, async ({ projectId }) => {
|
|
89
|
-
const
|
|
144
|
+
const client = getApi();
|
|
145
|
+
if (!client)
|
|
146
|
+
return authRequiredResponse();
|
|
147
|
+
const data = await client.get(`/api/projects/${projectId}`);
|
|
90
148
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
91
149
|
});
|
|
92
150
|
server.tool("create_project", "Create a new project under a business", {
|
|
@@ -100,7 +158,10 @@ server.tool("create_project", "Create a new project under a business", {
|
|
|
100
158
|
startDate: z.string().optional().describe("ISO date"),
|
|
101
159
|
estimatedEndDate: z.string().optional().describe("ISO date"),
|
|
102
160
|
}, async (params) => {
|
|
103
|
-
const
|
|
161
|
+
const client = getApi();
|
|
162
|
+
if (!client)
|
|
163
|
+
return authRequiredResponse();
|
|
164
|
+
const data = await client.post("/api/admin/projects", params);
|
|
104
165
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
105
166
|
});
|
|
106
167
|
server.tool("update_project", "Update an existing project", {
|
|
@@ -113,7 +174,10 @@ server.tool("update_project", "Update an existing project", {
|
|
|
113
174
|
startDate: z.string().optional().describe("ISO date"),
|
|
114
175
|
estimatedEndDate: z.string().optional().describe("ISO date"),
|
|
115
176
|
}, async ({ projectId, ...updates }) => {
|
|
116
|
-
const
|
|
177
|
+
const client = getApi();
|
|
178
|
+
if (!client)
|
|
179
|
+
return authRequiredResponse();
|
|
180
|
+
const data = await client.patch(`/api/admin/projects/${projectId}`, updates);
|
|
117
181
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
118
182
|
});
|
|
119
183
|
// ── Tasks ──────────────────────────────────────────────────────────────────
|
|
@@ -124,11 +188,17 @@ server.tool("list_tasks", "List all tasks, optionally filtered by business", {
|
|
|
124
188
|
if (businessId)
|
|
125
189
|
params.set("businessId", businessId);
|
|
126
190
|
const query = params.toString() ? `?${params}` : "";
|
|
127
|
-
const
|
|
191
|
+
const client = getApi();
|
|
192
|
+
if (!client)
|
|
193
|
+
return authRequiredResponse();
|
|
194
|
+
const data = await client.get(`/api/tasks${query}`);
|
|
128
195
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
129
196
|
});
|
|
130
197
|
server.tool("list_project_tasks", "List tasks for a specific project", { projectId: z.string().describe("Project UUID") }, async ({ projectId }) => {
|
|
131
|
-
const
|
|
198
|
+
const client = getApi();
|
|
199
|
+
if (!client)
|
|
200
|
+
return authRequiredResponse();
|
|
201
|
+
const data = await client.get(`/api/projects/${projectId}/tasks`);
|
|
132
202
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
133
203
|
});
|
|
134
204
|
server.tool("create_task", "Create a task under a project", {
|
|
@@ -140,7 +210,10 @@ server.tool("create_task", "Create a task under a project", {
|
|
|
140
210
|
startDate: z.string().optional().describe("ISO date"),
|
|
141
211
|
dueDate: z.string().optional().describe("ISO date"),
|
|
142
212
|
}, async ({ projectId, ...body }) => {
|
|
143
|
-
const
|
|
213
|
+
const client = getApi();
|
|
214
|
+
if (!client)
|
|
215
|
+
return authRequiredResponse();
|
|
216
|
+
const data = await client.post(`/api/projects/${projectId}/tasks`, body);
|
|
144
217
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
145
218
|
});
|
|
146
219
|
server.tool("update_task", "Update an existing task", {
|
|
@@ -154,7 +227,10 @@ server.tool("update_task", "Update an existing task", {
|
|
|
154
227
|
startDate: z.string().optional().describe("ISO date"),
|
|
155
228
|
dueDate: z.string().optional().describe("ISO date"),
|
|
156
229
|
}, async ({ projectId, taskId, ...updates }) => {
|
|
157
|
-
const
|
|
230
|
+
const client = getApi();
|
|
231
|
+
if (!client)
|
|
232
|
+
return authRequiredResponse();
|
|
233
|
+
const data = await client.patch(`/api/projects/${projectId}/tasks/${taskId}`, updates);
|
|
158
234
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
159
235
|
});
|
|
160
236
|
server.tool("delete_task", "Delete a task (set confirm=true to proceed)", {
|
|
@@ -162,13 +238,19 @@ server.tool("delete_task", "Delete a task (set confirm=true to proceed)", {
|
|
|
162
238
|
taskId: z.string().describe("Task UUID"),
|
|
163
239
|
confirm: z.literal(true).describe("Must be true to confirm deletion"),
|
|
164
240
|
}, async ({ projectId, taskId }) => {
|
|
165
|
-
|
|
241
|
+
const client = getApi();
|
|
242
|
+
if (!client)
|
|
243
|
+
return authRequiredResponse();
|
|
244
|
+
await client.delete(`/api/projects/${projectId}/tasks/${taskId}`);
|
|
166
245
|
return { content: [{ type: "text", text: "Task deleted." }] };
|
|
167
246
|
});
|
|
168
247
|
// ── Catalog ────────────────────────────────────────────────────────────────
|
|
169
248
|
server.tool("list_catalog_items", "List all items in the line item catalog", { all: z.boolean().optional().describe("Include inactive items") }, async ({ all }) => {
|
|
170
249
|
const query = all ? "?all=true" : "";
|
|
171
|
-
const
|
|
250
|
+
const client = getApi();
|
|
251
|
+
if (!client)
|
|
252
|
+
return authRequiredResponse();
|
|
253
|
+
const data = await client.get(`/api/catalog${query}`);
|
|
172
254
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
173
255
|
});
|
|
174
256
|
server.tool("create_catalog_item", "Add an item to the line item catalog", {
|
|
@@ -179,7 +261,10 @@ server.tool("create_catalog_item", "Add an item to the line item catalog", {
|
|
|
179
261
|
unitAmount: z.number().int().min(0).describe("Price in cents"),
|
|
180
262
|
unit: z.string().optional().describe("hour, each, month, project, per seat, quarterly, annual"),
|
|
181
263
|
}, async (params) => {
|
|
182
|
-
const
|
|
264
|
+
const client = getApi();
|
|
265
|
+
if (!client)
|
|
266
|
+
return authRequiredResponse();
|
|
267
|
+
const data = await client.post("/api/catalog", params);
|
|
183
268
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
184
269
|
});
|
|
185
270
|
server.tool("update_catalog_item", "Update a catalog item", {
|
|
@@ -192,7 +277,10 @@ server.tool("update_catalog_item", "Update a catalog item", {
|
|
|
192
277
|
unit: z.string().optional(),
|
|
193
278
|
isActive: z.boolean().optional(),
|
|
194
279
|
}, async ({ itemId, ...updates }) => {
|
|
195
|
-
const
|
|
280
|
+
const client = getApi();
|
|
281
|
+
if (!client)
|
|
282
|
+
return authRequiredResponse();
|
|
283
|
+
const data = await client.patch(`/api/catalog/${itemId}`, updates);
|
|
196
284
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
197
285
|
});
|
|
198
286
|
// ── Users ──────────────────────────────────────────────────────────────────
|
|
@@ -206,12 +294,18 @@ server.tool("list_users", "List all users in the portal", {
|
|
|
206
294
|
if (includeStaff)
|
|
207
295
|
params.set("includeStaff", "true");
|
|
208
296
|
const query = params.toString() ? `?${params}` : "";
|
|
209
|
-
const
|
|
297
|
+
const client = getApi();
|
|
298
|
+
if (!client)
|
|
299
|
+
return authRequiredResponse();
|
|
300
|
+
const data = await client.get(`/api/admin/users${query}`);
|
|
210
301
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
211
302
|
});
|
|
212
303
|
// ── Proposals ──────────────────────────────────────────────────────────────
|
|
213
304
|
server.tool("list_proposals", "List proposals for a project", { projectId: z.string().describe("Project UUID") }, async ({ projectId }) => {
|
|
214
|
-
const
|
|
305
|
+
const client = getApi();
|
|
306
|
+
if (!client)
|
|
307
|
+
return authRequiredResponse();
|
|
308
|
+
const data = await client.get(`/api/projects/${projectId}/proposals`);
|
|
215
309
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
216
310
|
});
|
|
217
311
|
server.tool("create_proposal", "Create a proposal (quote) for a project", {
|
|
@@ -225,12 +319,18 @@ server.tool("create_proposal", "Create a proposal (quote) for a project", {
|
|
|
225
319
|
})).describe("Line items for the proposal"),
|
|
226
320
|
expiresAt: z.string().optional().describe("ISO date for expiration"),
|
|
227
321
|
}, async ({ projectId, ...body }) => {
|
|
228
|
-
const
|
|
322
|
+
const client = getApi();
|
|
323
|
+
if (!client)
|
|
324
|
+
return authRequiredResponse();
|
|
325
|
+
const data = await client.post(`/api/projects/${projectId}/proposals`, body);
|
|
229
326
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
230
327
|
});
|
|
231
328
|
// ── Conversations & Messages ───────────────────────────────────────────────
|
|
232
329
|
server.tool("list_conversations", "List all conversations (message threads) for the service account", {}, async () => {
|
|
233
|
-
const
|
|
330
|
+
const client = getApi();
|
|
331
|
+
if (!client)
|
|
332
|
+
return authRequiredResponse();
|
|
333
|
+
const data = await client.get("/api/messages");
|
|
234
334
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
235
335
|
});
|
|
236
336
|
server.tool("get_conversation_messages", "Get messages in a conversation thread", {
|
|
@@ -241,7 +341,10 @@ server.tool("get_conversation_messages", "Get messages in a conversation thread"
|
|
|
241
341
|
if (cursor)
|
|
242
342
|
params.set("cursor", cursor);
|
|
243
343
|
const query = params.toString() ? `?${params}` : "";
|
|
244
|
-
const
|
|
344
|
+
const client = getApi();
|
|
345
|
+
if (!client)
|
|
346
|
+
return authRequiredResponse();
|
|
347
|
+
const data = await client.get(`/api/messages/${conversationId}${query}`);
|
|
245
348
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
246
349
|
});
|
|
247
350
|
server.tool("create_conversation", "Create a new conversation (DM, group, project, or business thread)", {
|
|
@@ -251,7 +354,10 @@ server.tool("create_conversation", "Create a new conversation (DM, group, projec
|
|
|
251
354
|
projectId: z.string().optional().describe("Project UUID (for project type)"),
|
|
252
355
|
businessId: z.string().optional().describe("Business UUID (for business type)"),
|
|
253
356
|
}, async (params) => {
|
|
254
|
-
const
|
|
357
|
+
const client = getApi();
|
|
358
|
+
if (!client)
|
|
359
|
+
return authRequiredResponse();
|
|
360
|
+
const data = await client.post("/api/conversations", params);
|
|
255
361
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
256
362
|
});
|
|
257
363
|
server.tool("send_message", "Send a message in a conversation", {
|
|
@@ -259,12 +365,18 @@ server.tool("send_message", "Send a message in a conversation", {
|
|
|
259
365
|
content: z.string().describe("Message content (1-10000 chars)"),
|
|
260
366
|
parentMessageId: z.string().optional().describe("Reply to a specific message"),
|
|
261
367
|
}, async ({ conversationId, ...body }) => {
|
|
262
|
-
const
|
|
368
|
+
const client = getApi();
|
|
369
|
+
if (!client)
|
|
370
|
+
return authRequiredResponse();
|
|
371
|
+
const data = await client.post(`/api/messages/${conversationId}`, body);
|
|
263
372
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
264
373
|
});
|
|
265
374
|
// ── Deliverables ───────────────────────────────────────────────────────────
|
|
266
375
|
server.tool("list_deliverables", "List deliverables for a project", { projectId: z.string().describe("Project UUID") }, async ({ projectId }) => {
|
|
267
|
-
const
|
|
376
|
+
const client = getApi();
|
|
377
|
+
if (!client)
|
|
378
|
+
return authRequiredResponse();
|
|
379
|
+
const data = await client.get(`/api/projects/${projectId}/deliverables`);
|
|
268
380
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
269
381
|
});
|
|
270
382
|
server.tool("create_deliverable", "Create a deliverable under a project", {
|
|
@@ -273,7 +385,10 @@ server.tool("create_deliverable", "Create a deliverable under a project", {
|
|
|
273
385
|
description: z.string().optional(),
|
|
274
386
|
status: z.enum(["pending", "in_progress", "completed"]).optional(),
|
|
275
387
|
}, async ({ projectId, ...body }) => {
|
|
276
|
-
const
|
|
388
|
+
const client = getApi();
|
|
389
|
+
if (!client)
|
|
390
|
+
return authRequiredResponse();
|
|
391
|
+
const data = await client.post(`/api/projects/${projectId}/deliverables`, body);
|
|
277
392
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
278
393
|
});
|
|
279
394
|
server.tool("update_deliverable", "Update a deliverable", {
|
|
@@ -283,12 +398,18 @@ server.tool("update_deliverable", "Update a deliverable", {
|
|
|
283
398
|
description: z.string().optional(),
|
|
284
399
|
status: z.enum(["pending", "in_progress", "completed"]).optional(),
|
|
285
400
|
}, async ({ projectId, deliverableId, ...updates }) => {
|
|
286
|
-
const
|
|
401
|
+
const client = getApi();
|
|
402
|
+
if (!client)
|
|
403
|
+
return authRequiredResponse();
|
|
404
|
+
const data = await client.patch(`/api/projects/${projectId}/deliverables/${deliverableId}`, updates);
|
|
287
405
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
288
406
|
});
|
|
289
407
|
// ── Applications (Pipeline) ───────────────────────────────────────────────
|
|
290
408
|
server.tool("list_applications", "List all applications in the pipeline", {}, async () => {
|
|
291
|
-
const
|
|
409
|
+
const client = getApi();
|
|
410
|
+
if (!client)
|
|
411
|
+
return authRequiredResponse();
|
|
412
|
+
const data = await client.get("/api/admin/applications");
|
|
292
413
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
293
414
|
});
|
|
294
415
|
// ── Start server ───────────────────────────────────────────────────────────
|