@dudamel/mcp-integrations 2026.917.2
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.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +289 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/stdio-server.d.ts +3 -0
- package/dist/stdio-server.d.ts.map +1 -0
- package/dist/stdio-server.js +359 -0
- package/dist/tools/integrations.d.ts +5 -0
- package/dist/tools/integrations.d.ts.map +1 -0
- package/dist/tools/mcp-catalog.d.ts +10 -0
- package/dist/tools/mcp-catalog.d.ts.map +1 -0
- package/dist/tools/mcp-servers.d.ts +15 -0
- package/dist/tools/mcp-servers.d.ts.map +1 -0
- package/package.json +45 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
// ../common/dist/http-client.js
|
|
2
|
+
function parseApiErrorBody(body) {
|
|
3
|
+
try {
|
|
4
|
+
const parsed = JSON.parse(body);
|
|
5
|
+
if (typeof parsed !== "object" || parsed === null || !("error" in parsed))
|
|
6
|
+
return void 0;
|
|
7
|
+
const error = parsed.error;
|
|
8
|
+
if (typeof error === "object" && error !== null && typeof error.code === "string" && typeof error.message === "string") {
|
|
9
|
+
return error;
|
|
10
|
+
}
|
|
11
|
+
return void 0;
|
|
12
|
+
} catch {
|
|
13
|
+
return void 0;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
var DudamelApiError = class extends Error {
|
|
17
|
+
status;
|
|
18
|
+
statusText;
|
|
19
|
+
body;
|
|
20
|
+
/** Stable machine-readable code from the API error body, or 'UNKNOWN' for non-conforming bodies. */
|
|
21
|
+
code;
|
|
22
|
+
/** Per-field validation errors, when the API provided them. */
|
|
23
|
+
details;
|
|
24
|
+
/** Extra machine-readable data (e.g. `inputsSchema` on execute validation failures). */
|
|
25
|
+
context;
|
|
26
|
+
constructor(status, statusText, body) {
|
|
27
|
+
const apiError = parseApiErrorBody(body);
|
|
28
|
+
const lines = [
|
|
29
|
+
apiError ? `Dudamel API error ${status} ${statusText}: ${apiError.code}: ${apiError.message}` : `Dudamel API error ${status} ${statusText}: ${body}`
|
|
30
|
+
];
|
|
31
|
+
if (apiError?.details?.length) {
|
|
32
|
+
lines.push(...apiError.details.map((d) => ` - ${d.path}: ${d.message}`));
|
|
33
|
+
}
|
|
34
|
+
if (apiError?.context?.["inputsSchema"] !== void 0) {
|
|
35
|
+
lines.push(`Expected inputs schema: ${JSON.stringify(apiError.context["inputsSchema"])}`);
|
|
36
|
+
}
|
|
37
|
+
super(lines.join("\n"));
|
|
38
|
+
this.status = status;
|
|
39
|
+
this.statusText = statusText;
|
|
40
|
+
this.body = body;
|
|
41
|
+
this.name = "DudamelApiError";
|
|
42
|
+
this.code = apiError?.code ?? "UNKNOWN";
|
|
43
|
+
this.details = apiError?.details;
|
|
44
|
+
this.context = apiError?.context;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
var DudamelApiClient = class {
|
|
48
|
+
baseUrl;
|
|
49
|
+
headers;
|
|
50
|
+
constructor(baseUrl, apiKey, authScheme) {
|
|
51
|
+
this.baseUrl = baseUrl.replace(/\/+$/, "");
|
|
52
|
+
this.headers = {
|
|
53
|
+
Authorization: `${authScheme ?? "ApiKey"} ${apiKey}`,
|
|
54
|
+
"Content-Type": "application/json"
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
async get(path, params) {
|
|
58
|
+
const url = this.buildUrl(path, params);
|
|
59
|
+
return this.request(url, { method: "GET" });
|
|
60
|
+
}
|
|
61
|
+
async post(path, body) {
|
|
62
|
+
const url = this.buildUrl(path);
|
|
63
|
+
return this.request(url, {
|
|
64
|
+
method: "POST",
|
|
65
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
async put(path, body) {
|
|
69
|
+
const url = this.buildUrl(path);
|
|
70
|
+
return this.request(url, {
|
|
71
|
+
method: "PUT",
|
|
72
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
async delete(path) {
|
|
76
|
+
const url = this.buildUrl(path);
|
|
77
|
+
return this.request(url, { method: "DELETE" });
|
|
78
|
+
}
|
|
79
|
+
buildUrl(path, params) {
|
|
80
|
+
const url = new URL(path, this.baseUrl);
|
|
81
|
+
if (params) {
|
|
82
|
+
for (const [key, value] of Object.entries(params)) {
|
|
83
|
+
if (value !== void 0 && value !== "") {
|
|
84
|
+
url.searchParams.set(key, value);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return url.toString();
|
|
89
|
+
}
|
|
90
|
+
async request(url, init) {
|
|
91
|
+
const response = await fetch(url, {
|
|
92
|
+
...init,
|
|
93
|
+
headers: this.headers
|
|
94
|
+
});
|
|
95
|
+
if (!response.ok) {
|
|
96
|
+
const body = await response.text();
|
|
97
|
+
throw new DudamelApiError(response.status, response.statusText, body);
|
|
98
|
+
}
|
|
99
|
+
const text = await response.text();
|
|
100
|
+
if (!text) {
|
|
101
|
+
return void 0;
|
|
102
|
+
}
|
|
103
|
+
return JSON.parse(text);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// ../common/dist/read-only-filter.js
|
|
108
|
+
function withReadOnlyFilter(server, readOnly) {
|
|
109
|
+
if (!readOnly)
|
|
110
|
+
return server;
|
|
111
|
+
const original = server.registerTool.bind(server);
|
|
112
|
+
const filtered = (...args) => {
|
|
113
|
+
const config = args[1];
|
|
114
|
+
if (config?.annotations?.readOnlyHint === false) {
|
|
115
|
+
return void 0;
|
|
116
|
+
}
|
|
117
|
+
return original(...args);
|
|
118
|
+
};
|
|
119
|
+
return new Proxy(server, {
|
|
120
|
+
get(target, prop, receiver) {
|
|
121
|
+
if (prop === "registerTool")
|
|
122
|
+
return filtered;
|
|
123
|
+
return Reflect.get(target, prop, receiver);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ../common/dist/create-server.js
|
|
129
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
130
|
+
function createSurfaceServer(opts) {
|
|
131
|
+
const { config } = opts;
|
|
132
|
+
const server = new McpServer({ name: opts.name, version: "0.1.0" }, {
|
|
133
|
+
capabilities: { tools: {} },
|
|
134
|
+
instructions: opts.instructions
|
|
135
|
+
});
|
|
136
|
+
const client = new DudamelApiClient(config.apiUrl, config.apiKey, config.authScheme);
|
|
137
|
+
const ctx = {
|
|
138
|
+
role: config.role,
|
|
139
|
+
readOnly: config.readOnly ?? false,
|
|
140
|
+
agentName: config.agentName,
|
|
141
|
+
callChain: config.callChain,
|
|
142
|
+
turnId: config.turnId,
|
|
143
|
+
rootTurnId: config.rootTurnId,
|
|
144
|
+
conversationId: config.conversationId,
|
|
145
|
+
requesterUserId: config.requesterUserId,
|
|
146
|
+
responseTarget: config.responseTarget
|
|
147
|
+
};
|
|
148
|
+
const host = withReadOnlyFilter(server, ctx.readOnly);
|
|
149
|
+
for (const register of opts.registrars)
|
|
150
|
+
register(host, client, ctx);
|
|
151
|
+
return server;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ../common/dist/util.js
|
|
155
|
+
function jsonResult(data) {
|
|
156
|
+
return {
|
|
157
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// src/tools/integrations.ts
|
|
162
|
+
import { z } from "zod";
|
|
163
|
+
function registerIntegrationTools(server, client, ctx) {
|
|
164
|
+
server.registerTool(
|
|
165
|
+
"list-integrations",
|
|
166
|
+
{
|
|
167
|
+
description: "List all integration instances (Slack, GitHub, Trello, etc.) with their status and configuration.",
|
|
168
|
+
annotations: { readOnlyHint: true }
|
|
169
|
+
},
|
|
170
|
+
async () => {
|
|
171
|
+
const page = await client.get("/api/integrations");
|
|
172
|
+
return jsonResult({ items: page.items, total: page.total });
|
|
173
|
+
}
|
|
174
|
+
);
|
|
175
|
+
server.registerTool(
|
|
176
|
+
"list-integration-types",
|
|
177
|
+
{
|
|
178
|
+
description: "List available integration types that can be configured (e.g. slack, github, trello).",
|
|
179
|
+
annotations: { readOnlyHint: true }
|
|
180
|
+
},
|
|
181
|
+
async () => {
|
|
182
|
+
const page = await client.get("/api/integration-types");
|
|
183
|
+
return jsonResult({ items: page.items, total: page.total });
|
|
184
|
+
}
|
|
185
|
+
);
|
|
186
|
+
if (ctx.readOnly || ctx.role !== "admin") return;
|
|
187
|
+
server.registerTool(
|
|
188
|
+
"activate-integration",
|
|
189
|
+
{
|
|
190
|
+
description: "Activate an integration instance.",
|
|
191
|
+
inputSchema: {
|
|
192
|
+
id: z.string().describe("Integration instance ID (UUID)")
|
|
193
|
+
},
|
|
194
|
+
annotations: { readOnlyHint: false, destructiveHint: false }
|
|
195
|
+
},
|
|
196
|
+
async ({ id }) => {
|
|
197
|
+
const result = await client.post(`/api/integrations/${id}/activate`);
|
|
198
|
+
return jsonResult(result);
|
|
199
|
+
}
|
|
200
|
+
);
|
|
201
|
+
server.registerTool(
|
|
202
|
+
"deactivate-integration",
|
|
203
|
+
{
|
|
204
|
+
description: "Deactivate an integration instance.",
|
|
205
|
+
inputSchema: {
|
|
206
|
+
id: z.string().describe("Integration instance ID (UUID)")
|
|
207
|
+
},
|
|
208
|
+
annotations: { readOnlyHint: false, destructiveHint: false }
|
|
209
|
+
},
|
|
210
|
+
async ({ id }) => {
|
|
211
|
+
const result = await client.post(`/api/integrations/${id}/deactivate`);
|
|
212
|
+
return jsonResult(result);
|
|
213
|
+
}
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// src/tools/mcp-servers.ts
|
|
218
|
+
import { z as z2 } from "zod";
|
|
219
|
+
function registerMcpServerTools(server, client, ctx) {
|
|
220
|
+
server.registerTool(
|
|
221
|
+
"list-mcp-servers",
|
|
222
|
+
{
|
|
223
|
+
description: "List all configured MCP server instances. Returns id, name, description, serverType, authMode, and OAuth status.",
|
|
224
|
+
annotations: { readOnlyHint: true }
|
|
225
|
+
},
|
|
226
|
+
async () => {
|
|
227
|
+
const page = await client.get("/api/mcp-servers");
|
|
228
|
+
return jsonResult({ items: page.items, total: page.total });
|
|
229
|
+
}
|
|
230
|
+
);
|
|
231
|
+
server.registerTool(
|
|
232
|
+
"get-mcp-server-secrets",
|
|
233
|
+
{
|
|
234
|
+
description: "List the secret key names configured for an MCP server. Does not reveal secret values.",
|
|
235
|
+
inputSchema: {
|
|
236
|
+
id: z2.string().describe("MCP Server ID (UUID)")
|
|
237
|
+
},
|
|
238
|
+
annotations: { readOnlyHint: true }
|
|
239
|
+
},
|
|
240
|
+
async ({ id }) => {
|
|
241
|
+
const secrets = await client.get(`/api/mcp-servers/${id}/secrets`);
|
|
242
|
+
return jsonResult(secrets);
|
|
243
|
+
}
|
|
244
|
+
);
|
|
245
|
+
if (ctx.readOnly || ctx.role !== "admin") return;
|
|
246
|
+
server.registerTool(
|
|
247
|
+
"set-mcp-server-secrets",
|
|
248
|
+
{
|
|
249
|
+
description: "Set or update the service account secrets for an MCP server.",
|
|
250
|
+
inputSchema: {
|
|
251
|
+
id: z2.string().describe("MCP Server ID (UUID)"),
|
|
252
|
+
secrets: z2.record(z2.string()).describe("Key-value pairs of secret names and values")
|
|
253
|
+
},
|
|
254
|
+
annotations: { readOnlyHint: false, destructiveHint: false }
|
|
255
|
+
},
|
|
256
|
+
async ({ id, secrets }) => {
|
|
257
|
+
const result = await client.put(`/api/mcp-servers/${id}/secrets`, secrets);
|
|
258
|
+
return jsonResult(result);
|
|
259
|
+
}
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// src/tools/mcp-catalog.ts
|
|
264
|
+
function registerMcpCatalogTools(server, client, _ctx) {
|
|
265
|
+
server.registerTool(
|
|
266
|
+
"list-mcp-catalog",
|
|
267
|
+
{
|
|
268
|
+
description: "List the catalog of pre-curated MCP servers available for installation. Returns server type, connection methods, permission modes, and setup guides.",
|
|
269
|
+
annotations: { readOnlyHint: true }
|
|
270
|
+
},
|
|
271
|
+
async () => {
|
|
272
|
+
const page = await client.get("/api/mcp-catalog");
|
|
273
|
+
return jsonResult({ items: page.items, total: page.total });
|
|
274
|
+
}
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// src/server.ts
|
|
279
|
+
function createServer(config) {
|
|
280
|
+
return createSurfaceServer({
|
|
281
|
+
name: "dudamel-integrations",
|
|
282
|
+
instructions: `Dudamel Integrations MCP \u2014 manage integration instances, MCP servers, and the MCP catalog. Running as role=${config.role}, readOnly=${config.readOnly ?? false}.`,
|
|
283
|
+
registrars: [registerIntegrationTools, registerMcpServerTools, registerMcpCatalogTools],
|
|
284
|
+
config
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
export {
|
|
288
|
+
createServer
|
|
289
|
+
};
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAK3D,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,4DASnD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stdio-server.d.ts","sourceRoot":"","sources":["../src/stdio-server.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/stdio-server.ts
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
|
|
6
|
+
// ../common/dist/http-client.js
|
|
7
|
+
function parseApiErrorBody(body) {
|
|
8
|
+
try {
|
|
9
|
+
const parsed = JSON.parse(body);
|
|
10
|
+
if (typeof parsed !== "object" || parsed === null || !("error" in parsed))
|
|
11
|
+
return void 0;
|
|
12
|
+
const error = parsed.error;
|
|
13
|
+
if (typeof error === "object" && error !== null && typeof error.code === "string" && typeof error.message === "string") {
|
|
14
|
+
return error;
|
|
15
|
+
}
|
|
16
|
+
return void 0;
|
|
17
|
+
} catch {
|
|
18
|
+
return void 0;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
var DudamelApiError = class extends Error {
|
|
22
|
+
status;
|
|
23
|
+
statusText;
|
|
24
|
+
body;
|
|
25
|
+
/** Stable machine-readable code from the API error body, or 'UNKNOWN' for non-conforming bodies. */
|
|
26
|
+
code;
|
|
27
|
+
/** Per-field validation errors, when the API provided them. */
|
|
28
|
+
details;
|
|
29
|
+
/** Extra machine-readable data (e.g. `inputsSchema` on execute validation failures). */
|
|
30
|
+
context;
|
|
31
|
+
constructor(status, statusText, body) {
|
|
32
|
+
const apiError = parseApiErrorBody(body);
|
|
33
|
+
const lines = [
|
|
34
|
+
apiError ? `Dudamel API error ${status} ${statusText}: ${apiError.code}: ${apiError.message}` : `Dudamel API error ${status} ${statusText}: ${body}`
|
|
35
|
+
];
|
|
36
|
+
if (apiError?.details?.length) {
|
|
37
|
+
lines.push(...apiError.details.map((d) => ` - ${d.path}: ${d.message}`));
|
|
38
|
+
}
|
|
39
|
+
if (apiError?.context?.["inputsSchema"] !== void 0) {
|
|
40
|
+
lines.push(`Expected inputs schema: ${JSON.stringify(apiError.context["inputsSchema"])}`);
|
|
41
|
+
}
|
|
42
|
+
super(lines.join("\n"));
|
|
43
|
+
this.status = status;
|
|
44
|
+
this.statusText = statusText;
|
|
45
|
+
this.body = body;
|
|
46
|
+
this.name = "DudamelApiError";
|
|
47
|
+
this.code = apiError?.code ?? "UNKNOWN";
|
|
48
|
+
this.details = apiError?.details;
|
|
49
|
+
this.context = apiError?.context;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
var DudamelApiClient = class {
|
|
53
|
+
baseUrl;
|
|
54
|
+
headers;
|
|
55
|
+
constructor(baseUrl, apiKey, authScheme) {
|
|
56
|
+
this.baseUrl = baseUrl.replace(/\/+$/, "");
|
|
57
|
+
this.headers = {
|
|
58
|
+
Authorization: `${authScheme ?? "ApiKey"} ${apiKey}`,
|
|
59
|
+
"Content-Type": "application/json"
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
async get(path, params) {
|
|
63
|
+
const url = this.buildUrl(path, params);
|
|
64
|
+
return this.request(url, { method: "GET" });
|
|
65
|
+
}
|
|
66
|
+
async post(path, body) {
|
|
67
|
+
const url = this.buildUrl(path);
|
|
68
|
+
return this.request(url, {
|
|
69
|
+
method: "POST",
|
|
70
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
async put(path, body) {
|
|
74
|
+
const url = this.buildUrl(path);
|
|
75
|
+
return this.request(url, {
|
|
76
|
+
method: "PUT",
|
|
77
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
async delete(path) {
|
|
81
|
+
const url = this.buildUrl(path);
|
|
82
|
+
return this.request(url, { method: "DELETE" });
|
|
83
|
+
}
|
|
84
|
+
buildUrl(path, params) {
|
|
85
|
+
const url = new URL(path, this.baseUrl);
|
|
86
|
+
if (params) {
|
|
87
|
+
for (const [key, value] of Object.entries(params)) {
|
|
88
|
+
if (value !== void 0 && value !== "") {
|
|
89
|
+
url.searchParams.set(key, value);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return url.toString();
|
|
94
|
+
}
|
|
95
|
+
async request(url, init) {
|
|
96
|
+
const response = await fetch(url, {
|
|
97
|
+
...init,
|
|
98
|
+
headers: this.headers
|
|
99
|
+
});
|
|
100
|
+
if (!response.ok) {
|
|
101
|
+
const body = await response.text();
|
|
102
|
+
throw new DudamelApiError(response.status, response.statusText, body);
|
|
103
|
+
}
|
|
104
|
+
const text = await response.text();
|
|
105
|
+
if (!text) {
|
|
106
|
+
return void 0;
|
|
107
|
+
}
|
|
108
|
+
return JSON.parse(text);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// ../common/dist/resolve-role.js
|
|
113
|
+
var WHOAMI_TIMEOUT_MS = 5e3;
|
|
114
|
+
function isSystemRole(value) {
|
|
115
|
+
return value === "admin" || value === "user";
|
|
116
|
+
}
|
|
117
|
+
async function resolveSystemRole(options) {
|
|
118
|
+
if (isSystemRole(options.envRole))
|
|
119
|
+
return options.envRole;
|
|
120
|
+
const doFetch = options.fetchImpl ?? fetch;
|
|
121
|
+
const url = `${options.apiUrl.replace(/\/+$/, "")}/api/users/me`;
|
|
122
|
+
try {
|
|
123
|
+
const response = await doFetch(url, {
|
|
124
|
+
method: "GET",
|
|
125
|
+
headers: { Authorization: `ApiKey ${options.apiKey}` },
|
|
126
|
+
signal: AbortSignal.timeout(WHOAMI_TIMEOUT_MS)
|
|
127
|
+
});
|
|
128
|
+
if (!response.ok)
|
|
129
|
+
return "user";
|
|
130
|
+
const body = await response.json();
|
|
131
|
+
const role2 = body?.systemRole;
|
|
132
|
+
return isSystemRole(role2) ? role2 : "user";
|
|
133
|
+
} catch {
|
|
134
|
+
return "user";
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ../common/dist/read-only-filter.js
|
|
139
|
+
function withReadOnlyFilter(server2, readOnly) {
|
|
140
|
+
if (!readOnly)
|
|
141
|
+
return server2;
|
|
142
|
+
const original = server2.registerTool.bind(server2);
|
|
143
|
+
const filtered = (...args) => {
|
|
144
|
+
const config = args[1];
|
|
145
|
+
if (config?.annotations?.readOnlyHint === false) {
|
|
146
|
+
return void 0;
|
|
147
|
+
}
|
|
148
|
+
return original(...args);
|
|
149
|
+
};
|
|
150
|
+
return new Proxy(server2, {
|
|
151
|
+
get(target, prop, receiver) {
|
|
152
|
+
if (prop === "registerTool")
|
|
153
|
+
return filtered;
|
|
154
|
+
return Reflect.get(target, prop, receiver);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ../common/dist/create-server.js
|
|
160
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
161
|
+
function createSurfaceServer(opts) {
|
|
162
|
+
const { config } = opts;
|
|
163
|
+
const server2 = new McpServer({ name: opts.name, version: "0.1.0" }, {
|
|
164
|
+
capabilities: { tools: {} },
|
|
165
|
+
instructions: opts.instructions
|
|
166
|
+
});
|
|
167
|
+
const client = new DudamelApiClient(config.apiUrl, config.apiKey, config.authScheme);
|
|
168
|
+
const ctx2 = {
|
|
169
|
+
role: config.role,
|
|
170
|
+
readOnly: config.readOnly ?? false,
|
|
171
|
+
agentName: config.agentName,
|
|
172
|
+
callChain: config.callChain,
|
|
173
|
+
turnId: config.turnId,
|
|
174
|
+
rootTurnId: config.rootTurnId,
|
|
175
|
+
conversationId: config.conversationId,
|
|
176
|
+
requesterUserId: config.requesterUserId,
|
|
177
|
+
responseTarget: config.responseTarget
|
|
178
|
+
};
|
|
179
|
+
const host = withReadOnlyFilter(server2, ctx2.readOnly);
|
|
180
|
+
for (const register of opts.registrars)
|
|
181
|
+
register(host, client, ctx2);
|
|
182
|
+
return server2;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ../common/dist/stdio.js
|
|
186
|
+
function resolveStdioContext() {
|
|
187
|
+
let responseTarget;
|
|
188
|
+
const rawTarget = process.env["DUDAMEL_RESPONSE_TARGET"];
|
|
189
|
+
if (rawTarget) {
|
|
190
|
+
try {
|
|
191
|
+
responseTarget = JSON.parse(rawTarget);
|
|
192
|
+
} catch {
|
|
193
|
+
process.stderr.write("Warning: DUDAMEL_RESPONSE_TARGET is not valid JSON \u2014 ignoring.\n");
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
apiUrl: process.env["DUDAMEL_API_URL"] ?? "http://localhost:3000",
|
|
198
|
+
apiKey: process.env["DUDAMEL_API_KEY"],
|
|
199
|
+
envRole: process.env["DUDAMEL_USER_ROLE"],
|
|
200
|
+
// read/write access is per agent-assignment (Plan 52); the spawner injects the mode.
|
|
201
|
+
readOnly: process.env["DUDAMEL_MCP_MODE"] === "readonly",
|
|
202
|
+
agentName: process.env["DUDAMEL_AGENT_NAME"],
|
|
203
|
+
callChain: (process.env["DUDAMEL_CALL_CHAIN"] ?? "").split(",").map((s) => s.trim()).filter(Boolean),
|
|
204
|
+
turnId: process.env["DUDAMEL_TURN_ID"],
|
|
205
|
+
rootTurnId: process.env["DUDAMEL_ROOT_TURN_ID"],
|
|
206
|
+
conversationId: process.env["DUDAMEL_CONVERSATION_ID"],
|
|
207
|
+
requesterUserId: process.env["DUDAMEL_REQUESTER_USER_ID"],
|
|
208
|
+
responseTarget
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ../common/dist/util.js
|
|
213
|
+
function jsonResult(data) {
|
|
214
|
+
return {
|
|
215
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// src/tools/integrations.ts
|
|
220
|
+
import { z } from "zod";
|
|
221
|
+
function registerIntegrationTools(server2, client, ctx2) {
|
|
222
|
+
server2.registerTool(
|
|
223
|
+
"list-integrations",
|
|
224
|
+
{
|
|
225
|
+
description: "List all integration instances (Slack, GitHub, Trello, etc.) with their status and configuration.",
|
|
226
|
+
annotations: { readOnlyHint: true }
|
|
227
|
+
},
|
|
228
|
+
async () => {
|
|
229
|
+
const page = await client.get("/api/integrations");
|
|
230
|
+
return jsonResult({ items: page.items, total: page.total });
|
|
231
|
+
}
|
|
232
|
+
);
|
|
233
|
+
server2.registerTool(
|
|
234
|
+
"list-integration-types",
|
|
235
|
+
{
|
|
236
|
+
description: "List available integration types that can be configured (e.g. slack, github, trello).",
|
|
237
|
+
annotations: { readOnlyHint: true }
|
|
238
|
+
},
|
|
239
|
+
async () => {
|
|
240
|
+
const page = await client.get("/api/integration-types");
|
|
241
|
+
return jsonResult({ items: page.items, total: page.total });
|
|
242
|
+
}
|
|
243
|
+
);
|
|
244
|
+
if (ctx2.readOnly || ctx2.role !== "admin") return;
|
|
245
|
+
server2.registerTool(
|
|
246
|
+
"activate-integration",
|
|
247
|
+
{
|
|
248
|
+
description: "Activate an integration instance.",
|
|
249
|
+
inputSchema: {
|
|
250
|
+
id: z.string().describe("Integration instance ID (UUID)")
|
|
251
|
+
},
|
|
252
|
+
annotations: { readOnlyHint: false, destructiveHint: false }
|
|
253
|
+
},
|
|
254
|
+
async ({ id }) => {
|
|
255
|
+
const result = await client.post(`/api/integrations/${id}/activate`);
|
|
256
|
+
return jsonResult(result);
|
|
257
|
+
}
|
|
258
|
+
);
|
|
259
|
+
server2.registerTool(
|
|
260
|
+
"deactivate-integration",
|
|
261
|
+
{
|
|
262
|
+
description: "Deactivate an integration instance.",
|
|
263
|
+
inputSchema: {
|
|
264
|
+
id: z.string().describe("Integration instance ID (UUID)")
|
|
265
|
+
},
|
|
266
|
+
annotations: { readOnlyHint: false, destructiveHint: false }
|
|
267
|
+
},
|
|
268
|
+
async ({ id }) => {
|
|
269
|
+
const result = await client.post(`/api/integrations/${id}/deactivate`);
|
|
270
|
+
return jsonResult(result);
|
|
271
|
+
}
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// src/tools/mcp-servers.ts
|
|
276
|
+
import { z as z2 } from "zod";
|
|
277
|
+
function registerMcpServerTools(server2, client, ctx2) {
|
|
278
|
+
server2.registerTool(
|
|
279
|
+
"list-mcp-servers",
|
|
280
|
+
{
|
|
281
|
+
description: "List all configured MCP server instances. Returns id, name, description, serverType, authMode, and OAuth status.",
|
|
282
|
+
annotations: { readOnlyHint: true }
|
|
283
|
+
},
|
|
284
|
+
async () => {
|
|
285
|
+
const page = await client.get("/api/mcp-servers");
|
|
286
|
+
return jsonResult({ items: page.items, total: page.total });
|
|
287
|
+
}
|
|
288
|
+
);
|
|
289
|
+
server2.registerTool(
|
|
290
|
+
"get-mcp-server-secrets",
|
|
291
|
+
{
|
|
292
|
+
description: "List the secret key names configured for an MCP server. Does not reveal secret values.",
|
|
293
|
+
inputSchema: {
|
|
294
|
+
id: z2.string().describe("MCP Server ID (UUID)")
|
|
295
|
+
},
|
|
296
|
+
annotations: { readOnlyHint: true }
|
|
297
|
+
},
|
|
298
|
+
async ({ id }) => {
|
|
299
|
+
const secrets = await client.get(`/api/mcp-servers/${id}/secrets`);
|
|
300
|
+
return jsonResult(secrets);
|
|
301
|
+
}
|
|
302
|
+
);
|
|
303
|
+
if (ctx2.readOnly || ctx2.role !== "admin") return;
|
|
304
|
+
server2.registerTool(
|
|
305
|
+
"set-mcp-server-secrets",
|
|
306
|
+
{
|
|
307
|
+
description: "Set or update the service account secrets for an MCP server.",
|
|
308
|
+
inputSchema: {
|
|
309
|
+
id: z2.string().describe("MCP Server ID (UUID)"),
|
|
310
|
+
secrets: z2.record(z2.string()).describe("Key-value pairs of secret names and values")
|
|
311
|
+
},
|
|
312
|
+
annotations: { readOnlyHint: false, destructiveHint: false }
|
|
313
|
+
},
|
|
314
|
+
async ({ id, secrets }) => {
|
|
315
|
+
const result = await client.put(`/api/mcp-servers/${id}/secrets`, secrets);
|
|
316
|
+
return jsonResult(result);
|
|
317
|
+
}
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// src/tools/mcp-catalog.ts
|
|
322
|
+
function registerMcpCatalogTools(server2, client, _ctx) {
|
|
323
|
+
server2.registerTool(
|
|
324
|
+
"list-mcp-catalog",
|
|
325
|
+
{
|
|
326
|
+
description: "List the catalog of pre-curated MCP servers available for installation. Returns server type, connection methods, permission modes, and setup guides.",
|
|
327
|
+
annotations: { readOnlyHint: true }
|
|
328
|
+
},
|
|
329
|
+
async () => {
|
|
330
|
+
const page = await client.get("/api/mcp-catalog");
|
|
331
|
+
return jsonResult({ items: page.items, total: page.total });
|
|
332
|
+
}
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// src/server.ts
|
|
337
|
+
function createServer(config) {
|
|
338
|
+
return createSurfaceServer({
|
|
339
|
+
name: "dudamel-integrations",
|
|
340
|
+
instructions: `Dudamel Integrations MCP \u2014 manage integration instances, MCP servers, and the MCP catalog. Running as role=${config.role}, readOnly=${config.readOnly ?? false}.`,
|
|
341
|
+
registrars: [registerIntegrationTools, registerMcpServerTools, registerMcpCatalogTools],
|
|
342
|
+
config
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// src/stdio-server.ts
|
|
347
|
+
var ctx = resolveStdioContext();
|
|
348
|
+
if (!ctx.apiKey) {
|
|
349
|
+
process.stderr.write("Error: DUDAMEL_API_KEY environment variable is required.\n");
|
|
350
|
+
process.exit(1);
|
|
351
|
+
}
|
|
352
|
+
var role = await resolveSystemRole({
|
|
353
|
+
apiUrl: ctx.apiUrl,
|
|
354
|
+
apiKey: ctx.apiKey,
|
|
355
|
+
envRole: ctx.envRole
|
|
356
|
+
});
|
|
357
|
+
var server = createServer({ ...ctx, apiKey: ctx.apiKey, role });
|
|
358
|
+
var transport = new StdioServerTransport();
|
|
359
|
+
await server.connect(transport);
|
|
@@ -0,0 +1,5 @@
|
|
|
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
|
+
export declare function registerIntegrationTools(server: McpServer, client: DudamelApiClient, ctx: ToolContext): void;
|
|
5
|
+
//# sourceMappingURL=integrations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integrations.d.ts","sourceRoot":"","sources":["../../src/tools/integrations.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;AAGvD,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,gBAAgB,EACxB,GAAG,EAAE,WAAW,GACf,IAAI,CA8DN"}
|
|
@@ -0,0 +1,10 @@
|
|
|
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
|
+
* `list-mcp-catalog` for the `integrations` surface (Plan 52 Parte C) — the
|
|
6
|
+
* catalog of pre-curated MCP servers (how the platform connects to external
|
|
7
|
+
* tools). Read-only, user-visible (moved out of the old `system` tool group).
|
|
8
|
+
*/
|
|
9
|
+
export declare function registerMcpCatalogTools(server: McpServer, client: DudamelApiClient, _ctx: ToolContext): void;
|
|
10
|
+
//# sourceMappingURL=mcp-catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-catalog.d.ts","sourceRoot":"","sources":["../../src/tools/mcp-catalog.ts"],"names":[],"mappings":"AAAA,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;AAGvD;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,WAAW,GAChB,IAAI,CAaN"}
|
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
* MCP-server tools for the `integrations` surface (Plan 52 Parte C). Scoped to the
|
|
6
|
+
* integration-backed servers: listing them and managing their service secrets.
|
|
7
|
+
*
|
|
8
|
+
* The standalone MCP-server management path (create/update/delete) was pruned: it
|
|
9
|
+
* has no dashboard UI, `remote` servers don't spawn, and integration-less rows are
|
|
10
|
+
* world-visible without a permission model. `install-mcp-server` /
|
|
11
|
+
* `get-mcp-server-status` were mcp-runner leftovers (endpoints no longer exist).
|
|
12
|
+
* Agent↔MCP assignment lives in the `agents` surface (see agent-mcp.ts).
|
|
13
|
+
*/
|
|
14
|
+
export declare function registerMcpServerTools(server: McpServer, client: DudamelApiClient, ctx: ToolContext): void;
|
|
15
|
+
//# sourceMappingURL=mcp-servers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-servers.d.ts","sourceRoot":"","sources":["../../src/tools/mcp-servers.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;AAGvD;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,gBAAgB,EACxB,GAAG,EAAE,WAAW,GACf,IAAI,CAiDN"}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dudamel/mcp-integrations",
|
|
3
|
+
"version": "2026.917.2",
|
|
4
|
+
"description": "Dudamel integrations MCP — stdio client for Dudamel's integrations management surface over its REST API.",
|
|
5
|
+
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"bin": {
|
|
10
|
+
"dudamel-mcp-integrations": "./dist/stdio-server.js"
|
|
11
|
+
},
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/agents-squad/dudamel.git",
|
|
24
|
+
"directory": "packages/mcp/integrations"
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
31
|
+
"zod": "^3.24.4"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"esbuild": "^0.25.0",
|
|
35
|
+
"@dudamel/contracts": "0.1.0",
|
|
36
|
+
"@dudamel/mcp-common": "0.1.0"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"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",
|
|
40
|
+
"lint": "eslint src/",
|
|
41
|
+
"lint:fix": "eslint src/ --fix",
|
|
42
|
+
"test": "vitest run --passWithNoTests",
|
|
43
|
+
"clean": "rm -rf dist"
|
|
44
|
+
}
|
|
45
|
+
}
|