@delega-dev/mcp 1.0.6 → 1.0.8

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 (3) hide show
  1. package/README.md +7 -0
  2. package/dist/index.js +95 -31
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -34,9 +34,16 @@ Add to your MCP client config (e.g. Claude Desktop `claude_desktop_config.json`)
34
34
  |----------|---------|-------------|
35
35
  | `DELEGA_API_URL` | `http://127.0.0.1:18890` | Delega API endpoint |
36
36
  | `DELEGA_AGENT_KEY` | (none) | Agent API key for authenticated requests |
37
+ | `DELEGA_REVEAL_AGENT_KEYS` | `0` | Set to `1` only if you want MCP tool output to print full agent API keys |
37
38
 
38
39
  For the hosted tier, use `https://api.delega.dev` as the URL.
39
40
 
41
+ ## Security Notes
42
+
43
+ - Non-local `DELEGA_API_URL` values must use `https://`.
44
+ - Agent keys are passed through environment variables rather than command-line arguments, which avoids process-list leakage.
45
+ - MCP tool output redacts full agent API keys by default. Set `DELEGA_REVEAL_AGENT_KEYS=1` only when you explicitly want them printed back to the client.
46
+
40
47
  ## Tools
41
48
 
42
49
  | Tool | Description |
package/dist/index.js CHANGED
@@ -7,14 +7,34 @@ import { z } from "zod";
7
7
 
8
8
  // src/delega-client.ts
9
9
  var DEFAULT_BASE_URL = "http://127.0.0.1:18890";
10
+ var LOCAL_API_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1"]);
11
+ var DelegaApiError = class extends Error {
12
+ status;
13
+ statusText;
14
+ responseBody;
15
+ constructor(status, statusText, responseBody) {
16
+ super(`Delega API request failed (${status} ${statusText})`);
17
+ this.name = "DelegaApiError";
18
+ this.status = status;
19
+ this.statusText = statusText;
20
+ this.responseBody = responseBody;
21
+ }
22
+ };
23
+ function normalizeBaseUrl(rawUrl) {
24
+ const parsed = new URL(rawUrl);
25
+ if (parsed.protocol !== "https:" && !LOCAL_API_HOSTS.has(parsed.hostname)) {
26
+ throw new Error("Delega API URL must use HTTPS unless it points to localhost");
27
+ }
28
+ return rawUrl.replace(/\/+$/, "");
29
+ }
10
30
  var DelegaClient = class {
11
31
  baseUrl;
12
32
  agentKey;
13
33
  pathPrefix;
14
34
  constructor(baseUrl, agentKey) {
15
- this.baseUrl = (baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
35
+ this.baseUrl = normalizeBaseUrl(baseUrl || DEFAULT_BASE_URL);
16
36
  this.agentKey = agentKey;
17
- this.pathPrefix = this.baseUrl.includes("api.delega.dev") ? "/v1" : "/api";
37
+ this.pathPrefix = new URL(this.baseUrl).hostname === "api.delega.dev" ? "/v1" : "/api";
18
38
  }
19
39
  async request(method, path, body, query) {
20
40
  const url = new URL(path, this.baseUrl);
@@ -38,9 +58,7 @@ var DelegaClient = class {
38
58
  });
39
59
  if (!res.ok) {
40
60
  const text = await res.text().catch(() => "");
41
- throw new Error(
42
- `Delega API error: ${res.status} ${res.statusText}${text ? ` \u2014 ${text}` : ""}`
43
- );
61
+ throw new DelegaApiError(res.status, res.statusText, text);
44
62
  }
45
63
  if (res.status === 204) {
46
64
  return void 0;
@@ -130,15 +148,62 @@ function formatTask(t) {
130
148
  function formatProject(p) {
131
149
  return `[#${p.id}] ${p.name}`;
132
150
  }
151
+ function maskApiKey(key) {
152
+ if (key.length <= 12) return key;
153
+ return `${key.slice(0, 8)}...${key.slice(-4)}`;
154
+ }
133
155
  function formatAgent(a) {
134
156
  const lines = [];
135
157
  lines.push(`[#${a.id}] ${a.name}${a.display_name ? ` (${a.display_name})` : ""}`);
136
158
  if (a.description) lines.push(` Description: ${a.description}`);
137
- if (a.api_key) lines.push(` API Key: ${a.api_key}`);
159
+ if (a.api_key) {
160
+ if (process.env.DELEGA_REVEAL_AGENT_KEYS === "1") {
161
+ lines.push(` API Key: ${a.api_key}`);
162
+ } else {
163
+ lines.push(` API Key Preview: ${maskApiKey(a.api_key)}`);
164
+ }
165
+ }
138
166
  if (a.permissions?.length) lines.push(` Permissions: ${a.permissions.join(", ")}`);
139
167
  if (a.active !== void 0) lines.push(` Active: ${a.active ? "yes" : "no"}`);
140
168
  return lines.join("\n");
141
169
  }
170
+ function sanitizeToolError(error) {
171
+ if (error instanceof DelegaApiError) {
172
+ if (error.responseBody) {
173
+ console.error("Delega API error response:", {
174
+ status: error.status,
175
+ statusText: error.statusText,
176
+ body: error.responseBody
177
+ });
178
+ }
179
+ if (error.status === 400 || error.status === 422) {
180
+ return "Delega API rejected the request. Check the tool inputs and try again.";
181
+ }
182
+ if (error.status === 401) {
183
+ return "Delega API authentication failed. Check DELEGA_AGENT_KEY.";
184
+ }
185
+ if (error.status === 403) {
186
+ return "Delega API denied this action.";
187
+ }
188
+ if (error.status === 404) {
189
+ return "The requested Delega resource was not found.";
190
+ }
191
+ if (error.status >= 500) {
192
+ return "Delega API returned a server error.";
193
+ }
194
+ return `Delega API request failed (${error.status} ${error.statusText}).`;
195
+ }
196
+ if (error instanceof Error) {
197
+ return error.message;
198
+ }
199
+ return "Unexpected error";
200
+ }
201
+ function toolErrorResult(error) {
202
+ return {
203
+ content: [{ type: "text", text: `Error: ${sanitizeToolError(error)}` }],
204
+ isError: true
205
+ };
206
+ }
142
207
  var server = new McpServer({
143
208
  name: "delega-mcp",
144
209
  version: "1.0.0"
@@ -160,8 +225,8 @@ server.tool(
160
225
  }
161
226
  const text = tasks.map(formatTask).join("\n\n");
162
227
  return { content: [{ type: "text", text }] };
163
- } catch (e) {
164
- return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
228
+ } catch (error) {
229
+ return toolErrorResult(error);
165
230
  }
166
231
  }
167
232
  );
@@ -175,8 +240,8 @@ server.tool(
175
240
  try {
176
241
  const task = await client.getTask(task_id);
177
242
  return { content: [{ type: "text", text: formatTask(task) }] };
178
- } catch (e) {
179
- return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
243
+ } catch (error) {
244
+ return toolErrorResult(error);
180
245
  }
181
246
  }
182
247
  );
@@ -199,8 +264,8 @@ server.tool(
199
264
 
200
265
  ${formatTask(task)}` }]
201
266
  };
202
- } catch (e) {
203
- return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
267
+ } catch (error) {
268
+ return toolErrorResult(error);
204
269
  }
205
270
  }
206
271
  );
@@ -224,8 +289,8 @@ server.tool(
224
289
 
225
290
  ${formatTask(task)}` }]
226
291
  };
