@topolo/mcp 0.11.11 → 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/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 (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) return void 0;
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" && /^[$.a-z0-9_[\]-]{1,160}$/i.test(item));
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 fetchOnce();
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 fetchOnce();
1071
+ res = await fetchWithReadRetry();
1024
1072
  }
1025
1073
  }
1026
1074
  } catch (err) {
@@ -1672,26 +1720,29 @@ var TopoloClient = class _TopoloClient {
1672
1720
  };
1673
1721
  function validateActionResource(entry, resource) {
1674
1722
  const permissionName = entry.permissionName || entry.requiredPermission.split(".").at(-1) || "";
1675
- if (!permissionName.startsWith("workspace:")) return null;
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;
1676
1727
  if (!resource) {
1677
1728
  return {
1678
1729
  path: "$.resource",
1679
1730
  code: "required",
1680
- message: "Select a workspace with the application resource catalog before planning or calling this action."
1731
+ message: `Select resourceType "${expectedType}" with the application resource catalog before planning or calling this action.`
1681
1732
  };
1682
1733
  }
1683
- if (resource.resourceType !== "workspace") {
1734
+ if (resource.resourceType !== expectedType) {
1684
1735
  return {
1685
1736
  path: "$.resource.resourceType",
1686
1737
  code: "const",
1687
- message: 'This action requires resourceType "workspace".'
1738
+ message: `This action requires resourceType "${expectedType}".`
1688
1739
  };
1689
1740
  }
1690
1741
  if (!resource.resourceId.trim()) {
1691
1742
  return {
1692
1743
  path: "$.resource.resourceId",
1693
1744
  code: "required",
1694
- message: "This action requires a workspace resourceId."
1745
+ message: `This action requires a ${expectedType} resourceId.`
1695
1746
  };
1696
1747
  }
1697
1748
  return null;
@@ -2063,7 +2114,8 @@ function compactActionCatalogEntry(action) {
2063
2114
  agentAccess: action.agentAccess ?? (action.requiresConfirmation ? "confirm" : action.readOnly ? "auto" : "confirm"),
2064
2115
  requiredPermission: action.requiredPermission,
2065
2116
  requiredInput: required,
2066
- resourceType: action.agentContract?.resource?.kind ?? null,
2117
+ resourceType: action.agentContract?.resourceContext?.kind ?? (action.permissionName.startsWith("workspace:") ? "workspace" : null),
2118
+ resultResourceType: action.agentContract?.resource?.kind ?? null,
2067
2119
  exampleInput: example.value,
2068
2120
  exampleInputAvailable: example.available,
2069
2121
  docsUrl: action.agentContract?.docsUrl ?? null
@@ -2411,6 +2463,7 @@ var ERROR_CODE_PATTERN2 = /^[a-z][a-z0-9_]{0,63}$/;
2411
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;
2412
2464
  var CLOUDFLARE_RAY_ID_PATTERN2 = /^[0-9a-f]{16}-[A-Z]{3}$/;
2413
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;
2414
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$/;
2415
2468
  var PUBLIC_DETAIL_SOURCES2 = /* @__PURE__ */ new Set([
2416
2469
  "api_key_introspection",
@@ -2496,7 +2549,11 @@ function extractTopoloServiceErrorDetails2(body) {
2496
2549
  if (candidate === void 0) candidate = nestedRecord.details ?? nestedRecord;
2497
2550
  }
2498
2551
  if (candidate === void 0) candidate = record;
2499
- if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) return void 0;
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;
2500
2557
  const source = candidate;
2501
2558
  const safe = {};
2502
2559
  for (const [key, value] of Object.entries(source)) {
@@ -2533,12 +2590,42 @@ function extractTopoloServiceErrorDetails2(body) {
2533
2590
  continue;
2534
2591
  }
2535
2592
  if (["missingFields", "fields", "paths"].includes(key) && Array.isArray(value)) {
2536
- const paths = value.filter((item) => typeof item === "string" && /^[$.a-z0-9_[\]-]{1,160}$/i.test(item));
2593
+ const paths = value.filter((item) => typeof item === "string" && PUBLIC_FIELD_PATH_PATTERN2.test(item));
2537
2594
  if (paths.length) safe[key] = paths;
2538
2595
  }
2539
2596
  }
2540
2597
  return Object.keys(safe).length ? safe : void 0;
2541
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
+ }
2542
2629
  var TopoloHttpError2 = class extends TopoloSdkError2 {
2543
2630
  status;
2544
2631
  body;
@@ -3047,7 +3134,7 @@ function summarizeActionCapabilities(service, actions) {
3047
3134
  mutating: actions.filter((action) => !action.readOnly).length,
3048
3135
  destructive: actions.filter((action) => action.destructive).length,
3049
3136
  confirmationRequired: actions.filter((action) => action.requiresConfirmation).length,
3050
- resourceTypes: [...new Set(actions.map((action) => action.agentContract?.resource?.kind).filter(Boolean))].sort()
3137
+ resourceTypes: [...new Set(actions.map((action) => action.agentContract?.resourceContext?.kind ?? (action.permissionName.startsWith("workspace:") ? "workspace" : null)).filter(Boolean))].sort()
3051
3138
  };
3052
3139
  }
3053
3140
  function coerceObjectParam(value, name) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@topolo/mcp",
3
- "version": "0.11.11",
4
- "topoloPublishedVersion": "0.11.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.11.11"
38
+ "@topolo/sdk": "0.14.2"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@types/node": "^20.12.0",