@topolo/mcp 0.4.8 → 0.4.10
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 +11 -5
- package/dist/http.d.ts +63 -0
- package/dist/http.js +16462 -0
- package/dist/index.js +49 -21
- package/package.json +17 -13
package/dist/index.js
CHANGED
|
@@ -86,7 +86,7 @@ var SERVICES = {
|
|
|
86
86
|
mail: "https://mail.topolo.app",
|
|
87
87
|
mdm: "https://mdm.topolo.app",
|
|
88
88
|
messages: "https://messages.topolo.app",
|
|
89
|
-
nexus: "https://nexus
|
|
89
|
+
nexus: "https://nexus.topolo.app",
|
|
90
90
|
notify: "https://notify.topolo.app",
|
|
91
91
|
observability_watch: "https://observability-watch.topolo.app",
|
|
92
92
|
one: "https://www.topolo.app",
|
|
@@ -635,8 +635,10 @@ var APPLICATIONS = {
|
|
|
635
635
|
{
|
|
636
636
|
"name": "topolo-feed-api",
|
|
637
637
|
"kind": "worker",
|
|
638
|
-
"apiUrl":
|
|
639
|
-
"domains": [
|
|
638
|
+
"apiUrl": "https://feed-api.topolo.app",
|
|
639
|
+
"domains": [
|
|
640
|
+
"feed-api.topolo.app"
|
|
641
|
+
],
|
|
640
642
|
"routes": [],
|
|
641
643
|
"serviceId": null
|
|
642
644
|
},
|
|
@@ -1007,9 +1009,9 @@ var APPLICATIONS = {
|
|
|
1007
1009
|
{
|
|
1008
1010
|
"name": "topolo-nexus-gateway",
|
|
1009
1011
|
"kind": "worker",
|
|
1010
|
-
"apiUrl": "https://nexus
|
|
1012
|
+
"apiUrl": "https://nexus.topolo.app",
|
|
1011
1013
|
"domains": [
|
|
1012
|
-
"nexus
|
|
1014
|
+
"nexus.topolo.app"
|
|
1013
1015
|
],
|
|
1014
1016
|
"routes": [],
|
|
1015
1017
|
"serviceId": "nexus"
|
|
@@ -1369,8 +1371,10 @@ var APPLICATIONS = {
|
|
|
1369
1371
|
{
|
|
1370
1372
|
"name": "topolo-socialize-api",
|
|
1371
1373
|
"kind": "worker",
|
|
1372
|
-
"apiUrl":
|
|
1373
|
-
"domains": [
|
|
1374
|
+
"apiUrl": "https://socialize-api.topolo.app",
|
|
1375
|
+
"domains": [
|
|
1376
|
+
"socialize-api.topolo.app"
|
|
1377
|
+
],
|
|
1374
1378
|
"routes": [],
|
|
1375
1379
|
"serviceId": null
|
|
1376
1380
|
},
|
|
@@ -2214,10 +2218,14 @@ var TopoloClient = class _TopoloClient {
|
|
|
2214
2218
|
if (!serviceKey) {
|
|
2215
2219
|
throw new TopoloSdkError("unknown_service", "service is required");
|
|
2216
2220
|
}
|
|
2217
|
-
if (serviceKey === "auth") {
|
|
2221
|
+
if (serviceKey === "auth" || serviceKey === "developers") {
|
|
2218
2222
|
const overrideUrl = resolveConfiguredServiceUrl(serviceKey, this.serviceUrls);
|
|
2219
2223
|
if (overrideUrl) return { serviceKey, baseUrl: overrideUrl };
|
|
2220
|
-
|
|
2224
|
+
if (serviceKey === "developers") {
|
|
2225
|
+
const derived = deriveDevelopersFromAuth(resolveConfiguredServiceUrl("auth", this.serviceUrls));
|
|
2226
|
+
if (derived) return { serviceKey, baseUrl: derived };
|
|
2227
|
+
}
|
|
2228
|
+
return { serviceKey, baseUrl: DEFAULT_SERVICE_URLS[serviceKey] };
|
|
2221
2229
|
}
|
|
2222
2230
|
const live = await this.resolveLiveCallableService(serviceKey);
|
|
2223
2231
|
if (live) {
|
|
@@ -2257,7 +2265,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
2257
2265
|
if (!normalized) return null;
|
|
2258
2266
|
const configuredServiceId = resolveConfiguredServiceId(normalized, this.serviceIds);
|
|
2259
2267
|
if (configuredServiceId) return configuredServiceId;
|
|
2260
|
-
const authBase = resolveServiceUrl("
|
|
2268
|
+
const authBase = resolveServiceUrl("developers", this.serviceUrls);
|
|
2261
2269
|
const cacheKey = `${authBase}|${normalized}`;
|
|
2262
2270
|
const now = Date.now();
|
|
2263
2271
|
const cached = _TopoloClient.serviceIdCache.get(cacheKey);
|
|
@@ -2311,7 +2319,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
2311
2319
|
if (this.serviceCatalogCache && this.serviceCatalogCache.expiresAt > now) {
|
|
2312
2320
|
return this.serviceCatalogCache.entries;
|
|
2313
2321
|
}
|
|
2314
|
-
const res = await this.request({ service: "
|
|
2322
|
+
const res = await this.request({ service: "developers", method: "GET", path: "/api/services/catalog" });
|
|
2315
2323
|
const raw = res.data?.services ?? res.services;
|
|
2316
2324
|
if (!Array.isArray(raw)) {
|
|
2317
2325
|
throw new TopoloSdkError(
|
|
@@ -2352,8 +2360,9 @@ var TopoloClient = class _TopoloClient {
|
|
|
2352
2360
|
return entries;
|
|
2353
2361
|
}
|
|
2354
2362
|
/**
|
|
2355
|
-
* List callable actions from the live
|
|
2356
|
-
* the
|
|
2363
|
+
* List callable actions from the live registry-canonical catalog (definitions
|
|
2364
|
+
* from the Catalog registry, scoping from Auth). The response is scoped to the
|
|
2365
|
+
* caller's organization and effective permissions; no bundled/static app
|
|
2357
2366
|
* action list is used as an authorization source.
|
|
2358
2367
|
*/
|
|
2359
2368
|
async listActions(options = {}) {
|
|
@@ -2362,7 +2371,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
2362
2371
|
return this.actionCatalogCache.entries;
|
|
2363
2372
|
}
|
|
2364
2373
|
const res = await this.request({
|
|
2365
|
-
service: "
|
|
2374
|
+
service: "developers",
|
|
2366
2375
|
method: "GET",
|
|
2367
2376
|
path: "/api/actions/catalog",
|
|
2368
2377
|
...options.service ? { query: { service: options.service } } : {}
|
|
@@ -2542,6 +2551,17 @@ function serviceKeyCandidates2(slug) {
|
|
|
2542
2551
|
function normalizeLookupKey(value) {
|
|
2543
2552
|
return String(value || "").trim().toLowerCase().replace(/\s+/g, "-");
|
|
2544
2553
|
}
|
|
2554
|
+
function deriveDevelopersFromAuth(authUrl) {
|
|
2555
|
+
if (!authUrl) return null;
|
|
2556
|
+
try {
|
|
2557
|
+
const url = new URL(authUrl);
|
|
2558
|
+
if (!/^auth\./i.test(url.hostname)) return null;
|
|
2559
|
+
url.hostname = url.hostname.replace(/^auth\./i, "developers.");
|
|
2560
|
+
return url.origin;
|
|
2561
|
+
} catch {
|
|
2562
|
+
return null;
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2545
2565
|
function catalogEntryMatches(entry, needle) {
|
|
2546
2566
|
return catalogEntryLookupKeys(entry).includes(needle);
|
|
2547
2567
|
}
|
|
@@ -2586,6 +2606,7 @@ function normalizeActionCatalogEntry(item) {
|
|
|
2586
2606
|
readOnly: Boolean(a.read_only ?? a.readOnly),
|
|
2587
2607
|
destructive: Boolean(a.destructive),
|
|
2588
2608
|
requiresConfirmation: Boolean(a.requires_confirmation ?? a.requiresConfirmation),
|
|
2609
|
+
agentAccess: pickAgentAccess(a.agent_access ?? a.agentAccess),
|
|
2589
2610
|
status: pickActionString(a.status),
|
|
2590
2611
|
sourceRevision: pickActionString(a.source_revision) ?? pickActionString(a.sourceRevision)
|
|
2591
2612
|
};
|
|
@@ -2596,6 +2617,9 @@ function isHttpMethod(value) {
|
|
|
2596
2617
|
function pickActionString(value) {
|
|
2597
2618
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
2598
2619
|
}
|
|
2620
|
+
function pickAgentAccess(value) {
|
|
2621
|
+
return value === "auto" || value === "confirm" || value === "off" ? value : null;
|
|
2622
|
+
}
|
|
2599
2623
|
function plainObject(value) {
|
|
2600
2624
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
2601
2625
|
}
|
|
@@ -2687,7 +2711,6 @@ var CrmModule = class {
|
|
|
2687
2711
|
constructor(client) {
|
|
2688
2712
|
this.client = client;
|
|
2689
2713
|
}
|
|
2690
|
-
client;
|
|
2691
2714
|
async listContacts(options = {}) {
|
|
2692
2715
|
const res = await this.client.request({
|
|
2693
2716
|
service: "crm",
|
|
@@ -2733,7 +2756,6 @@ var IdentityModule = class {
|
|
|
2733
2756
|
constructor(client) {
|
|
2734
2757
|
this.client = client;
|
|
2735
2758
|
}
|
|
2736
|
-
client;
|
|
2737
2759
|
/** Returns the resolved user, organization, and permission set for the
|
|
2738
2760
|
* current credential. Equivalent to `TopoloClient.introspect()` — exposed
|
|
2739
2761
|
* on this module for symmetry with the other domain modules. */
|
|
@@ -3109,14 +3131,20 @@ async function buildTopoloTools(topolo, scopes, tools = TOOLS) {
|
|
|
3109
3131
|
const actionTools = actionsToToolDefs(actions);
|
|
3110
3132
|
return filterToolsByScopes([...baseTools, ...actionTools], scopes);
|
|
3111
3133
|
}
|
|
3134
|
+
function effectiveAgentAccess(a) {
|
|
3135
|
+
if (a.agentAccess) return a.agentAccess;
|
|
3136
|
+
if (a.destructive) return "off";
|
|
3137
|
+
if (a.readOnly && !a.requiresConfirmation) return "auto";
|
|
3138
|
+
return "confirm";
|
|
3139
|
+
}
|
|
3112
3140
|
function actionsToToolDefs(actions) {
|
|
3113
|
-
return actions.map((action) => ({
|
|
3141
|
+
return actions.map((action) => ({ action, access: effectiveAgentAccess(action) })).filter(({ access }) => access !== "off").map(({ action, access }) => ({
|
|
3114
3142
|
name: action.toolName || toolNameForAction(action),
|
|
3115
3143
|
title: action.title,
|
|
3116
3144
|
description: action.description,
|
|
3117
3145
|
requiredScopes: action.requiredPermission ? [action.requiredPermission] : [],
|
|
3118
|
-
destructive:
|
|
3119
|
-
inputSchema: actionInputSchema(action),
|
|
3146
|
+
destructive: access === "confirm",
|
|
3147
|
+
inputSchema: actionInputSchema(action, access),
|
|
3120
3148
|
handler: async (topolo, args) => {
|
|
3121
3149
|
const confirm = Boolean(args["confirm"]);
|
|
3122
3150
|
const input = { ...args };
|
|
@@ -3130,7 +3158,7 @@ function toolNameForAction(action) {
|
|
|
3130
3158
|
const name = action.name.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
3131
3159
|
return ["topolo", service, name].filter(Boolean).join("_").slice(0, 96);
|
|
3132
3160
|
}
|
|
3133
|
-
function actionInputSchema(action) {
|
|
3161
|
+
function actionInputSchema(action, access = "auto") {
|
|
3134
3162
|
const schema = {
|
|
3135
3163
|
type: "object",
|
|
3136
3164
|
properties: {},
|
|
@@ -3138,7 +3166,7 @@ function actionInputSchema(action) {
|
|
|
3138
3166
|
...action.inputSchema
|
|
3139
3167
|
};
|
|
3140
3168
|
const properties = schema.properties && typeof schema.properties === "object" && !Array.isArray(schema.properties) ? { ...schema.properties } : {};
|
|
3141
|
-
if (
|
|
3169
|
+
if (access === "confirm") {
|
|
3142
3170
|
properties.confirm = {
|
|
3143
3171
|
type: "boolean",
|
|
3144
3172
|
description: "Required for mutating actions. Explicit human-in-the-loop acknowledgement."
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@topolo/mcp",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.10",
|
|
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": {
|
|
@@ -10,32 +10,36 @@
|
|
|
10
10
|
"./dispatch": {
|
|
11
11
|
"types": "./dist/dispatch.d.ts",
|
|
12
12
|
"import": "./dist/dispatch.js"
|
|
13
|
+
},
|
|
14
|
+
"./http": {
|
|
15
|
+
"types": "./dist/http.d.ts",
|
|
16
|
+
"import": "./dist/http.js"
|
|
13
17
|
}
|
|
14
18
|
},
|
|
15
19
|
"files": [
|
|
16
20
|
"dist"
|
|
17
21
|
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsup",
|
|
24
|
+
"dev": "tsup --watch",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"start": "node dist/index.js",
|
|
27
|
+
"test": "vitest run",
|
|
28
|
+
"test:watch": "vitest",
|
|
29
|
+
"coverage": "vitest run --coverage"
|
|
30
|
+
},
|
|
18
31
|
"dependencies": {
|
|
19
32
|
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
20
33
|
},
|
|
21
34
|
"devDependencies": {
|
|
35
|
+
"@topolo/sdk": "workspace:*",
|
|
22
36
|
"@types/node": "^20.12.0",
|
|
23
37
|
"@vitest/coverage-v8": "^2.1.0",
|
|
24
38
|
"tsup": "^8.0.0",
|
|
25
39
|
"typescript": "^5.4.0",
|
|
26
|
-
"vitest": "^2.1.0"
|
|
27
|
-
"@topolo/sdk": "0.4.8"
|
|
40
|
+
"vitest": "^2.1.0"
|
|
28
41
|
},
|
|
29
42
|
"engines": {
|
|
30
43
|
"node": ">=20"
|
|
31
|
-
},
|
|
32
|
-
"scripts": {
|
|
33
|
-
"build": "tsup",
|
|
34
|
-
"dev": "tsup --watch",
|
|
35
|
-
"typecheck": "tsc --noEmit",
|
|
36
|
-
"start": "node dist/index.js",
|
|
37
|
-
"test": "vitest run",
|
|
38
|
-
"test:watch": "vitest",
|
|
39
|
-
"coverage": "vitest run --coverage"
|
|
40
44
|
}
|
|
41
|
-
}
|
|
45
|
+
}
|