@topolo/mcp 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -5
- package/dist/dispatch.js +30 -20
- package/dist/index.js +531 -210
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -112,7 +112,10 @@ clients surface that as a failed server launch.
|
|
|
112
112
|
`topolo_api_call` is the escape hatch for endpoints without typed MCP tools
|
|
113
113
|
yet. GET requests work without confirmation; mutating HTTP methods must pass
|
|
114
114
|
`confirm: true` or the SDK rejects the call. Backing services still enforce
|
|
115
|
-
permissions for every request.
|
|
115
|
+
permissions for every request. The tool schema intentionally does not embed a
|
|
116
|
+
large service enum; agents should call `topolo_list_services` first, then pass a
|
|
117
|
+
service ID/slug. The SDK can resolve live Auth-catalog API/technical service
|
|
118
|
+
surfaces for newly registered services before the next MCP package release.
|
|
116
119
|
|
|
117
120
|
## Safety rails
|
|
118
121
|
|
|
@@ -159,11 +162,12 @@ waiting for JSON-RPC on stdin. To drive it manually, use the MCP Inspector:
|
|
|
159
162
|
npx @modelcontextprotocol/inspector node dist/index.js
|
|
160
163
|
```
|
|
161
164
|
|
|
162
|
-
## Release
|
|
165
|
+
## Release Flow
|
|
163
166
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
`NPM_TOKEN` secret.
|
|
167
|
+
Preferred flow: push a `v<x.y.z>` tag to `main`; GitHub Actions typechecks,
|
|
168
|
+
tests, builds, verifies the tag matches `package.json`, and publishes to npm via
|
|
169
|
+
the `NPM_TOKEN` secret. Operators may publish directly with an npm token only
|
|
170
|
+
after running the same package checks and `npm pack --dry-run`.
|
|
167
171
|
|
|
168
172
|
## Phase 2 (planned)
|
|
169
173
|
|
package/dist/dispatch.js
CHANGED
|
@@ -52,7 +52,6 @@ function splitService(servicePart) {
|
|
|
52
52
|
|
|
53
53
|
// src/tools.ts
|
|
54
54
|
var APPLICATION_IDS = Object.keys(APPLICATIONS).sort();
|
|
55
|
-
var SERVICE_IDS = Object.keys(DEFAULT_SERVICE_URLS).sort();
|
|
56
55
|
var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
|
|
57
56
|
var TOOLS = [
|
|
58
57
|
{
|
|
@@ -91,7 +90,7 @@ var TOOLS = [
|
|
|
91
90
|
{
|
|
92
91
|
name: "topolo_list_applications",
|
|
93
92
|
title: "List every Topolo platform application",
|
|
94
|
-
description: "Returns the
|
|
93
|
+
description: "Returns the live Topolo application catalog from the platform registry. Reflects apps as they are registered/removed with no client rebuild. Authenticated callers in an entitled org also see the developer_only apps their role permits. Falls back to a bundled snapshot only if the registry is unreachable.",
|
|
95
94
|
requiredScopes: [],
|
|
96
95
|
destructive: false,
|
|
97
96
|
inputSchema: {
|
|
@@ -99,14 +98,14 @@ var TOOLS = [
|
|
|
99
98
|
properties: {},
|
|
100
99
|
additionalProperties: false
|
|
101
100
|
},
|
|
102
|
-
handler: async () => ({
|
|
103
|
-
applications:
|
|
101
|
+
handler: async (topolo) => ({
|
|
102
|
+
applications: await topolo.client.listServices()
|
|
104
103
|
})
|
|
105
104
|
},
|
|
106
105
|
{
|
|
107
106
|
name: "topolo_get_application",
|
|
108
107
|
title: "Describe one Topolo platform application",
|
|
109
|
-
description: "Returns one application catalog entry
|
|
108
|
+
description: "Returns one live application catalog entry (service id, slug, name, base URL, status). Accepts the service id, slug, or name. Resolved against the live registry so newly-registered apps work without a client rebuild.",
|
|
110
109
|
requiredScopes: [],
|
|
111
110
|
destructive: false,
|
|
112
111
|
inputSchema: {
|
|
@@ -114,21 +113,29 @@ var TOOLS = [
|
|
|
114
113
|
properties: {
|
|
115
114
|
application: {
|
|
116
115
|
type: "string",
|
|
117
|
-
|
|
118
|
-
description: "Application ID from topolo_list_applications."
|
|
116
|
+
description: "Application identifier: service id, slug, or name from topolo_list_applications."
|
|
119
117
|
}
|
|
120
118
|
},
|
|
121
119
|
required: ["application"],
|
|
122
120
|
additionalProperties: false
|
|
123
121
|
},
|
|
124
|
-
handler: async (
|
|
122
|
+
handler: async (topolo, args) => {
|
|
125
123
|
const application = args["application"];
|
|
126
|
-
if (typeof application !== "string" || !
|
|
124
|
+
if (typeof application !== "string" || !application.trim()) {
|
|
125
|
+
throw new Error("`application` must be a non-empty string.");
|
|
126
|
+
}
|
|
127
|
+
const needle = application.trim().toLowerCase();
|
|
128
|
+
const catalog = await topolo.client.listServices();
|
|
129
|
+
const match = catalog.find(
|
|
130
|
+
(e) => e.serviceId.toLowerCase() === needle || e.slug && e.slug.toLowerCase() === needle || e.name && e.name.toLowerCase() === needle
|
|
131
|
+
);
|
|
132
|
+
if (!match) {
|
|
133
|
+
const known = catalog.map((e) => e.slug || e.serviceId).sort().filter((value) => typeof value === "string" && value.length > 0);
|
|
127
134
|
throw new Error(
|
|
128
|
-
`Unknown application "${
|
|
135
|
+
`Unknown application "${application}". ${formatKnownValues(known, "applications")}`
|
|
129
136
|
);
|
|
130
137
|
}
|
|
131
|
-
return { application:
|
|
138
|
+
return { application: match };
|
|
132
139
|
}
|
|
133
140
|
},
|
|
134
141
|
{
|
|
@@ -142,7 +149,6 @@ var TOOLS = [
|
|
|
142
149
|
properties: {
|
|
143
150
|
application: {
|
|
144
151
|
type: "string",
|
|
145
|
-
enum: APPLICATION_IDS,
|
|
146
152
|
description: "Optional application ID from topolo_list_applications."
|
|
147
153
|
}
|
|
148
154
|
},
|
|
@@ -160,7 +166,7 @@ var TOOLS = [
|
|
|
160
166
|
}
|
|
161
167
|
if (typeof application !== "string" || !APPLICATION_IDS.includes(application)) {
|
|
162
168
|
throw new Error(
|
|
163
|
-
`Unknown application "${String(application)}".
|
|
169
|
+
`Unknown application "${String(application)}". ${formatKnownValues(APPLICATION_IDS, "applications")}`
|
|
164
170
|
);
|
|
165
171
|
}
|
|
166
172
|
const app = APPLICATIONS[application];
|
|
@@ -183,7 +189,6 @@ var TOOLS = [
|
|
|
183
189
|
properties: {
|
|
184
190
|
application: {
|
|
185
191
|
type: "string",
|
|
186
|
-
enum: APPLICATION_IDS,
|
|
187
192
|
description: "Optional application ID from topolo_list_applications."
|
|
188
193
|
},
|
|
189
194
|
failOn: {
|
|
@@ -208,7 +213,7 @@ var TOOLS = [
|
|
|
208
213
|
}
|
|
209
214
|
if (typeof application !== "string" || !APPLICATION_IDS.includes(application)) {
|
|
210
215
|
throw new Error(
|
|
211
|
-
`Unknown application "${String(application)}".
|
|
216
|
+
`Unknown application "${String(application)}". ${formatKnownValues(APPLICATION_IDS, "applications")}`
|
|
212
217
|
);
|
|
213
218
|
}
|
|
214
219
|
report = auditAllApplicationRequirements([application]);
|
|
@@ -241,7 +246,7 @@ var TOOLS = [
|
|
|
241
246
|
{
|
|
242
247
|
name: "topolo_api_call",
|
|
243
248
|
title: "Call any Topolo platform service",
|
|
244
|
-
description:
|
|
249
|
+
description: "Low-level passthrough to any Topolo service. Use when no domain-specific tool exists for the operation you need. The request is signed with the current credential and scoped to the caller's organization \u2014 the target service will return 403 if your scopes do not permit the operation, even though this tool is advertised. Mutating calls (non-GET methods) require confirm:true. Call topolo_list_services first to discover callable service IDs; the SDK can also resolve live Auth-catalog service ids/slugs for newly registered API surfaces.",
|
|
245
250
|
requiredScopes: [],
|
|
246
251
|
destructive: true,
|
|
247
252
|
inputSchema: {
|
|
@@ -249,7 +254,6 @@ var TOOLS = [
|
|
|
249
254
|
properties: {
|
|
250
255
|
service: {
|
|
251
256
|
type: "string",
|
|
252
|
-
enum: SERVICE_IDS,
|
|
253
257
|
description: "Target service ID from the platform registry."
|
|
254
258
|
},
|
|
255
259
|
method: {
|
|
@@ -282,8 +286,8 @@ var TOOLS = [
|
|
|
282
286
|
handler: async (topolo, args) => {
|
|
283
287
|
const service = args["service"];
|
|
284
288
|
const path = args["path"];
|
|
285
|
-
if (typeof service !== "string" || !
|
|
286
|
-
throw new Error(
|
|
289
|
+
if (typeof service !== "string" || !service.trim()) {
|
|
290
|
+
throw new Error("service is required. Call topolo_list_services first to discover callable services.");
|
|
287
291
|
}
|
|
288
292
|
if (typeof path !== "string" || !path) {
|
|
289
293
|
throw new Error("path is required");
|
|
@@ -298,7 +302,7 @@ var TOOLS = [
|
|
|
298
302
|
Object.entries(query).map(([k, v]) => [k, String(v)])
|
|
299
303
|
) : void 0;
|
|
300
304
|
return topolo.client.request({
|
|
301
|
-
service,
|
|
305
|
+
service: service.trim(),
|
|
302
306
|
method,
|
|
303
307
|
path,
|
|
304
308
|
body: args["body"],
|
|
@@ -371,6 +375,12 @@ function statusesAtOrWorse(threshold) {
|
|
|
371
375
|
if (threshold === "needs_review") return ["missing", "needs_review"];
|
|
372
376
|
return ["missing", "needs_review", "partial"];
|
|
373
377
|
}
|
|
378
|
+
function formatKnownValues(values, label) {
|
|
379
|
+
const sorted = [...values].sort();
|
|
380
|
+
const preview = sorted.slice(0, 25).join(", ");
|
|
381
|
+
const suffix = sorted.length > 25 ? `, ... +${sorted.length - 25} more` : "";
|
|
382
|
+
return `Run discovery for the full ${label} list. Known snapshot sample: ${preview}${suffix}`;
|
|
383
|
+
}
|
|
374
384
|
|
|
375
385
|
// src/dispatch.ts
|
|
376
386
|
function listAvailableTopoloTools(scopes, tools = TOOLS) {
|