227
- } catch (e) {
228
- return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
292
+ } catch (error) {
293
+ return toolErrorResult(error);
229
294
  }
230
295
  }
231
296
  );
@@ -244,8 +309,8 @@ server.tool(
244
309
  Next occurrence: ${task.next_occurrence}`;
245
310
  }
246
311
  return { content: [{ type: "text", text }] };
247
- } catch (e) {
248
- return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
312
+ } catch (error) {
313
+ return toolErrorResult(error);
249
314
  }
250
315
  }
251
316
  );
@@ -261,8 +326,8 @@ server.tool(
261
326
  return {
262
327
  content: [{ type: "text", text: `Task #${task_id} deleted.` }]
263
328
  };
264
- } catch (e) {
265
- return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
329
+ } catch (error) {
330
+ return toolErrorResult(error);
266
331
  }
267
332
  }
268
333
  );
@@ -286,8 +351,8 @@ server.tool(
286
351
  }
287
352
  ]
288
353
  };
289
- } catch (e) {
290
- return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
354
+ } catch (error) {
355
+ return toolErrorResult(error);
291
356
  }
292
357
  }
293
358
  );
@@ -303,8 +368,8 @@ server.tool(
303
368
  }
304
369
  const text = projects.map(formatProject).join("\n");
305
370
  return { content: [{ type: "text", text }] };
306
- } catch (e) {
307
- return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
371
+ } catch (error) {
372
+ return toolErrorResult(error);
308
373
  }
309
374
  }
310
375
  );
@@ -327,8 +392,8 @@ server.tool(
327
392
  }
328
393
  }
329
394
  return { content: [{ type: "text", text: lines.join("\n") }] };
330
- } catch (e) {
331
- return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
395
+ } catch (error) {
396
+ return toolErrorResult(error);
332
397
  }
333
398
  }
334
399
  );
@@ -344,8 +409,8 @@ server.tool(
344
409
  }
345
410
  const text = agents.map(formatAgent).join("\n\n");
346
411
  return { content: [{ type: "text", text }] };
347
- } catch (e) {
348
- return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
412
+ } catch (error) {
413
+ return toolErrorResult(error);
349
414
  }
350
415
  }
351
416
  );
@@ -361,15 +426,14 @@ server.tool(
361
426
  async (params) => {
362
427
  try {
363
428
  const agent = await client.registerAgent(params);
429
+ const warning = process.env.DELEGA_REVEAL_AGENT_KEYS === "1" ? "\n\n\u26A0\uFE0F Save the API key \u2014 it won't be shown again." : "\n\nAPI keys are redacted by default in MCP output. Set DELEGA_REVEAL_AGENT_KEYS=1 to reveal them.";
364
430
  return {
365
431
  content: [{ type: "text", text: `Agent registered:
366
432
 
367
- ${formatAgent(agent)}
368
-
369
- \u26A0\uFE0F Save the API key \u2014 it won't be shown again.` }]
433
+ ${formatAgent(agent)}${warning}` }]
370
434
  };
371
- } catch (e) {
372
- return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
435
+ } catch (error) {
436
+ return toolErrorResult(error);
373
437
  }
374
438
  }
375
439
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@delega-dev/mcp",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "mcpName": "io.github.delega-dev/delega",
5
5
  "description": "MCP server for Delega — task infrastructure for AI agents",
6
6
  "type": "module",