@topolo/mcp 0.11.10 → 0.11.12
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 +1 -1
- package/dist/http.js +2373 -1128
- package/dist/index.js +131 -9
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -80,6 +80,7 @@ var ERROR_CODE_PATTERN = /^[a-z][a-z0-9_]{0,63}$/;
|
|
|
80
80
|
var TOPOLO_REQUEST_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
81
81
|
var CLOUDFLARE_RAY_ID_PATTERN = /^[0-9a-f]{16}-[A-Z]{3}$/;
|
|
82
82
|
var PERMISSION_PATTERN = /^[a-z][a-z0-9_-]{0,63}(?:\.[a-z][a-z0-9_-]{0,63})*:(?:[a-z][a-z0-9_-]{0,63}|\*)$/;
|
|
83
|
+
var PUBLIC_FIELD_PATH_PATTERN = /^[$.a-z0-9_[\]-]{1,160}$/i;
|
|
83
84
|
var RETRY_AFTER_DATE_PATTERN = /^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), \d{2} (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{4} \d{2}:\d{2}:\d{2} GMT$/;
|
|
84
85
|
var PUBLIC_DETAIL_SOURCES = /* @__PURE__ */ new Set([
|
|
85
86
|
"api_key_introspection",
|
|
@@ -192,7 +193,11 @@ function extractTopoloServiceErrorDetails(body) {
|
|
|
192
193
|
if (candidate === void 0) candidate = nestedRecord.details ?? nestedRecord;
|
|
193
194
|
}
|
|
194
195
|
if (candidate === void 0) candidate = record;
|
|
195
|
-
if (
|
|
196
|
+
if (Array.isArray(candidate)) {
|
|
197
|
+
const paths = canonicalTopoloValidationPaths(candidate);
|
|
198
|
+
return paths.length ? { paths } : void 0;
|
|
199
|
+
}
|
|
200
|
+
if (!candidate || typeof candidate !== "object") return void 0;
|
|
196
201
|
const source = candidate;
|
|
197
202
|
const safe = {};
|
|
198
203
|
for (const [key, value] of Object.entries(source)) {
|
|
@@ -229,12 +234,42 @@ function extractTopoloServiceErrorDetails(body) {
|
|
|
229
234
|
continue;
|
|
230
235
|
}
|
|
231
236
|
if (["missingFields", "fields", "paths"].includes(key) && Array.isArray(value)) {
|
|
232
|
-
const paths = value.filter((item) => typeof item === "string" &&
|
|
237
|
+
const paths = value.filter((item) => typeof item === "string" && PUBLIC_FIELD_PATH_PATTERN.test(item));
|
|
233
238
|
if (paths.length) safe[key] = paths;
|
|
234
239
|
}
|
|
235
240
|
}
|
|
236
241
|
return Object.keys(safe).length ? safe : void 0;
|
|
237
242
|
}
|
|
243
|
+
function canonicalTopoloValidationPaths(issues) {
|
|
244
|
+
const paths = [];
|
|
245
|
+
for (const issue of issues) {
|
|
246
|
+
if (!issue || typeof issue !== "object" || Array.isArray(issue)) continue;
|
|
247
|
+
const record = issue;
|
|
248
|
+
const path = canonicalTopoloValidationPath(record.field) ?? canonicalTopoloValidationPath(record.path);
|
|
249
|
+
if (path && !paths.includes(path)) paths.push(path);
|
|
250
|
+
if (paths.length === 32) break;
|
|
251
|
+
}
|
|
252
|
+
return paths;
|
|
253
|
+
}
|
|
254
|
+
function canonicalTopoloValidationPath(value) {
|
|
255
|
+
if (typeof value === "string") {
|
|
256
|
+
const path2 = value.trim();
|
|
257
|
+
return PUBLIC_FIELD_PATH_PATTERN.test(path2) ? path2 : void 0;
|
|
258
|
+
}
|
|
259
|
+
if (!Array.isArray(value) || value.length === 0 || value.length > 32) return void 0;
|
|
260
|
+
const segments = value.map((segment) => {
|
|
261
|
+
if (typeof segment === "number" && Number.isSafeInteger(segment) && segment >= 0) {
|
|
262
|
+
return String(segment);
|
|
263
|
+
}
|
|
264
|
+
if (typeof segment === "string" && /^[a-z0-9_$-]{1,80}$/i.test(segment)) {
|
|
265
|
+
return segment;
|
|
266
|
+
}
|
|
267
|
+
return null;
|
|
268
|
+
});
|
|
269
|
+
if (segments.includes(null)) return void 0;
|
|
270
|
+
const path = segments.join(".");
|
|
271
|
+
return PUBLIC_FIELD_PATH_PATTERN.test(path) ? path : void 0;
|
|
272
|
+
}
|
|
238
273
|
function publicTopoloErrorMessage(error) {
|
|
239
274
|
const code = canonicalTopoloErrorCode(
|
|
240
275
|
typeof error === "string" ? error : error.code ?? extractTopoloServiceErrorCode(error.body)
|
|
@@ -1011,16 +1046,29 @@ var TopoloClient = class _TopoloClient {
|
|
|
1011
1046
|
clearTimeout(timeoutId);
|
|
1012
1047
|
}
|
|
1013
1048
|
};
|
|
1049
|
+
const fetchWithReadRetry = async () => {
|
|
1050
|
+
const maxAttempts = method === "GET" ? 2 : 1;
|
|
1051
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
1052
|
+
try {
|
|
1053
|
+
return await fetchOnce();
|
|
1054
|
+
} catch (error) {
|
|
1055
|
+
const transportFailure = !(error instanceof TopoloSdkError) && !opts.signal?.aborted;
|
|
1056
|
+
if (!transportFailure || attempt === maxAttempts) throw error;
|
|
1057
|
+
await new Promise((resolve2) => setTimeout(resolve2, 250));
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
throw new TopoloSdkError("network_error", "Topolo read retry loop exhausted.");
|
|
1061
|
+
};
|
|
1014
1062
|
let res;
|
|
1015
1063
|
try {
|
|
1016
1064
|
const attemptedCredential = this.credential;
|
|
1017
|
-
res = await
|
|
1065
|
+
res = await fetchWithReadRetry();
|
|
1018
1066
|
if (res.status === 401 && attemptedCredential.kind === "access_token" && this.refreshCredential) {
|
|
1019
1067
|
await res.body?.cancel().catch(() => void 0);
|
|
1020
1068
|
const refreshed = await this.refreshRejectedAccessToken(attemptedCredential.accessToken);
|
|
1021
1069
|
if (refreshed) {
|
|
1022
1070
|
applyAuthHeaders(headers, refreshed);
|
|
1023
|
-
res = await
|
|
1071
|
+
res = await fetchWithReadRetry();
|
|
1024
1072
|
}
|
|
1025
1073
|
}
|
|
1026
1074
|
} catch (err) {
|
|
@@ -1540,7 +1588,9 @@ var TopoloClient = class _TopoloClient {
|
|
|
1540
1588
|
};
|
|
1541
1589
|
}
|
|
1542
1590
|
planResolvedAction(entry, input, options) {
|
|
1543
|
-
const
|
|
1591
|
+
const inputValidation = validateTopoloActionInput(entry.inputSchema, input);
|
|
1592
|
+
const resourceIssue = validateActionResource(entry, options.resource);
|
|
1593
|
+
const validation = resourceIssue ? { valid: false, errors: [...inputValidation.errors, resourceIssue] } : inputValidation;
|
|
1544
1594
|
const agentAccess = entry.agentAccess ?? (entry.destructive ? "off" : entry.readOnly && !entry.requiresConfirmation ? "auto" : "confirm");
|
|
1545
1595
|
const confirmationRequired = agentAccess === "confirm" || entry.requiresConfirmation || entry.destructive;
|
|
1546
1596
|
let request = null;
|
|
@@ -1599,6 +1649,13 @@ var TopoloClient = class _TopoloClient {
|
|
|
1599
1649
|
`Topolo action "${entry.actionId}" input is invalid: ${validation.errors.map((issue) => `${issue.path} ${issue.message}`).join("; ")}`
|
|
1600
1650
|
);
|
|
1601
1651
|
}
|
|
1652
|
+
const resourceIssue = validateActionResource(entry, options.resource);
|
|
1653
|
+
if (resourceIssue) {
|
|
1654
|
+
throw new TopoloSdkError(
|
|
1655
|
+
"invalid_action_resource",
|
|
1656
|
+
`Topolo action "${entry.actionId}" resource context is invalid: ${resourceIssue.message}`
|
|
1657
|
+
);
|
|
1658
|
+
}
|
|
1602
1659
|
const mapped = mapActionInput(entry, input);
|
|
1603
1660
|
const service = entry.appSlug || entry.appId;
|
|
1604
1661
|
const result = await this.request({
|
|
@@ -1661,6 +1718,35 @@ var TopoloClient = class _TopoloClient {
|
|
|
1661
1718
|
};
|
|
1662
1719
|
}
|
|
1663
1720
|
};
|
|
1721
|
+
function validateActionResource(entry, resource) {
|
|
1722
|
+
const permissionName = entry.permissionName || entry.requiredPermission.split(".").at(-1) || "";
|
|
1723
|
+
const declaredContext = entry.agentContract?.resourceContext;
|
|
1724
|
+
const declaredKind = typeof declaredContext?.kind === "string" ? declaredContext.kind.trim() : "";
|
|
1725
|
+
const expectedType = declaredKind || (permissionName.startsWith("workspace:") ? "workspace" : "");
|
|
1726
|
+
if (!expectedType || declaredContext?.required === false && !permissionName.startsWith("workspace:")) return null;
|
|
1727
|
+
if (!resource) {
|
|
1728
|
+
return {
|
|
1729
|
+
path: "$.resource",
|
|
1730
|
+
code: "required",
|
|
1731
|
+
message: `Select resourceType "${expectedType}" with the application resource catalog before planning or calling this action.`
|
|
1732
|
+
};
|
|
1733
|
+
}
|
|
1734
|
+
if (resource.resourceType !== expectedType) {
|
|
1735
|
+
return {
|
|
1736
|
+
path: "$.resource.resourceType",
|
|
1737
|
+
code: "const",
|
|
1738
|
+
message: `This action requires resourceType "${expectedType}".`
|
|
1739
|
+
};
|
|
1740
|
+
}
|
|
1741
|
+
if (!resource.resourceId.trim()) {
|
|
1742
|
+
return {
|
|
1743
|
+
path: "$.resource.resourceId",
|
|
1744
|
+
code: "required",
|
|
1745
|
+
message: `This action requires a ${expectedType} resourceId.`
|
|
1746
|
+
};
|
|
1747
|
+
}
|
|
1748
|
+
return null;
|
|
1749
|
+
}
|
|
1664
1750
|
function resolveConfiguredAppId(slug, appIds) {
|
|
1665
1751
|
for (const serviceKey of serviceKeyCandidates2(slug)) {
|
|
1666
1752
|
const configured = appIds?.[serviceKey];
|
|
@@ -2028,7 +2114,8 @@ function compactActionCatalogEntry(action) {
|
|
|
2028
2114
|
agentAccess: action.agentAccess ?? (action.requiresConfirmation ? "confirm" : action.readOnly ? "auto" : "confirm"),
|
|
2029
2115
|
requiredPermission: action.requiredPermission,
|
|
2030
2116
|
requiredInput: required,
|
|
2031
|
-
resourceType: action.agentContract?.
|
|
2117
|
+
resourceType: action.agentContract?.resourceContext?.kind ?? (action.permissionName.startsWith("workspace:") ? "workspace" : null),
|
|
2118
|
+
resultResourceType: action.agentContract?.resource?.kind ?? null,
|
|
2032
2119
|
exampleInput: example.value,
|
|
2033
2120
|
exampleInputAvailable: example.available,
|
|
2034
2121
|
docsUrl: action.agentContract?.docsUrl ?? null
|
|
@@ -2376,6 +2463,7 @@ var ERROR_CODE_PATTERN2 = /^[a-z][a-z0-9_]{0,63}$/;
|
|
|
2376
2463
|
var TOPOLO_REQUEST_ID_PATTERN2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
2377
2464
|
var CLOUDFLARE_RAY_ID_PATTERN2 = /^[0-9a-f]{16}-[A-Z]{3}$/;
|
|
2378
2465
|
var PERMISSION_PATTERN2 = /^[a-z][a-z0-9_-]{0,63}(?:\.[a-z][a-z0-9_-]{0,63})*:(?:[a-z][a-z0-9_-]{0,63}|\*)$/;
|
|
2466
|
+
var PUBLIC_FIELD_PATH_PATTERN2 = /^[$.a-z0-9_[\]-]{1,160}$/i;
|
|
2379
2467
|
var RETRY_AFTER_DATE_PATTERN2 = /^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), \d{2} (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{4} \d{2}:\d{2}:\d{2} GMT$/;
|
|
2380
2468
|
var PUBLIC_DETAIL_SOURCES2 = /* @__PURE__ */ new Set([
|
|
2381
2469
|
"api_key_introspection",
|
|
@@ -2461,7 +2549,11 @@ function extractTopoloServiceErrorDetails2(body) {
|
|
|
2461
2549
|
if (candidate === void 0) candidate = nestedRecord.details ?? nestedRecord;
|
|
2462
2550
|
}
|
|
2463
2551
|
if (candidate === void 0) candidate = record;
|
|
2464
|
-
if (
|
|
2552
|
+
if (Array.isArray(candidate)) {
|
|
2553
|
+
const paths = canonicalTopoloValidationPaths2(candidate);
|
|
2554
|
+
return paths.length ? { paths } : void 0;
|
|
2555
|
+
}
|
|
2556
|
+
if (!candidate || typeof candidate !== "object") return void 0;
|
|
2465
2557
|
const source = candidate;
|
|
2466
2558
|
const safe = {};
|
|
2467
2559
|
for (const [key, value] of Object.entries(source)) {
|
|
@@ -2498,12 +2590,42 @@ function extractTopoloServiceErrorDetails2(body) {
|
|
|
2498
2590
|
continue;
|
|
2499
2591
|
}
|
|
2500
2592
|
if (["missingFields", "fields", "paths"].includes(key) && Array.isArray(value)) {
|
|
2501
|
-
const paths = value.filter((item) => typeof item === "string" &&
|
|
2593
|
+
const paths = value.filter((item) => typeof item === "string" && PUBLIC_FIELD_PATH_PATTERN2.test(item));
|
|
2502
2594
|
if (paths.length) safe[key] = paths;
|
|
2503
2595
|
}
|
|
2504
2596
|
}
|
|
2505
2597
|
return Object.keys(safe).length ? safe : void 0;
|
|
2506
2598
|
}
|
|
2599
|
+
function canonicalTopoloValidationPaths2(issues) {
|
|
2600
|
+
const paths = [];
|
|
2601
|
+
for (const issue of issues) {
|
|
2602
|
+
if (!issue || typeof issue !== "object" || Array.isArray(issue)) continue;
|
|
2603
|
+
const record = issue;
|
|
2604
|
+
const path = canonicalTopoloValidationPath2(record.field) ?? canonicalTopoloValidationPath2(record.path);
|
|
2605
|
+
if (path && !paths.includes(path)) paths.push(path);
|
|
2606
|
+
if (paths.length === 32) break;
|
|
2607
|
+
}
|
|
2608
|
+
return paths;
|
|
2609
|
+
}
|
|
2610
|
+
function canonicalTopoloValidationPath2(value) {
|
|
2611
|
+
if (typeof value === "string") {
|
|
2612
|
+
const path2 = value.trim();
|
|
2613
|
+
return PUBLIC_FIELD_PATH_PATTERN2.test(path2) ? path2 : void 0;
|
|
2614
|
+
}
|
|
2615
|
+
if (!Array.isArray(value) || value.length === 0 || value.length > 32) return void 0;
|
|
2616
|
+
const segments = value.map((segment) => {
|
|
2617
|
+
if (typeof segment === "number" && Number.isSafeInteger(segment) && segment >= 0) {
|
|
2618
|
+
return String(segment);
|
|
2619
|
+
}
|
|
2620
|
+
if (typeof segment === "string" && /^[a-z0-9_$-]{1,80}$/i.test(segment)) {
|
|
2621
|
+
return segment;
|
|
2622
|
+
}
|
|
2623
|
+
return null;
|
|
2624
|
+
});
|
|
2625
|
+
if (segments.includes(null)) return void 0;
|
|
2626
|
+
const path = segments.join(".");
|
|
2627
|
+
return PUBLIC_FIELD_PATH_PATTERN2.test(path) ? path : void 0;
|
|
2628
|
+
}
|
|
2507
2629
|
var TopoloHttpError2 = class extends TopoloSdkError2 {
|
|
2508
2630
|
status;
|
|
2509
2631
|
body;
|
|
@@ -3012,7 +3134,7 @@ function summarizeActionCapabilities(service, actions) {
|
|
|
3012
3134
|
mutating: actions.filter((action) => !action.readOnly).length,
|
|
3013
3135
|
destructive: actions.filter((action) => action.destructive).length,
|
|
3014
3136
|
confirmationRequired: actions.filter((action) => action.requiresConfirmation).length,
|
|
3015
|
-
resourceTypes: [...new Set(actions.map((action) => action.agentContract?.
|
|
3137
|
+
resourceTypes: [...new Set(actions.map((action) => action.agentContract?.resourceContext?.kind ?? (action.permissionName.startsWith("workspace:") ? "workspace" : null)).filter(Boolean))].sort()
|
|
3016
3138
|
};
|
|
3017
3139
|
}
|
|
3018
3140
|
function coerceObjectParam(value, name) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@topolo/mcp",
|
|
3
|
-
"version": "0.11.
|
|
4
|
-
"topoloPublishedVersion": "0.11.
|
|
3
|
+
"version": "0.11.12",
|
|
4
|
+
"topoloPublishedVersion": "0.11.12",
|
|
5
5
|
"description": "Model Context Protocol server for the Topolo platform. Exposes scope-gated tools that third-party agents (Claude, Codex, etc.) can call natively.",
|
|
6
6
|
"license": "UNLICENSED",
|
|
7
7
|
"author": "Topolo",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
38
|
-
"@topolo/sdk": "0.
|
|
38
|
+
"@topolo/sdk": "0.14.2"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/node": "^20.12.0",
|