@topolo/mcp 0.9.3 → 0.9.5
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 +14 -0
- package/dist/dispatch.js +36 -25
- package/dist/http.js +38 -27
- package/dist/index.js +42 -32
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -118,6 +118,7 @@ credentials already carry their own immutable context.
|
|
|
118
118
|
active stored credential. **Refuse to start** if none is available.
|
|
119
119
|
2. Call `GET /api/me` to load the user's granted scopes + role.
|
|
120
120
|
3. Connect the stdio transport and begin serving MCP requests.
|
|
121
|
+
|
|
121
122
|
4. Advertise a fixed, bounded discovery/invocation surface. Agents search one
|
|
122
123
|
application at a time and call exact action IDs; the server never expands
|
|
123
124
|
one MCP tool per action or materializes the global catalog.
|
|
@@ -126,6 +127,19 @@ Startup is synchronous and fast (< 500 ms typical). If the credential is
|
|
|
126
127
|
rejected, the process logs the error to stderr and exits non-zero — host
|
|
127
128
|
clients surface that as a failed server launch.
|
|
128
129
|
|
|
130
|
+
## Data privacy boundary
|
|
131
|
+
|
|
132
|
+
TopoloMCP is a transport adapter and does not persist customer data. It has no
|
|
133
|
+
database, KV, R2, Durable Object, filesystem-write, analytics, or log-storage
|
|
134
|
+
binding. Credentials remain in the caller-owned environment or the shared CLI
|
|
135
|
+
credential store, and every request is authorized by the target Topolo service.
|
|
136
|
+
|
|
137
|
+
Only messages raised by TopoloMCP's own input validators are returned verbatim.
|
|
138
|
+
Authentication, SDK, provider, session-construction, and unexpected runtime
|
|
139
|
+
errors are replaced with stable non-sensitive messages before they reach MCP
|
|
140
|
+
responses or stderr. Target services remain responsible for retention, export,
|
|
141
|
+
deletion, encryption, and audit storage for the data they own.
|
|
142
|
+
|
|
129
143
|
## Tools
|
|
130
144
|
|
|
131
145
|
| Tool | Required scopes | Destructive hint |
|
package/dist/dispatch.js
CHANGED
|
@@ -50,6 +50,17 @@ function splitService(servicePart) {
|
|
|
50
50
|
return [servicePart.slice(0, idx), servicePart.slice(idx + 1)];
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
// src/errors.ts
|
|
54
|
+
var TopoloMcpPublicError = class extends Error {
|
|
55
|
+
constructor(message) {
|
|
56
|
+
super(message);
|
|
57
|
+
this.name = "TopoloMcpPublicError";
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
function publicMcpErrorMessage(error, fallback) {
|
|
61
|
+
return error instanceof TopoloMcpPublicError ? error.message : fallback;
|
|
62
|
+
}
|
|
63
|
+
|
|
53
64
|
// src/tools.ts
|
|
54
65
|
var APPLICATION_IDS = Object.keys(APPLICATIONS).sort();
|
|
55
66
|
var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
|
|
@@ -58,7 +69,7 @@ function optionalResourceContext(args) {
|
|
|
58
69
|
const resourceId = typeof args["resourceId"] === "string" ? args["resourceId"].trim() : "";
|
|
59
70
|
if (!resourceType && !resourceId) return void 0;
|
|
60
71
|
if (!resourceType || !resourceId) {
|
|
61
|
-
throw new
|
|
72
|
+
throw new TopoloMcpPublicError("resourceType and resourceId must be provided together.");
|
|
62
73
|
}
|
|
63
74
|
return { resourceType, resourceId };
|
|
64
75
|
}
|
|
@@ -140,7 +151,7 @@ var TOOLS = [
|
|
|
140
151
|
},
|
|
141
152
|
handler: async (topolo, args) => {
|
|
142
153
|
const service = typeof args["service"] === "string" ? args["service"].trim() : "";
|
|
143
|
-
if (!service) throw new
|
|
154
|
+
if (!service) throw new TopoloMcpPublicError("service is required for bounded action discovery.");
|
|
144
155
|
return topolo.client.searchActions({
|
|
145
156
|
service,
|
|
146
157
|
query: typeof args["query"] === "string" ? args["query"] : void 0,
|
|
@@ -169,7 +180,7 @@ var TOOLS = [
|
|
|
169
180
|
handler: async (topolo, args) => {
|
|
170
181
|
const application = args["application"];
|
|
171
182
|
if (typeof application !== "string" || !application.trim()) {
|
|
172
|
-
throw new
|
|
183
|
+
throw new TopoloMcpPublicError("`application` must be a non-empty string.");
|
|
173
184
|
}
|
|
174
185
|
return { application: await topolo.client.getService(application) };
|
|
175
186
|
}
|
|
@@ -188,7 +199,7 @@ var TOOLS = [
|
|
|
188
199
|
},
|
|
189
200
|
handler: async (topolo, args) => {
|
|
190
201
|
const actionId = typeof args["actionId"] === "string" ? args["actionId"].trim() : "";
|
|
191
|
-
if (!actionId) throw new
|
|
202
|
+
if (!actionId) throw new TopoloMcpPublicError("actionId is required.");
|
|
192
203
|
return { action: await topolo.client.getAction(actionId) };
|
|
193
204
|
}
|
|
194
205
|
},
|
|
@@ -211,10 +222,10 @@ var TOOLS = [
|
|
|
211
222
|
},
|
|
212
223
|
handler: async (topolo, args) => {
|
|
213
224
|
const actionId = typeof args["actionId"] === "string" ? args["actionId"].trim() : "";
|
|
214
|
-
if (!actionId) throw new
|
|
225
|
+
if (!actionId) throw new TopoloMcpPublicError("actionId is required.");
|
|
215
226
|
const input = args["input"];
|
|
216
227
|
if (input !== void 0 && (!input || typeof input !== "object" || Array.isArray(input))) {
|
|
217
|
-
throw new
|
|
228
|
+
throw new TopoloMcpPublicError("input must be an object.");
|
|
218
229
|
}
|
|
219
230
|
const resource = optionalResourceContext(args);
|
|
220
231
|
return topolo.client.readAction(
|
|
@@ -244,10 +255,10 @@ var TOOLS = [
|
|
|
244
255
|
},
|
|
245
256
|
handler: async (topolo, args) => {
|
|
246
257
|
const actionId = typeof args["actionId"] === "string" ? args["actionId"].trim() : "";
|
|
247
|
-
if (!actionId) throw new
|
|
258
|
+
if (!actionId) throw new TopoloMcpPublicError("actionId is required.");
|
|
248
259
|
const input = args["input"];
|
|
249
260
|
if (input !== void 0 && (!input || typeof input !== "object" || Array.isArray(input))) {
|
|
250
|
-
throw new
|
|
261
|
+
throw new TopoloMcpPublicError("input must be an object.");
|
|
251
262
|
}
|
|
252
263
|
const resource = optionalResourceContext(args);
|
|
253
264
|
return topolo.client.callAction(
|
|
@@ -277,10 +288,10 @@ var TOOLS = [
|
|
|
277
288
|
},
|
|
278
289
|
handler: async (topolo, args) => {
|
|
279
290
|
const actionId = typeof args["actionId"] === "string" ? args["actionId"].trim() : "";
|
|
280
|
-
if (!actionId) throw new
|
|
291
|
+
if (!actionId) throw new TopoloMcpPublicError("actionId is required.");
|
|
281
292
|
const input = args["input"];
|
|
282
293
|
if (input !== void 0 && (!input || typeof input !== "object" || Array.isArray(input))) {
|
|
283
|
-
throw new
|
|
294
|
+
throw new TopoloMcpPublicError("input must be an object.");
|
|
284
295
|
}
|
|
285
296
|
return topolo.client.validateActionInput(actionId, input ?? {});
|
|
286
297
|
}
|
|
@@ -305,10 +316,10 @@ var TOOLS = [
|
|
|
305
316
|
},
|
|
306
317
|
handler: async (topolo, args) => {
|
|
307
318
|
const actionId = typeof args["actionId"] === "string" ? args["actionId"].trim() : "";
|
|
308
|
-
if (!actionId) throw new
|
|
319
|
+
if (!actionId) throw new TopoloMcpPublicError("actionId is required.");
|
|
309
320
|
const input = args["input"];
|
|
310
321
|
if (input !== void 0 && (!input || typeof input !== "object" || Array.isArray(input))) {
|
|
311
|
-
throw new
|
|
322
|
+
throw new TopoloMcpPublicError("input must be an object.");
|
|
312
323
|
}
|
|
313
324
|
const resource = optionalResourceContext(args);
|
|
314
325
|
return topolo.client.planAction(
|
|
@@ -335,7 +346,7 @@ var TOOLS = [
|
|
|
335
346
|
},
|
|
336
347
|
handler: async (topolo, args) => {
|
|
337
348
|
const actionId = typeof args["actionId"] === "string" ? args["actionId"].trim() : "";
|
|
338
|
-
if (!actionId) throw new
|
|
349
|
+
if (!actionId) throw new TopoloMcpPublicError("actionId is required.");
|
|
339
350
|
const action = await topolo.client.getAction(actionId);
|
|
340
351
|
return {
|
|
341
352
|
actionId: action.actionId,
|
|
@@ -358,7 +369,7 @@ var TOOLS = [
|
|
|
358
369
|
},
|
|
359
370
|
handler: async (topolo, args) => {
|
|
360
371
|
const service = typeof args["service"] === "string" ? args["service"].trim() : "";
|
|
361
|
-
if (!service) throw new
|
|
372
|
+
if (!service) throw new TopoloMcpPublicError("service is required.");
|
|
362
373
|
return topolo.client.searchActions({ service, query: "capabilities", limit: 100 });
|
|
363
374
|
}
|
|
364
375
|
},
|
|
@@ -376,7 +387,7 @@ var TOOLS = [
|
|
|
376
387
|
},
|
|
377
388
|
handler: async (topolo, args) => {
|
|
378
389
|
const application = typeof args["application"] === "string" ? args["application"].trim() : "";
|
|
379
|
-
if (!application) throw new
|
|
390
|
+
if (!application) throw new TopoloMcpPublicError("application is required.");
|
|
380
391
|
const entry = await topolo.client.getService(application);
|
|
381
392
|
return {
|
|
382
393
|
application: { appId: entry.appId, slug: entry.slug, name: entry.name },
|
|
@@ -398,7 +409,7 @@ var TOOLS = [
|
|
|
398
409
|
},
|
|
399
410
|
handler: async (topolo, args) => {
|
|
400
411
|
const application = typeof args["application"] === "string" ? args["application"].trim() : "";
|
|
401
|
-
if (!application) throw new
|
|
412
|
+
if (!application) throw new TopoloMcpPublicError("application is required.");
|
|
402
413
|
return topolo.client.listBindableResources(application);
|
|
403
414
|
}
|
|
404
415
|
},
|
|
@@ -429,7 +440,7 @@ var TOOLS = [
|
|
|
429
440
|
};
|
|
430
441
|
}
|
|
431
442
|
if (typeof application !== "string" || !APPLICATION_IDS.includes(application)) {
|
|
432
|
-
throw new
|
|
443
|
+
throw new TopoloMcpPublicError(
|
|
433
444
|
`Unknown application "${String(application)}". ${formatKnownValues(APPLICATION_IDS, "applications")}`
|
|
434
445
|
);
|
|
435
446
|
}
|
|
@@ -467,7 +478,7 @@ var TOOLS = [
|
|
|
467
478
|
const application = args["application"];
|
|
468
479
|
const failOn = args["failOn"];
|
|
469
480
|
if (failOn !== void 0 && typeof failOn !== "string") {
|
|
470
|
-
throw new
|
|
481
|
+
throw new TopoloMcpPublicError("failOn must be one of: missing, needs_review, partial");
|
|
471
482
|
}
|
|
472
483
|
const gate = failOn ? normalizeFailOn(failOn) : null;
|
|
473
484
|
let report;
|
|
@@ -476,7 +487,7 @@ var TOOLS = [
|
|
|
476
487
|
return gate ? { ...report, conformanceGate: evaluateApplicationAuditGate(report, gate) } : report;
|
|
477
488
|
}
|
|
478
489
|
if (typeof application !== "string" || !APPLICATION_IDS.includes(application)) {
|
|
479
|
-
throw new
|
|
490
|
+
throw new TopoloMcpPublicError(
|
|
480
491
|
`Unknown application "${String(application)}". ${formatKnownValues(APPLICATION_IDS, "applications")}`
|
|
481
492
|
);
|
|
482
493
|
}
|
|
@@ -536,14 +547,14 @@ var TOOLS = [
|
|
|
536
547
|
const service = args["service"];
|
|
537
548
|
const path = args["path"];
|
|
538
549
|
if (typeof service !== "string" || !service.trim()) {
|
|
539
|
-
throw new
|
|
550
|
+
throw new TopoloMcpPublicError("service is required. Call topolo_search_applications first to discover callable services.");
|
|
540
551
|
}
|
|
541
552
|
if (typeof path !== "string" || !path) {
|
|
542
|
-
throw new
|
|
553
|
+
throw new TopoloMcpPublicError("path is required");
|
|
543
554
|
}
|
|
544
555
|
const methodRaw = args["method"] ?? "GET";
|
|
545
556
|
if (typeof methodRaw !== "string" || !HTTP_METHODS.includes(methodRaw)) {
|
|
546
|
-
throw new
|
|
557
|
+
throw new TopoloMcpPublicError(`Unknown HTTP method "${String(methodRaw)}"`);
|
|
547
558
|
}
|
|
548
559
|
const method = methodRaw;
|
|
549
560
|
const query = args["query"];
|
|
@@ -599,7 +610,7 @@ function normalizeFailOn(value) {
|
|
|
599
610
|
if (normalized === "missing" || normalized === "needs_review" || normalized === "partial") {
|
|
600
611
|
return normalized;
|
|
601
612
|
}
|
|
602
|
-
throw new
|
|
613
|
+
throw new TopoloMcpPublicError("failOn must be one of: missing, needs_review, partial");
|
|
603
614
|
}
|
|
604
615
|
function statusesAtOrWorse(threshold) {
|
|
605
616
|
if (threshold === "missing") return ["missing"];
|
|
@@ -694,14 +705,14 @@ async function dispatchTopoloTool(input) {
|
|
|
694
705
|
return {
|
|
695
706
|
ok: false,
|
|
696
707
|
reason: "auth",
|
|
697
|
-
error:
|
|
708
|
+
error: "Authentication failed.",
|
|
698
709
|
durationMs: Date.now() - started
|
|
699
710
|
};
|
|
700
711
|
}
|
|
701
712
|
return {
|
|
702
713
|
ok: false,
|
|
703
714
|
reason: "handler",
|
|
704
|
-
error: err
|
|
715
|
+
error: publicMcpErrorMessage(err, "Tool execution failed."),
|
|
705
716
|
durationMs: Date.now() - started
|
|
706
717
|
};
|
|
707
718
|
}
|
package/dist/http.js
CHANGED
|
@@ -14217,6 +14217,17 @@ function credentialFingerprint(credential) {
|
|
|
14217
14217
|
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
14218
14218
|
}
|
|
14219
14219
|
|
|
14220
|
+
// src/errors.ts
|
|
14221
|
+
var TopoloMcpPublicError = class extends Error {
|
|
14222
|
+
constructor(message) {
|
|
14223
|
+
super(message);
|
|
14224
|
+
this.name = "TopoloMcpPublicError";
|
|
14225
|
+
}
|
|
14226
|
+
};
|
|
14227
|
+
function publicMcpErrorMessage(error2, fallback) {
|
|
14228
|
+
return error2 instanceof TopoloMcpPublicError ? error2.message : fallback;
|
|
14229
|
+
}
|
|
14230
|
+
|
|
14220
14231
|
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.26.0_@cfworker+json-schema@4.1.1_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
|
|
14221
14232
|
function isZ4Schema(s) {
|
|
14222
14233
|
const schema = s;
|
|
@@ -15872,7 +15883,7 @@ function optionalResourceContext(args) {
|
|
|
15872
15883
|
const resourceId = typeof args["resourceId"] === "string" ? args["resourceId"].trim() : "";
|
|
15873
15884
|
if (!resourceType && !resourceId) return void 0;
|
|
15874
15885
|
if (!resourceType || !resourceId) {
|
|
15875
|
-
throw new
|
|
15886
|
+
throw new TopoloMcpPublicError("resourceType and resourceId must be provided together.");
|
|
15876
15887
|
}
|
|
15877
15888
|
return { resourceType, resourceId };
|
|
15878
15889
|
}
|
|
@@ -15954,7 +15965,7 @@ var TOOLS = [
|
|
|
15954
15965
|
},
|
|
15955
15966
|
handler: async (topolo, args) => {
|
|
15956
15967
|
const service = typeof args["service"] === "string" ? args["service"].trim() : "";
|
|
15957
|
-
if (!service) throw new
|
|
15968
|
+
if (!service) throw new TopoloMcpPublicError("service is required for bounded action discovery.");
|
|
15958
15969
|
return topolo.client.searchActions({
|
|
15959
15970
|
service,
|
|
15960
15971
|
query: typeof args["query"] === "string" ? args["query"] : void 0,
|
|
@@ -15983,7 +15994,7 @@ var TOOLS = [
|
|
|
15983
15994
|
handler: async (topolo, args) => {
|
|
15984
15995
|
const application = args["application"];
|
|
15985
15996
|
if (typeof application !== "string" || !application.trim()) {
|
|
15986
|
-
throw new
|
|
15997
|
+
throw new TopoloMcpPublicError("`application` must be a non-empty string.");
|
|
15987
15998
|
}
|
|
15988
15999
|
return { application: await topolo.client.getService(application) };
|
|
15989
16000
|
}
|
|
@@ -16002,7 +16013,7 @@ var TOOLS = [
|
|
|
16002
16013
|
},
|
|
16003
16014
|
handler: async (topolo, args) => {
|
|
16004
16015
|
const actionId = typeof args["actionId"] === "string" ? args["actionId"].trim() : "";
|
|
16005
|
-
if (!actionId) throw new
|
|
16016
|
+
if (!actionId) throw new TopoloMcpPublicError("actionId is required.");
|
|
16006
16017
|
return { action: await topolo.client.getAction(actionId) };
|
|
16007
16018
|
}
|
|
16008
16019
|
},
|
|
@@ -16025,10 +16036,10 @@ var TOOLS = [
|
|
|
16025
16036
|
},
|
|
16026
16037
|
handler: async (topolo, args) => {
|
|
16027
16038
|
const actionId = typeof args["actionId"] === "string" ? args["actionId"].trim() : "";
|
|
16028
|
-
if (!actionId) throw new
|
|
16039
|
+
if (!actionId) throw new TopoloMcpPublicError("actionId is required.");
|
|
16029
16040
|
const input = args["input"];
|
|
16030
16041
|
if (input !== void 0 && (!input || typeof input !== "object" || Array.isArray(input))) {
|
|
16031
|
-
throw new
|
|
16042
|
+
throw new TopoloMcpPublicError("input must be an object.");
|
|
16032
16043
|
}
|
|
16033
16044
|
const resource = optionalResourceContext(args);
|
|
16034
16045
|
return topolo.client.readAction(
|
|
@@ -16058,10 +16069,10 @@ var TOOLS = [
|
|
|
16058
16069
|
},
|
|
16059
16070
|
handler: async (topolo, args) => {
|
|
16060
16071
|
const actionId = typeof args["actionId"] === "string" ? args["actionId"].trim() : "";
|
|
16061
|
-
if (!actionId) throw new
|
|
16072
|
+
if (!actionId) throw new TopoloMcpPublicError("actionId is required.");
|
|
16062
16073
|
const input = args["input"];
|
|
16063
16074
|
if (input !== void 0 && (!input || typeof input !== "object" || Array.isArray(input))) {
|
|
16064
|
-
throw new
|
|
16075
|
+
throw new TopoloMcpPublicError("input must be an object.");
|
|
16065
16076
|
}
|
|
16066
16077
|
const resource = optionalResourceContext(args);
|
|
16067
16078
|
return topolo.client.callAction(
|
|
@@ -16091,10 +16102,10 @@ var TOOLS = [
|
|
|
16091
16102
|
},
|
|
16092
16103
|
handler: async (topolo, args) => {
|
|
16093
16104
|
const actionId = typeof args["actionId"] === "string" ? args["actionId"].trim() : "";
|
|
16094
|
-
if (!actionId) throw new
|
|
16105
|
+
if (!actionId) throw new TopoloMcpPublicError("actionId is required.");
|
|
16095
16106
|
const input = args["input"];
|
|
16096
16107
|
if (input !== void 0 && (!input || typeof input !== "object" || Array.isArray(input))) {
|
|
16097
|
-
throw new
|
|
16108
|
+
throw new TopoloMcpPublicError("input must be an object.");
|
|
16098
16109
|
}
|
|
16099
16110
|
return topolo.client.validateActionInput(actionId, input ?? {});
|
|
16100
16111
|
}
|
|
@@ -16119,10 +16130,10 @@ var TOOLS = [
|
|
|
16119
16130
|
},
|
|
16120
16131
|
handler: async (topolo, args) => {
|
|
16121
16132
|
const actionId = typeof args["actionId"] === "string" ? args["actionId"].trim() : "";
|
|
16122
|
-
if (!actionId) throw new
|
|
16133
|
+
if (!actionId) throw new TopoloMcpPublicError("actionId is required.");
|
|
16123
16134
|
const input = args["input"];
|
|
16124
16135
|
if (input !== void 0 && (!input || typeof input !== "object" || Array.isArray(input))) {
|
|
16125
|
-
throw new
|
|
16136
|
+
throw new TopoloMcpPublicError("input must be an object.");
|
|
16126
16137
|
}
|
|
16127
16138
|
const resource = optionalResourceContext(args);
|
|
16128
16139
|
return topolo.client.planAction(
|
|
@@ -16149,7 +16160,7 @@ var TOOLS = [
|
|
|
16149
16160
|
},
|
|
16150
16161
|
handler: async (topolo, args) => {
|
|
16151
16162
|
const actionId = typeof args["actionId"] === "string" ? args["actionId"].trim() : "";
|
|
16152
|
-
if (!actionId) throw new
|
|
16163
|
+
if (!actionId) throw new TopoloMcpPublicError("actionId is required.");
|
|
16153
16164
|
const action = await topolo.client.getAction(actionId);
|
|
16154
16165
|
return {
|
|
16155
16166
|
actionId: action.actionId,
|
|
@@ -16172,7 +16183,7 @@ var TOOLS = [
|
|
|
16172
16183
|
},
|
|
16173
16184
|
handler: async (topolo, args) => {
|
|
16174
16185
|
const service = typeof args["service"] === "string" ? args["service"].trim() : "";
|
|
16175
|
-
if (!service) throw new
|
|
16186
|
+
if (!service) throw new TopoloMcpPublicError("service is required.");
|
|
16176
16187
|
return topolo.client.searchActions({ service, query: "capabilities", limit: 100 });
|
|
16177
16188
|
}
|
|
16178
16189
|
},
|
|
@@ -16190,7 +16201,7 @@ var TOOLS = [
|
|
|
16190
16201
|
},
|
|
16191
16202
|
handler: async (topolo, args) => {
|
|
16192
16203
|
const application = typeof args["application"] === "string" ? args["application"].trim() : "";
|
|
16193
|
-
if (!application) throw new
|
|
16204
|
+
if (!application) throw new TopoloMcpPublicError("application is required.");
|
|
16194
16205
|
const entry = await topolo.client.getService(application);
|
|
16195
16206
|
return {
|
|
16196
16207
|
application: { appId: entry.appId, slug: entry.slug, name: entry.name },
|
|
@@ -16212,7 +16223,7 @@ var TOOLS = [
|
|
|
16212
16223
|
},
|
|
16213
16224
|
handler: async (topolo, args) => {
|
|
16214
16225
|
const application = typeof args["application"] === "string" ? args["application"].trim() : "";
|
|
16215
|
-
if (!application) throw new
|
|
16226
|
+
if (!application) throw new TopoloMcpPublicError("application is required.");
|
|
16216
16227
|
return topolo.client.listBindableResources(application);
|
|
16217
16228
|
}
|
|
16218
16229
|
},
|
|
@@ -16243,7 +16254,7 @@ var TOOLS = [
|
|
|
16243
16254
|
};
|
|
16244
16255
|
}
|
|
16245
16256
|
if (typeof application !== "string" || !APPLICATION_IDS.includes(application)) {
|
|
16246
|
-
throw new
|
|
16257
|
+
throw new TopoloMcpPublicError(
|
|
16247
16258
|
`Unknown application "${String(application)}". ${formatKnownValues(APPLICATION_IDS, "applications")}`
|
|
16248
16259
|
);
|
|
16249
16260
|
}
|
|
@@ -16281,7 +16292,7 @@ var TOOLS = [
|
|
|
16281
16292
|
const application = args["application"];
|
|
16282
16293
|
const failOn = args["failOn"];
|
|
16283
16294
|
if (failOn !== void 0 && typeof failOn !== "string") {
|
|
16284
|
-
throw new
|
|
16295
|
+
throw new TopoloMcpPublicError("failOn must be one of: missing, needs_review, partial");
|
|
16285
16296
|
}
|
|
16286
16297
|
const gate = failOn ? normalizeFailOn(failOn) : null;
|
|
16287
16298
|
let report;
|
|
@@ -16290,7 +16301,7 @@ var TOOLS = [
|
|
|
16290
16301
|
return gate ? { ...report, conformanceGate: evaluateApplicationAuditGate(report, gate) } : report;
|
|
16291
16302
|
}
|
|
16292
16303
|
if (typeof application !== "string" || !APPLICATION_IDS.includes(application)) {
|
|
16293
|
-
throw new
|
|
16304
|
+
throw new TopoloMcpPublicError(
|
|
16294
16305
|
`Unknown application "${String(application)}". ${formatKnownValues(APPLICATION_IDS, "applications")}`
|
|
16295
16306
|
);
|
|
16296
16307
|
}
|
|
@@ -16350,14 +16361,14 @@ var TOOLS = [
|
|
|
16350
16361
|
const service = args["service"];
|
|
16351
16362
|
const path = args["path"];
|
|
16352
16363
|
if (typeof service !== "string" || !service.trim()) {
|
|
16353
|
-
throw new
|
|
16364
|
+
throw new TopoloMcpPublicError("service is required. Call topolo_search_applications first to discover callable services.");
|
|
16354
16365
|
}
|
|
16355
16366
|
if (typeof path !== "string" || !path) {
|
|
16356
|
-
throw new
|
|
16367
|
+
throw new TopoloMcpPublicError("path is required");
|
|
16357
16368
|
}
|
|
16358
16369
|
const methodRaw = args["method"] ?? "GET";
|
|
16359
16370
|
if (typeof methodRaw !== "string" || !HTTP_METHODS.includes(methodRaw)) {
|
|
16360
|
-
throw new
|
|
16371
|
+
throw new TopoloMcpPublicError(`Unknown HTTP method "${String(methodRaw)}"`);
|
|
16361
16372
|
}
|
|
16362
16373
|
const method = methodRaw;
|
|
16363
16374
|
const query = args["query"];
|
|
@@ -16413,7 +16424,7 @@ function normalizeFailOn(value) {
|
|
|
16413
16424
|
if (normalized === "missing" || normalized === "needs_review" || normalized === "partial") {
|
|
16414
16425
|
return normalized;
|
|
16415
16426
|
}
|
|
16416
|
-
throw new
|
|
16427
|
+
throw new TopoloMcpPublicError("failOn must be one of: missing, needs_review, partial");
|
|
16417
16428
|
}
|
|
16418
16429
|
function statusesAtOrWorse(threshold) {
|
|
16419
16430
|
if (threshold === "missing") return ["missing"];
|
|
@@ -16517,9 +16528,9 @@ function buildServer(topolo, scopes, tools = TOOLS) {
|
|
|
16517
16528
|
return toolError(`Permission denied. Required: ${err.required.join(", ") || "(unknown)"}`);
|
|
16518
16529
|
}
|
|
16519
16530
|
if (err instanceof TopoloAuthError) {
|
|
16520
|
-
return toolError(
|
|
16531
|
+
return toolError("Authentication failed.");
|
|
16521
16532
|
}
|
|
16522
|
-
return toolError(err
|
|
16533
|
+
return toolError(publicMcpErrorMessage(err, "Tool execution failed."));
|
|
16523
16534
|
}
|
|
16524
16535
|
});
|
|
16525
16536
|
return server;
|
|
@@ -16622,11 +16633,11 @@ function createTopoloMcpHttpHandler(options = {}) {
|
|
|
16622
16633
|
session = await buildSessionForRequest(credential);
|
|
16623
16634
|
} catch (err) {
|
|
16624
16635
|
if (err instanceof TopoloAuthError2) {
|
|
16625
|
-
return jsonRpcError(401,
|
|
16636
|
+
return jsonRpcError(401, "Credential rejected.", -32001);
|
|
16626
16637
|
}
|
|
16627
16638
|
return jsonRpcError(
|
|
16628
16639
|
500,
|
|
16629
|
-
|
|
16640
|
+
publicMcpErrorMessage(err, "Failed to initialize MCP session.")
|
|
16630
16641
|
);
|
|
16631
16642
|
}
|
|
16632
16643
|
return session.transport.handleRequest(request);
|
package/dist/index.js
CHANGED
|
@@ -3899,6 +3899,17 @@ function splitService(servicePart) {
|
|
|
3899
3899
|
return [servicePart.slice(0, idx), servicePart.slice(idx + 1)];
|
|
3900
3900
|
}
|
|
3901
3901
|
|
|
3902
|
+
// src/errors.ts
|
|
3903
|
+
var TopoloMcpPublicError = class extends Error {
|
|
3904
|
+
constructor(message) {
|
|
3905
|
+
super(message);
|
|
3906
|
+
this.name = "TopoloMcpPublicError";
|
|
3907
|
+
}
|
|
3908
|
+
};
|
|
3909
|
+
function publicMcpErrorMessage(error, fallback) {
|
|
3910
|
+
return error instanceof TopoloMcpPublicError ? error.message : fallback;
|
|
3911
|
+
}
|
|
3912
|
+
|
|
3902
3913
|
// src/tools.ts
|
|
3903
3914
|
var APPLICATION_IDS = Object.keys(APPLICATIONS).sort();
|
|
3904
3915
|
var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
|
|
@@ -3907,7 +3918,7 @@ function optionalResourceContext(args) {
|
|
|
3907
3918
|
const resourceId = typeof args["resourceId"] === "string" ? args["resourceId"].trim() : "";
|
|
3908
3919
|
if (!resourceType && !resourceId) return void 0;
|
|
3909
3920
|
if (!resourceType || !resourceId) {
|
|
3910
|
-
throw new
|
|
3921
|
+
throw new TopoloMcpPublicError("resourceType and resourceId must be provided together.");
|
|
3911
3922
|
}
|
|
3912
3923
|
return { resourceType, resourceId };
|
|
3913
3924
|
}
|
|
@@ -3989,7 +4000,7 @@ var TOOLS = [
|
|
|
3989
4000
|
},
|
|
3990
4001
|
handler: async (topolo, args) => {
|
|
3991
4002
|
const service = typeof args["service"] === "string" ? args["service"].trim() : "";
|
|
3992
|
-
if (!service) throw new
|
|
4003
|
+
if (!service) throw new TopoloMcpPublicError("service is required for bounded action discovery.");
|
|
3993
4004
|
return topolo.client.searchActions({
|
|
3994
4005
|
service,
|
|
3995
4006
|
query: typeof args["query"] === "string" ? args["query"] : void 0,
|
|
@@ -4018,7 +4029,7 @@ var TOOLS = [
|
|
|
4018
4029
|
handler: async (topolo, args) => {
|
|
4019
4030
|
const application = args["application"];
|
|
4020
4031
|
if (typeof application !== "string" || !application.trim()) {
|
|
4021
|
-
throw new
|
|
4032
|
+
throw new TopoloMcpPublicError("`application` must be a non-empty string.");
|
|
4022
4033
|
}
|
|
4023
4034
|
return { application: await topolo.client.getService(application) };
|
|
4024
4035
|
}
|
|
@@ -4037,7 +4048,7 @@ var TOOLS = [
|
|
|
4037
4048
|
},
|
|
4038
4049
|
handler: async (topolo, args) => {
|
|
4039
4050
|
const actionId = typeof args["actionId"] === "string" ? args["actionId"].trim() : "";
|
|
4040
|
-
if (!actionId) throw new
|
|
4051
|
+
if (!actionId) throw new TopoloMcpPublicError("actionId is required.");
|
|
4041
4052
|
return { action: await topolo.client.getAction(actionId) };
|
|
4042
4053
|
}
|
|
4043
4054
|
},
|
|
@@ -4060,10 +4071,10 @@ var TOOLS = [
|
|
|
4060
4071
|
},
|
|
4061
4072
|
handler: async (topolo, args) => {
|
|
4062
4073
|
const actionId = typeof args["actionId"] === "string" ? args["actionId"].trim() : "";
|
|
4063
|
-
if (!actionId) throw new
|
|
4074
|
+
if (!actionId) throw new TopoloMcpPublicError("actionId is required.");
|
|
4064
4075
|
const input = args["input"];
|
|
4065
4076
|
if (input !== void 0 && (!input || typeof input !== "object" || Array.isArray(input))) {
|
|
4066
|
-
throw new
|
|
4077
|
+
throw new TopoloMcpPublicError("input must be an object.");
|
|
4067
4078
|
}
|
|
4068
4079
|
const resource = optionalResourceContext(args);
|
|
4069
4080
|
return topolo.client.readAction(
|
|
@@ -4093,10 +4104,10 @@ var TOOLS = [
|
|
|
4093
4104
|
},
|
|
4094
4105
|
handler: async (topolo, args) => {
|
|
4095
4106
|
const actionId = typeof args["actionId"] === "string" ? args["actionId"].trim() : "";
|
|
4096
|
-
if (!actionId) throw new
|
|
4107
|
+
if (!actionId) throw new TopoloMcpPublicError("actionId is required.");
|
|
4097
4108
|
const input = args["input"];
|
|
4098
4109
|
if (input !== void 0 && (!input || typeof input !== "object" || Array.isArray(input))) {
|
|
4099
|
-
throw new
|
|
4110
|
+
throw new TopoloMcpPublicError("input must be an object.");
|
|
4100
4111
|
}
|
|
4101
4112
|
const resource = optionalResourceContext(args);
|
|
4102
4113
|
return topolo.client.callAction(
|
|
@@ -4126,10 +4137,10 @@ var TOOLS = [
|
|
|
4126
4137
|
},
|
|
4127
4138
|
handler: async (topolo, args) => {
|
|
4128
4139
|
const actionId = typeof args["actionId"] === "string" ? args["actionId"].trim() : "";
|
|
4129
|
-
if (!actionId) throw new
|
|
4140
|
+
if (!actionId) throw new TopoloMcpPublicError("actionId is required.");
|
|
4130
4141
|
const input = args["input"];
|
|
4131
4142
|
if (input !== void 0 && (!input || typeof input !== "object" || Array.isArray(input))) {
|
|
4132
|
-
throw new
|
|
4143
|
+
throw new TopoloMcpPublicError("input must be an object.");
|
|
4133
4144
|
}
|
|
4134
4145
|
return topolo.client.validateActionInput(actionId, input ?? {});
|
|
4135
4146
|
}
|
|
@@ -4154,10 +4165,10 @@ var TOOLS = [
|
|
|
4154
4165
|
},
|
|
4155
4166
|
handler: async (topolo, args) => {
|
|
4156
4167
|
const actionId = typeof args["actionId"] === "string" ? args["actionId"].trim() : "";
|
|
4157
|
-
if (!actionId) throw new
|
|
4168
|
+
if (!actionId) throw new TopoloMcpPublicError("actionId is required.");
|
|
4158
4169
|
const input = args["input"];
|
|
4159
4170
|
if (input !== void 0 && (!input || typeof input !== "object" || Array.isArray(input))) {
|
|
4160
|
-
throw new
|
|
4171
|
+
throw new TopoloMcpPublicError("input must be an object.");
|
|
4161
4172
|
}
|
|
4162
4173
|
const resource = optionalResourceContext(args);
|
|
4163
4174
|
return topolo.client.planAction(
|
|
@@ -4184,7 +4195,7 @@ var TOOLS = [
|
|
|
4184
4195
|
},
|
|
4185
4196
|
handler: async (topolo, args) => {
|
|
4186
4197
|
const actionId = typeof args["actionId"] === "string" ? args["actionId"].trim() : "";
|
|
4187
|
-
if (!actionId) throw new
|
|
4198
|
+
if (!actionId) throw new TopoloMcpPublicError("actionId is required.");
|
|
4188
4199
|
const action = await topolo.client.getAction(actionId);
|
|
4189
4200
|
return {
|
|
4190
4201
|
actionId: action.actionId,
|
|
@@ -4207,7 +4218,7 @@ var TOOLS = [
|
|
|
4207
4218
|
},
|
|
4208
4219
|
handler: async (topolo, args) => {
|
|
4209
4220
|
const service = typeof args["service"] === "string" ? args["service"].trim() : "";
|
|
4210
|
-
if (!service) throw new
|
|
4221
|
+
if (!service) throw new TopoloMcpPublicError("service is required.");
|
|
4211
4222
|
return topolo.client.searchActions({ service, query: "capabilities", limit: 100 });
|
|
4212
4223
|
}
|
|
4213
4224
|
},
|
|
@@ -4225,7 +4236,7 @@ var TOOLS = [
|
|
|
4225
4236
|
},
|
|
4226
4237
|
handler: async (topolo, args) => {
|
|
4227
4238
|
const application = typeof args["application"] === "string" ? args["application"].trim() : "";
|
|
4228
|
-
if (!application) throw new
|
|
4239
|
+
if (!application) throw new TopoloMcpPublicError("application is required.");
|
|
4229
4240
|
const entry = await topolo.client.getService(application);
|
|
4230
4241
|
return {
|
|
4231
4242
|
application: { appId: entry.appId, slug: entry.slug, name: entry.name },
|
|
@@ -4247,7 +4258,7 @@ var TOOLS = [
|
|
|
4247
4258
|
},
|
|
4248
4259
|
handler: async (topolo, args) => {
|
|
4249
4260
|
const application = typeof args["application"] === "string" ? args["application"].trim() : "";
|
|
4250
|
-
if (!application) throw new
|
|
4261
|
+
if (!application) throw new TopoloMcpPublicError("application is required.");
|
|
4251
4262
|
return topolo.client.listBindableResources(application);
|
|
4252
4263
|
}
|
|
4253
4264
|
},
|
|
@@ -4278,7 +4289,7 @@ var TOOLS = [
|
|
|
4278
4289
|
};
|
|
4279
4290
|
}
|
|
4280
4291
|
if (typeof application !== "string" || !APPLICATION_IDS.includes(application)) {
|
|
4281
|
-
throw new
|
|
4292
|
+
throw new TopoloMcpPublicError(
|
|
4282
4293
|
`Unknown application "${String(application)}". ${formatKnownValues(APPLICATION_IDS, "applications")}`
|
|
4283
4294
|
);
|
|
4284
4295
|
}
|
|
@@ -4316,7 +4327,7 @@ var TOOLS = [
|
|
|
4316
4327
|
const application = args["application"];
|
|
4317
4328
|
const failOn = args["failOn"];
|
|
4318
4329
|
if (failOn !== void 0 && typeof failOn !== "string") {
|
|
4319
|
-
throw new
|
|
4330
|
+
throw new TopoloMcpPublicError("failOn must be one of: missing, needs_review, partial");
|
|
4320
4331
|
}
|
|
4321
4332
|
const gate = failOn ? normalizeFailOn(failOn) : null;
|
|
4322
4333
|
let report;
|
|
@@ -4325,7 +4336,7 @@ var TOOLS = [
|
|
|
4325
4336
|
return gate ? { ...report, conformanceGate: evaluateApplicationAuditGate(report, gate) } : report;
|
|
4326
4337
|
}
|
|
4327
4338
|
if (typeof application !== "string" || !APPLICATION_IDS.includes(application)) {
|
|
4328
|
-
throw new
|
|
4339
|
+
throw new TopoloMcpPublicError(
|
|
4329
4340
|
`Unknown application "${String(application)}". ${formatKnownValues(APPLICATION_IDS, "applications")}`
|
|
4330
4341
|
);
|
|
4331
4342
|
}
|
|
@@ -4385,14 +4396,14 @@ var TOOLS = [
|
|
|
4385
4396
|
const service = args["service"];
|
|
4386
4397
|
const path = args["path"];
|
|
4387
4398
|
if (typeof service !== "string" || !service.trim()) {
|
|
4388
|
-
throw new
|
|
4399
|
+
throw new TopoloMcpPublicError("service is required. Call topolo_search_applications first to discover callable services.");
|
|
4389
4400
|
}
|
|
4390
4401
|
if (typeof path !== "string" || !path) {
|
|
4391
|
-
throw new
|
|
4402
|
+
throw new TopoloMcpPublicError("path is required");
|
|
4392
4403
|
}
|
|
4393
4404
|
const methodRaw = args["method"] ?? "GET";
|
|
4394
4405
|
if (typeof methodRaw !== "string" || !HTTP_METHODS.includes(methodRaw)) {
|
|
4395
|
-
throw new
|
|
4406
|
+
throw new TopoloMcpPublicError(`Unknown HTTP method "${String(methodRaw)}"`);
|
|
4396
4407
|
}
|
|
4397
4408
|
const method = methodRaw;
|
|
4398
4409
|
const query = args["query"];
|
|
@@ -4448,7 +4459,7 @@ function normalizeFailOn(value) {
|
|
|
4448
4459
|
if (normalized === "missing" || normalized === "needs_review" || normalized === "partial") {
|
|
4449
4460
|
return normalized;
|
|
4450
4461
|
}
|
|
4451
|
-
throw new
|
|
4462
|
+
throw new TopoloMcpPublicError("failOn must be one of: missing, needs_review, partial");
|
|
4452
4463
|
}
|
|
4453
4464
|
function statusesAtOrWorse(threshold) {
|
|
4454
4465
|
if (threshold === "missing") return ["missing"];
|
|
@@ -4552,9 +4563,9 @@ function buildServer(topolo, scopes, tools = TOOLS) {
|
|
|
4552
4563
|
return toolError(`Permission denied. Required: ${err.required.join(", ") || "(unknown)"}`);
|
|
4553
4564
|
}
|
|
4554
4565
|
if (err instanceof TopoloAuthError) {
|
|
4555
|
-
return toolError(
|
|
4566
|
+
return toolError("Authentication failed.");
|
|
4556
4567
|
}
|
|
4557
|
-
return toolError(err
|
|
4568
|
+
return toolError(publicMcpErrorMessage(err, "Tool execution failed."));
|
|
4558
4569
|
}
|
|
4559
4570
|
});
|
|
4560
4571
|
return server;
|
|
@@ -4588,7 +4599,7 @@ var DEFAULT_DEPENDENCIES = {
|
|
|
4588
4599
|
function resolvePinnedContextKey(profile, selector) {
|
|
4589
4600
|
const normalized = selector.trim().toLowerCase();
|
|
4590
4601
|
if (!normalized) {
|
|
4591
|
-
throw new
|
|
4602
|
+
throw new TopoloMcpPublicError("TOPOLO_CONTEXT must not be empty");
|
|
4592
4603
|
}
|
|
4593
4604
|
const contexts = Object.entries(profile.contexts ?? {});
|
|
4594
4605
|
const exact = contexts.find(([key]) => key.toLowerCase() === normalized);
|
|
@@ -4600,16 +4611,16 @@ function resolvePinnedContextKey(profile, selector) {
|
|
|
4600
4611
|
].some((value) => String(value || "").trim().toLowerCase() === normalized));
|
|
4601
4612
|
if (matches.length === 1) return matches[0][0];
|
|
4602
4613
|
if (matches.length > 1) {
|
|
4603
|
-
throw new
|
|
4614
|
+
throw new TopoloMcpPublicError(`TOPOLO_CONTEXT "${selector}" is ambiguous; use an exact context key`);
|
|
4604
4615
|
}
|
|
4605
|
-
throw new
|
|
4616
|
+
throw new TopoloMcpPublicError(`TOPOLO_CONTEXT "${selector}" is not signed in for this environment`);
|
|
4606
4617
|
}
|
|
4607
4618
|
async function resolveMcpCredential(environment, selector = process.env.TOPOLO_CONTEXT, dependencies = DEFAULT_DEPENDENCIES) {
|
|
4608
4619
|
const environmentCredential = dependencies.resolveCredentialFromEnv();
|
|
4609
4620
|
const requestedSelector = selector?.trim();
|
|
4610
4621
|
if (environmentCredential) {
|
|
4611
4622
|
if (requestedSelector) {
|
|
4612
|
-
throw new
|
|
4623
|
+
throw new TopoloMcpPublicError("TOPOLO_CONTEXT cannot be combined with an environment credential");
|
|
4613
4624
|
}
|
|
4614
4625
|
return { credential: environmentCredential, contextKey: null };
|
|
4615
4626
|
}
|
|
@@ -4690,15 +4701,14 @@ async function loadScopes(topolo) {
|
|
|
4690
4701
|
};
|
|
4691
4702
|
} catch (err) {
|
|
4692
4703
|
if (err instanceof TopoloAuthError) {
|
|
4693
|
-
process.stderr.write(
|
|
4694
|
-
`);
|
|
4704
|
+
process.stderr.write("TopoloMCP: credential rejected.\n");
|
|
4695
4705
|
process.exit(1);
|
|
4696
4706
|
}
|
|
4697
4707
|
throw err;
|
|
4698
4708
|
}
|
|
4699
4709
|
}
|
|
4700
4710
|
main().catch((err) => {
|
|
4701
|
-
process.stderr.write(`TopoloMCP fatal: ${err
|
|
4711
|
+
process.stderr.write(`TopoloMCP fatal: ${publicMcpErrorMessage(err, "Unexpected startup failure.")}
|
|
4702
4712
|
`);
|
|
4703
4713
|
process.exit(1);
|
|
4704
4714
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@topolo/mcp",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.5",
|
|
4
4
|
"description": "Model Context Protocol server for the Topolo platform. Exposes scope-gated tools that third-party agents (Claude, Codex, etc.) can call natively.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
|
-
"@topolo/sdk": "^0.9.
|
|
39
|
+
"@topolo/sdk": "^0.9.5"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@topolo/sdk": "file:../topolo-sdk",
|