@topolo/mcp 0.3.0 → 0.4.0
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/dispatch.js +18 -10
- package/dist/index.js +73 -13
- package/package.json +4 -2
package/dist/dispatch.js
CHANGED
|
@@ -91,7 +91,7 @@ var TOOLS = [
|
|
|
91
91
|
{
|
|
92
92
|
name: "topolo_list_applications",
|
|
93
93
|
title: "List every Topolo platform application",
|
|
94
|
-
description: "Returns the
|
|
94
|
+
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
95
|
requiredScopes: [],
|
|
96
96
|
destructive: false,
|
|
97
97
|
inputSchema: {
|
|
@@ -99,14 +99,14 @@ var TOOLS = [
|
|
|
99
99
|
properties: {},
|
|
100
100
|
additionalProperties: false
|
|
101
101
|
},
|
|
102
|
-
handler: async () => ({
|
|
103
|
-
applications:
|
|
102
|
+
handler: async (topolo) => ({
|
|
103
|
+
applications: await topolo.client.listServices()
|
|
104
104
|
})
|
|
105
105
|
},
|
|
106
106
|
{
|
|
107
107
|
name: "topolo_get_application",
|
|
108
108
|
title: "Describe one Topolo platform application",
|
|
109
|
-
description: "Returns one application catalog entry
|
|
109
|
+
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
110
|
requiredScopes: [],
|
|
111
111
|
destructive: false,
|
|
112
112
|
inputSchema: {
|
|
@@ -114,21 +114,29 @@ var TOOLS = [
|
|
|
114
114
|
properties: {
|
|
115
115
|
application: {
|
|
116
116
|
type: "string",
|
|
117
|
-
|
|
118
|
-
description: "Application ID from topolo_list_applications."
|
|
117
|
+
description: "Application identifier: service id, slug, or name from topolo_list_applications."
|
|
119
118
|
}
|
|
120
119
|
},
|
|
121
120
|
required: ["application"],
|
|
122
121
|
additionalProperties: false
|
|
123
122
|
},
|
|
124
|
-
handler: async (
|
|
123
|
+
handler: async (topolo, args) => {
|
|
125
124
|
const application = args["application"];
|
|
126
|
-
if (typeof application !== "string" || !
|
|
125
|
+
if (typeof application !== "string" || !application.trim()) {
|
|
126
|
+
throw new Error("`application` must be a non-empty string.");
|
|
127
|
+
}
|
|
128
|
+
const needle = application.trim().toLowerCase();
|
|
129
|
+
const catalog = await topolo.client.listServices();
|
|
130
|
+
const match = catalog.find(
|
|
131
|
+
(e) => e.serviceId.toLowerCase() === needle || e.slug && e.slug.toLowerCase() === needle || e.name && e.name.toLowerCase() === needle
|
|
132
|
+
);
|
|
133
|
+
if (!match) {
|
|
134
|
+
const known = catalog.map((e) => e.slug || e.serviceId).sort().join(", ");
|
|
127
135
|
throw new Error(
|
|
128
|
-
`Unknown application "${
|
|
136
|
+
`Unknown application "${application}". Known: ${known}`
|
|
129
137
|
);
|
|
130
138
|
}
|
|
131
|
-
return { application:
|
|
139
|
+
return { application: match };
|
|
132
140
|
}
|
|
133
141
|
},
|
|
134
142
|
{
|
package/dist/index.js
CHANGED
|
@@ -1516,12 +1516,12 @@ var APPLICATION_REQUIREMENTS = [
|
|
|
1516
1516
|
summary: "Applications must derive identity, organization, scopes, and service entitlement from TopoloAuth rather than inventing parallel auth state.",
|
|
1517
1517
|
appliesTo: ["browser", "api", "tooling", "agent_surface"],
|
|
1518
1518
|
evidence: [
|
|
1519
|
-
"@topolo/auth-client or @topolo/
|
|
1519
|
+
"@topolo/auth-client or @topolo-io/worker-runtime is used where applicable",
|
|
1520
1520
|
"No public command, tool, or SDK method accepts orgId",
|
|
1521
1521
|
"Service slug and Auth service registration are documented for protected services"
|
|
1522
1522
|
],
|
|
1523
1523
|
implementation: [
|
|
1524
|
-
"Use shared auth packages instead of copying token parsing or browser storage flows",
|
|
1524
|
+
"Use shared auth/runtime packages instead of copying token parsing, gateway, or browser storage flows",
|
|
1525
1525
|
"Derive organization from the credential on every request",
|
|
1526
1526
|
"Route OAuth, refresh, logout, and SSO paths according to the shared Auth contract"
|
|
1527
1527
|
]
|
|
@@ -2062,6 +2062,58 @@ var TopoloClient = class _TopoloClient {
|
|
|
2062
2062
|
});
|
|
2063
2063
|
return value;
|
|
2064
2064
|
}
|
|
2065
|
+
/**
|
|
2066
|
+
* List the platform service catalog from the live registry
|
|
2067
|
+
* (`GET /api/services/catalog` on TopoloAuth). Credential-aware: the
|
|
2068
|
+
* caller's credential is attached, so an authenticated caller in an
|
|
2069
|
+
* entitled org also receives the `developer_only` services its role
|
|
2070
|
+
* permits; an unauthenticated/agent caller receives `active` only.
|
|
2071
|
+
*
|
|
2072
|
+
* Resilient: on any network/HTTP failure this falls back to the
|
|
2073
|
+
* build-time generated `APPLICATIONS` snapshot so offline/degraded
|
|
2074
|
+
* callers still get a usable (if static) catalog. The live result is
|
|
2075
|
+
* authoritative; the snapshot is a floor, not a cap.
|
|
2076
|
+
*/
|
|
2077
|
+
async listServices() {
|
|
2078
|
+
try {
|
|
2079
|
+
const res = await this.request({ service: "auth", method: "GET", path: "/api/services/catalog" });
|
|
2080
|
+
const raw = res.data?.services ?? res.services;
|
|
2081
|
+
if (Array.isArray(raw) && raw.length > 0) {
|
|
2082
|
+
const entries = [];
|
|
2083
|
+
for (const item of raw) {
|
|
2084
|
+
const s = item;
|
|
2085
|
+
const serviceId = typeof s.service_id === "string" ? s.service_id : typeof s.id === "string" ? s.id : null;
|
|
2086
|
+
if (!serviceId) continue;
|
|
2087
|
+
entries.push({
|
|
2088
|
+
serviceId,
|
|
2089
|
+
slug: typeof s.app_slug === "string" ? s.app_slug : null,
|
|
2090
|
+
name: typeof s.name === "string" ? s.name : null,
|
|
2091
|
+
description: typeof s.description === "string" ? s.description : null,
|
|
2092
|
+
baseUrl: typeof s.base_url === "string" ? s.base_url : null,
|
|
2093
|
+
status: typeof s.status === "string" ? s.status : null,
|
|
2094
|
+
surfaceKind: typeof s.surface_kind === "string" ? s.surface_kind : null,
|
|
2095
|
+
launchable: s.launchable === true
|
|
2096
|
+
});
|
|
2097
|
+
}
|
|
2098
|
+
if (entries.length > 0) return entries;
|
|
2099
|
+
}
|
|
2100
|
+
} catch {
|
|
2101
|
+
}
|
|
2102
|
+
return this.generatedCatalogFallback();
|
|
2103
|
+
}
|
|
2104
|
+
generatedCatalogFallback() {
|
|
2105
|
+
const apps = Object.values(APPLICATIONS);
|
|
2106
|
+
return apps.map((app) => ({
|
|
2107
|
+
serviceId: app.id,
|
|
2108
|
+
slug: app.id,
|
|
2109
|
+
name: app.name ?? app.id,
|
|
2110
|
+
description: null,
|
|
2111
|
+
baseUrl: app.productionUrl ?? null,
|
|
2112
|
+
status: "active",
|
|
2113
|
+
surfaceKind: "application",
|
|
2114
|
+
launchable: Boolean(app.productionUrl)
|
|
2115
|
+
}));
|
|
2116
|
+
}
|
|
2065
2117
|
/**
|
|
2066
2118
|
* Introspect the current credential. Returns the caller's resolved identity,
|
|
2067
2119
|
* organization, and permission set. Used by CLI `whoami` and by MCP to gate
|
|
@@ -2322,7 +2374,7 @@ var TOOLS = [
|
|
|
2322
2374
|
{
|
|
2323
2375
|
name: "topolo_list_applications",
|
|
2324
2376
|
title: "List every Topolo platform application",
|
|
2325
|
-
description: "Returns the
|
|
2377
|
+
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.",
|
|
2326
2378
|
requiredScopes: [],
|
|
2327
2379
|
destructive: false,
|
|
2328
2380
|
inputSchema: {
|
|
@@ -2330,14 +2382,14 @@ var TOOLS = [
|
|
|
2330
2382
|
properties: {},
|
|
2331
2383
|
additionalProperties: false
|
|
2332
2384
|
},
|
|
2333
|
-
handler: async () => ({
|
|
2334
|
-
applications:
|
|
2385
|
+
handler: async (topolo) => ({
|
|
2386
|
+
applications: await topolo.client.listServices()
|
|
2335
2387
|
})
|
|
2336
2388
|
},
|
|
2337
2389
|
{
|
|
2338
2390
|
name: "topolo_get_application",
|
|
2339
2391
|
title: "Describe one Topolo platform application",
|
|
2340
|
-
description: "Returns one application catalog entry
|
|
2392
|
+
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.",
|
|
2341
2393
|
requiredScopes: [],
|
|
2342
2394
|
destructive: false,
|
|
2343
2395
|
inputSchema: {
|
|
@@ -2345,21 +2397,29 @@ var TOOLS = [
|
|
|
2345
2397
|
properties: {
|
|
2346
2398
|
application: {
|
|
2347
2399
|
type: "string",
|
|
2348
|
-
|
|
2349
|
-
description: "Application ID from topolo_list_applications."
|
|
2400
|
+
description: "Application identifier: service id, slug, or name from topolo_list_applications."
|
|
2350
2401
|
}
|
|
2351
2402
|
},
|
|
2352
2403
|
required: ["application"],
|
|
2353
2404
|
additionalProperties: false
|
|
2354
2405
|
},
|
|
2355
|
-
handler: async (
|
|
2406
|
+
handler: async (topolo, args) => {
|
|
2356
2407
|
const application = args["application"];
|
|
2357
|
-
if (typeof application !== "string" || !
|
|
2408
|
+
if (typeof application !== "string" || !application.trim()) {
|
|
2409
|
+
throw new Error("`application` must be a non-empty string.");
|
|
2410
|
+
}
|
|
2411
|
+
const needle = application.trim().toLowerCase();
|
|
2412
|
+
const catalog = await topolo.client.listServices();
|
|
2413
|
+
const match = catalog.find(
|
|
2414
|
+
(e) => e.serviceId.toLowerCase() === needle || e.slug && e.slug.toLowerCase() === needle || e.name && e.name.toLowerCase() === needle
|
|
2415
|
+
);
|
|
2416
|
+
if (!match) {
|
|
2417
|
+
const known = catalog.map((e) => e.slug || e.serviceId).sort().join(", ");
|
|
2358
2418
|
throw new Error(
|
|
2359
|
-
`Unknown application "${
|
|
2419
|
+
`Unknown application "${application}". Known: ${known}`
|
|
2360
2420
|
);
|
|
2361
2421
|
}
|
|
2362
|
-
return { application:
|
|
2422
|
+
return { application: match };
|
|
2363
2423
|
}
|
|
2364
2424
|
},
|
|
2365
2425
|
{
|
|
@@ -2604,7 +2664,7 @@ function statusesAtOrWorse(threshold) {
|
|
|
2604
2664
|
}
|
|
2605
2665
|
|
|
2606
2666
|
// src/version.ts
|
|
2607
|
-
var MCP_VERSION = "0.
|
|
2667
|
+
var MCP_VERSION = "0.4.0";
|
|
2608
2668
|
|
|
2609
2669
|
// src/server.ts
|
|
2610
2670
|
function buildServer(topolo, scopes, tools = TOOLS) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@topolo/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
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": {
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"typecheck": "tsc --noEmit",
|
|
22
22
|
"start": "node dist/index.js",
|
|
23
23
|
"test": "vitest run",
|
|
24
|
-
"test:watch": "vitest"
|
|
24
|
+
"test:watch": "vitest",
|
|
25
|
+
"coverage": "vitest run --coverage"
|
|
25
26
|
},
|
|
26
27
|
"dependencies": {
|
|
27
28
|
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
@@ -29,6 +30,7 @@
|
|
|
29
30
|
"devDependencies": {
|
|
30
31
|
"@topolo/sdk": "workspace:*",
|
|
31
32
|
"@types/node": "^20.12.0",
|
|
33
|
+
"@vitest/coverage-v8": "^2.1.0",
|
|
32
34
|
"tsup": "^8.0.0",
|
|
33
35
|
"typescript": "^5.4.0",
|
|
34
36
|
"vitest": "^2.1.0"
|