@dudamel/mcp-integrations 2026.930.2 → 2026.1002.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/dist/index.js +109 -3
- package/dist/server.d.ts.map +1 -1
- package/dist/stdio-server.js +109 -3
- package/dist/tools/mcp-catalog.d.ts +2 -2
- package/dist/tools/mcp-tools.d.ts +12 -0
- package/dist/tools/mcp-tools.d.ts.map +1 -0
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -267,7 +267,7 @@ function registerMcpCatalogTools(server, client, _ctx) {
|
|
|
267
267
|
server.registerTool(
|
|
268
268
|
"list-mcp-catalog",
|
|
269
269
|
{
|
|
270
|
-
description: "List the catalog of
|
|
270
|
+
description: "List the catalog of Dudamel's own MCP servers that are not tied to an integration: context, memory and the admin surfaces. MCP servers for external services (GitHub, Jira, Slack, \u2026) are not here \u2014 they come from creating an integration instance (see list-integration-types).",
|
|
271
271
|
annotations: { readOnlyHint: true }
|
|
272
272
|
},
|
|
273
273
|
async () => {
|
|
@@ -277,12 +277,118 @@ function registerMcpCatalogTools(server, client, _ctx) {
|
|
|
277
277
|
);
|
|
278
278
|
}
|
|
279
279
|
|
|
280
|
+
// src/tools/mcp-tools.ts
|
|
281
|
+
import { z as z3 } from "zod";
|
|
282
|
+
var SUMMARY_MAX_CHARS = 160;
|
|
283
|
+
function registerMcpToolLookupTools(server, client, _ctx) {
|
|
284
|
+
const fetchTools = async () => (await client.get("/api/mcp-tools")).items;
|
|
285
|
+
server.registerTool(
|
|
286
|
+
"list-mcp-tools",
|
|
287
|
+
{
|
|
288
|
+
description: "List the tools exposed by the MCP servers available to you. Without arguments, returns one line per server (tool counts); pass `server` (name or id) for its tools, or `query` to search tool names and descriptions across servers. Each tool comes with whether it only reads (readOnly) and a one-line summary \u2014 call get-mcp-tool for its full description and argument schema before using it in a workflow step.",
|
|
289
|
+
inputSchema: {
|
|
290
|
+
server: z3.string().optional().describe("MCP server name or id"),
|
|
291
|
+
query: z3.string().optional().describe("Case-insensitive text to match in tool names and descriptions")
|
|
292
|
+
},
|
|
293
|
+
annotations: { readOnlyHint: true }
|
|
294
|
+
},
|
|
295
|
+
async ({ server: serverRef, query }) => {
|
|
296
|
+
let tools = await fetchTools();
|
|
297
|
+
if (serverRef) {
|
|
298
|
+
tools = tools.filter((t) => matchesServer(t, serverRef));
|
|
299
|
+
if (tools.length === 0) throw new Error(noServerMessage(serverRef));
|
|
300
|
+
}
|
|
301
|
+
if (query) {
|
|
302
|
+
const q = query.toLowerCase();
|
|
303
|
+
tools = tools.filter(
|
|
304
|
+
(t) => t.name.toLowerCase().includes(q) || t.description?.toLowerCase().includes(q)
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
if (!serverRef && !query) {
|
|
308
|
+
return jsonResult({ servers: summarizeServers(tools) });
|
|
309
|
+
}
|
|
310
|
+
return jsonResult({
|
|
311
|
+
tools: tools.map((t) => ({
|
|
312
|
+
server: t.mcpServer,
|
|
313
|
+
name: t.name,
|
|
314
|
+
readOnly: isReadOnly(t),
|
|
315
|
+
summary: summarize(t.description)
|
|
316
|
+
})),
|
|
317
|
+
total: tools.length
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
);
|
|
321
|
+
server.registerTool(
|
|
322
|
+
"get-mcp-tool",
|
|
323
|
+
{
|
|
324
|
+
description: "Get one MCP tool in full: its description, the JSON schema of its arguments (inputSchema) and its annotations (readOnlyHint, destructiveHint, \u2026). Use it to write the `arguments:` of a workflow `mcp:` step instead of guessing them.",
|
|
325
|
+
inputSchema: {
|
|
326
|
+
server: z3.string().describe("MCP server name or id"),
|
|
327
|
+
tool: z3.string().describe("Tool name, as list-mcp-tools returns it")
|
|
328
|
+
},
|
|
329
|
+
annotations: { readOnlyHint: true }
|
|
330
|
+
},
|
|
331
|
+
async ({ server: serverRef, tool }) => {
|
|
332
|
+
const ofServer = (await fetchTools()).filter((t) => matchesServer(t, serverRef));
|
|
333
|
+
if (ofServer.length === 0) throw new Error(noServerMessage(serverRef));
|
|
334
|
+
const found = ofServer.find((t) => t.name === tool);
|
|
335
|
+
if (!found) {
|
|
336
|
+
throw new Error(
|
|
337
|
+
`MCP server '${serverRef}' has no tool '${tool}'. Call list-mcp-tools with server='${serverRef}' for the names it exposes.`
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
return jsonResult({
|
|
341
|
+
server: found.mcpServer,
|
|
342
|
+
serverId: found.mcpServerId,
|
|
343
|
+
name: found.name,
|
|
344
|
+
readOnly: isReadOnly(found),
|
|
345
|
+
description: found.description ?? "",
|
|
346
|
+
inputSchema: found.inputSchema ?? { type: "object", properties: {} },
|
|
347
|
+
annotations: found.annotations ?? {}
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
function matchesServer(t, ref) {
|
|
353
|
+
return t.mcpServer === ref || t.mcpServerId === ref;
|
|
354
|
+
}
|
|
355
|
+
function isReadOnly(t) {
|
|
356
|
+
return t.annotations?.readOnlyHint === true;
|
|
357
|
+
}
|
|
358
|
+
function noServerMessage(ref) {
|
|
359
|
+
return `No tools found for MCP server '${ref}': it does not exist, your roles do not grant it, or its tools have not been discovered yet. Call list-mcp-tools without arguments for the servers you can use.`;
|
|
360
|
+
}
|
|
361
|
+
function summarize(description) {
|
|
362
|
+
const firstLine = (description ?? "").trim().split("\n")[0] ?? "";
|
|
363
|
+
return firstLine.length > SUMMARY_MAX_CHARS ? `${firstLine.slice(0, SUMMARY_MAX_CHARS - 1).trimEnd()}\u2026` : firstLine;
|
|
364
|
+
}
|
|
365
|
+
function summarizeServers(tools) {
|
|
366
|
+
const byServer = /* @__PURE__ */ new Map();
|
|
367
|
+
for (const t of tools) {
|
|
368
|
+
const entry = byServer.get(t.mcpServerId) ?? {
|
|
369
|
+
server: t.mcpServer,
|
|
370
|
+
serverId: t.mcpServerId,
|
|
371
|
+
tools: 0,
|
|
372
|
+
readOnlyTools: 0
|
|
373
|
+
};
|
|
374
|
+
entry.tools++;
|
|
375
|
+
if (isReadOnly(t)) entry.readOnlyTools++;
|
|
376
|
+
byServer.set(t.mcpServerId, entry);
|
|
377
|
+
}
|
|
378
|
+
return [...byServer.values()].sort((a, b) => a.server.localeCompare(b.server));
|
|
379
|
+
}
|
|
380
|
+
|
|
280
381
|
// src/server.ts
|
|
281
382
|
function createServer(config) {
|
|
282
383
|
return createSurfaceServer({
|
|
283
384
|
name: "dudamel-integrations",
|
|
284
|
-
instructions: `Dudamel Integrations MCP \u2014 manage integration instances, MCP servers, and the MCP catalog. Running as role=${config.role}, readOnly=${config.readOnly ?? false}.`,
|
|
285
|
-
registrars: [
|
|
385
|
+
instructions: `Dudamel Integrations MCP \u2014 manage integration instances, MCP servers, and the MCP catalog, and look up the tools each MCP server exposes (list-mcp-tools / get-mcp-tool) before wiring them into a workflow step. Running as role=${config.role}, readOnly=${config.readOnly ?? false}.`,
|
|
386
|
+
registrars: [
|
|
387
|
+
registerIntegrationTools,
|
|
388
|
+
registerMcpServerTools,
|
|
389
|
+
registerMcpCatalogTools,
|
|
390
|
+
registerMcpToolLookupTools
|
|
391
|
+
],
|
|
286
392
|
config
|
|
287
393
|
});
|
|
288
394
|
}
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAM3D,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,4DAgBnD"}
|
package/dist/stdio-server.js
CHANGED
|
@@ -310,7 +310,7 @@ function registerMcpCatalogTools(server2, client, _ctx) {
|
|
|
310
310
|
server2.registerTool(
|
|
311
311
|
"list-mcp-catalog",
|
|
312
312
|
{
|
|
313
|
-
description: "List the catalog of
|
|
313
|
+
description: "List the catalog of Dudamel's own MCP servers that are not tied to an integration: context, memory and the admin surfaces. MCP servers for external services (GitHub, Jira, Slack, \u2026) are not here \u2014 they come from creating an integration instance (see list-integration-types).",
|
|
314
314
|
annotations: { readOnlyHint: true }
|
|
315
315
|
},
|
|
316
316
|
async () => {
|
|
@@ -320,12 +320,118 @@ function registerMcpCatalogTools(server2, client, _ctx) {
|
|
|
320
320
|
);
|
|
321
321
|
}
|
|
322
322
|
|
|
323
|
+
// src/tools/mcp-tools.ts
|
|
324
|
+
import { z as z3 } from "zod";
|
|
325
|
+
var SUMMARY_MAX_CHARS = 160;
|
|
326
|
+
function registerMcpToolLookupTools(server2, client, _ctx) {
|
|
327
|
+
const fetchTools = async () => (await client.get("/api/mcp-tools")).items;
|
|
328
|
+
server2.registerTool(
|
|
329
|
+
"list-mcp-tools",
|
|
330
|
+
{
|
|
331
|
+
description: "List the tools exposed by the MCP servers available to you. Without arguments, returns one line per server (tool counts); pass `server` (name or id) for its tools, or `query` to search tool names and descriptions across servers. Each tool comes with whether it only reads (readOnly) and a one-line summary \u2014 call get-mcp-tool for its full description and argument schema before using it in a workflow step.",
|
|
332
|
+
inputSchema: {
|
|
333
|
+
server: z3.string().optional().describe("MCP server name or id"),
|
|
334
|
+
query: z3.string().optional().describe("Case-insensitive text to match in tool names and descriptions")
|
|
335
|
+
},
|
|
336
|
+
annotations: { readOnlyHint: true }
|
|
337
|
+
},
|
|
338
|
+
async ({ server: serverRef, query }) => {
|
|
339
|
+
let tools = await fetchTools();
|
|
340
|
+
if (serverRef) {
|
|
341
|
+
tools = tools.filter((t) => matchesServer(t, serverRef));
|
|
342
|
+
if (tools.length === 0) throw new Error(noServerMessage(serverRef));
|
|
343
|
+
}
|
|
344
|
+
if (query) {
|
|
345
|
+
const q = query.toLowerCase();
|
|
346
|
+
tools = tools.filter(
|
|
347
|
+
(t) => t.name.toLowerCase().includes(q) || t.description?.toLowerCase().includes(q)
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
if (!serverRef && !query) {
|
|
351
|
+
return jsonResult({ servers: summarizeServers(tools) });
|
|
352
|
+
}
|
|
353
|
+
return jsonResult({
|
|
354
|
+
tools: tools.map((t) => ({
|
|
355
|
+
server: t.mcpServer,
|
|
356
|
+
name: t.name,
|
|
357
|
+
readOnly: isReadOnly(t),
|
|
358
|
+
summary: summarize(t.description)
|
|
359
|
+
})),
|
|
360
|
+
total: tools.length
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
);
|
|
364
|
+
server2.registerTool(
|
|
365
|
+
"get-mcp-tool",
|
|
366
|
+
{
|
|
367
|
+
description: "Get one MCP tool in full: its description, the JSON schema of its arguments (inputSchema) and its annotations (readOnlyHint, destructiveHint, \u2026). Use it to write the `arguments:` of a workflow `mcp:` step instead of guessing them.",
|
|
368
|
+
inputSchema: {
|
|
369
|
+
server: z3.string().describe("MCP server name or id"),
|
|
370
|
+
tool: z3.string().describe("Tool name, as list-mcp-tools returns it")
|
|
371
|
+
},
|
|
372
|
+
annotations: { readOnlyHint: true }
|
|
373
|
+
},
|
|
374
|
+
async ({ server: serverRef, tool }) => {
|
|
375
|
+
const ofServer = (await fetchTools()).filter((t) => matchesServer(t, serverRef));
|
|
376
|
+
if (ofServer.length === 0) throw new Error(noServerMessage(serverRef));
|
|
377
|
+
const found = ofServer.find((t) => t.name === tool);
|
|
378
|
+
if (!found) {
|
|
379
|
+
throw new Error(
|
|
380
|
+
`MCP server '${serverRef}' has no tool '${tool}'. Call list-mcp-tools with server='${serverRef}' for the names it exposes.`
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
return jsonResult({
|
|
384
|
+
server: found.mcpServer,
|
|
385
|
+
serverId: found.mcpServerId,
|
|
386
|
+
name: found.name,
|
|
387
|
+
readOnly: isReadOnly(found),
|
|
388
|
+
description: found.description ?? "",
|
|
389
|
+
inputSchema: found.inputSchema ?? { type: "object", properties: {} },
|
|
390
|
+
annotations: found.annotations ?? {}
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
function matchesServer(t, ref) {
|
|
396
|
+
return t.mcpServer === ref || t.mcpServerId === ref;
|
|
397
|
+
}
|
|
398
|
+
function isReadOnly(t) {
|
|
399
|
+
return t.annotations?.readOnlyHint === true;
|
|
400
|
+
}
|
|
401
|
+
function noServerMessage(ref) {
|
|
402
|
+
return `No tools found for MCP server '${ref}': it does not exist, your roles do not grant it, or its tools have not been discovered yet. Call list-mcp-tools without arguments for the servers you can use.`;
|
|
403
|
+
}
|
|
404
|
+
function summarize(description) {
|
|
405
|
+
const firstLine = (description ?? "").trim().split("\n")[0] ?? "";
|
|
406
|
+
return firstLine.length > SUMMARY_MAX_CHARS ? `${firstLine.slice(0, SUMMARY_MAX_CHARS - 1).trimEnd()}\u2026` : firstLine;
|
|
407
|
+
}
|
|
408
|
+
function summarizeServers(tools) {
|
|
409
|
+
const byServer = /* @__PURE__ */ new Map();
|
|
410
|
+
for (const t of tools) {
|
|
411
|
+
const entry = byServer.get(t.mcpServerId) ?? {
|
|
412
|
+
server: t.mcpServer,
|
|
413
|
+
serverId: t.mcpServerId,
|
|
414
|
+
tools: 0,
|
|
415
|
+
readOnlyTools: 0
|
|
416
|
+
};
|
|
417
|
+
entry.tools++;
|
|
418
|
+
if (isReadOnly(t)) entry.readOnlyTools++;
|
|
419
|
+
byServer.set(t.mcpServerId, entry);
|
|
420
|
+
}
|
|
421
|
+
return [...byServer.values()].sort((a, b) => a.server.localeCompare(b.server));
|
|
422
|
+
}
|
|
423
|
+
|
|
323
424
|
// src/server.ts
|
|
324
425
|
function createServer(config) {
|
|
325
426
|
return createSurfaceServer({
|
|
326
427
|
name: "dudamel-integrations",
|
|
327
|
-
instructions: `Dudamel Integrations MCP \u2014 manage integration instances, MCP servers, and the MCP catalog. Running as role=${config.role}, readOnly=${config.readOnly ?? false}.`,
|
|
328
|
-
registrars: [
|
|
428
|
+
instructions: `Dudamel Integrations MCP \u2014 manage integration instances, MCP servers, and the MCP catalog, and look up the tools each MCP server exposes (list-mcp-tools / get-mcp-tool) before wiring them into a workflow step. Running as role=${config.role}, readOnly=${config.readOnly ?? false}.`,
|
|
429
|
+
registrars: [
|
|
430
|
+
registerIntegrationTools,
|
|
431
|
+
registerMcpServerTools,
|
|
432
|
+
registerMcpCatalogTools,
|
|
433
|
+
registerMcpToolLookupTools
|
|
434
|
+
],
|
|
329
435
|
config
|
|
330
436
|
});
|
|
331
437
|
}
|
|
@@ -3,8 +3,8 @@ import type { DudamelApiClient } from '@dudamel/mcp-common';
|
|
|
3
3
|
import type { ToolContext } from '@dudamel/mcp-common';
|
|
4
4
|
/**
|
|
5
5
|
* `list-mcp-catalog` for the `integrations` surface (Plan 52 Parte C) — the
|
|
6
|
-
* catalog of
|
|
7
|
-
*
|
|
6
|
+
* catalog of Dudamel-owned MCP servers not tied to an integration (context,
|
|
7
|
+
* memory, the admin surfaces). Read-only, user-visible.
|
|
8
8
|
*/
|
|
9
9
|
export declare function registerMcpCatalogTools(server: McpServer, client: DudamelApiClient, _ctx: ToolContext): void;
|
|
10
10
|
//# sourceMappingURL=mcp-catalog.d.ts.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import type { DudamelApiClient } from '@dudamel/mcp-common';
|
|
3
|
+
import type { ToolContext } from '@dudamel/mcp-common';
|
|
4
|
+
/**
|
|
5
|
+
* Tool lookup for the `integrations` surface: what each MCP server available to the caller
|
|
6
|
+
* exposes, read from the tool lists the orchestrator discovers and caches. It is the only
|
|
7
|
+
* way an agent learns the tools of a server it does not have mounted itself — e.g. one it
|
|
8
|
+
* is writing a workflow `mcp:` step for. `GET /api/mcp-tools` filters by the caller's roles,
|
|
9
|
+
* so a user never sees the tools of a server its roles do not grant.
|
|
10
|
+
*/
|
|
11
|
+
export declare function registerMcpToolLookupTools(server: McpServer, client: DudamelApiClient, _ctx: ToolContext): void;
|
|
12
|
+
//# sourceMappingURL=mcp-tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-tools.d.ts","sourceRoot":"","sources":["../../src/tools/mcp-tools.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAMvD;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,WAAW,GAChB,IAAI,CAoFN"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dudamel/mcp-integrations",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.1002.1",
|
|
4
4
|
"description": "Dudamel integrations MCP — stdio client for Dudamel's integrations management surface over its REST API.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"type": "module",
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"esbuild": "^0.25.0",
|
|
35
35
|
"@dudamel/contracts": "0.1.0",
|
|
36
|
-
"@dudamel/mcp-
|
|
37
|
-
"@dudamel/mcp-
|
|
36
|
+
"@dudamel/mcp-tool-contract": "0.1.0",
|
|
37
|
+
"@dudamel/mcp-common": "0.1.0"
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
40
|
"build": "esbuild src/stdio-server.ts src/index.ts --bundle --platform=node --target=node20 --format=esm --outdir=dist --external:@modelcontextprotocol/sdk --external:zod && tsc --emitDeclarationOnly",
|