@topolo/mcp 0.11.1 → 0.11.3

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
@@ -3,7 +3,7 @@
3
3
  // src/index.ts
4
4
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
5
 
6
- // node_modules/@topolo/sdk/dist/index.js
6
+ // ../../node_modules/.pnpm/@topolo+sdk@0.11.3/node_modules/@topolo/sdk/dist/index.js
7
7
  function applyAuthHeaders(headers, credential) {
8
8
  if (credential.kind === "api_key") {
9
9
  headers.set("X-Api-Key", credential.apiKey);
@@ -758,6 +758,18 @@ function prepareTopoloUrlSessionUploadInput(contractValue, byteLength, input, op
758
758
  function topoloUploadResponseValue(response, path) {
759
759
  return path.split(".").reduce((value, segment) => isRecord2(value) ? value[segment] : void 0, response);
760
760
  }
761
+ function redactTopoloUrlSessionResult(response, uploadUrlPath) {
762
+ const redacted = cloneUploadValue(response);
763
+ let target = redacted;
764
+ const segments = uploadUrlPath.split(".");
765
+ for (const segment of segments.slice(0, -1)) {
766
+ target = isRecord2(target) ? target[segment] : void 0;
767
+ }
768
+ const finalSegment = segments.at(-1);
769
+ if (finalSegment && isRecord2(target) && finalSegment in target) target[finalSegment] = "[REDACTED]";
770
+ redactUploadHeaders(redacted);
771
+ return redacted;
772
+ }
761
773
  function decodeTopoloActionBase64(contentBase64) {
762
774
  if (!isCanonicalBase64(contentBase64)) {
763
775
  throw new TopoloSdkError("invalid_upload_content", "Upload content must be canonical base64.");
@@ -776,6 +788,28 @@ function mapUploadInput(contract, contentBase64, input, options) {
776
788
  [contract.fileNameField]: fileName
777
789
  };
778
790
  }
791
+ function cloneUploadValue(value) {
792
+ if (Array.isArray(value)) return value.map(cloneUploadValue);
793
+ if (!isRecord2(value)) return value;
794
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, cloneUploadValue(item)]));
795
+ }
796
+ function redactUploadHeaders(value) {
797
+ if (Array.isArray(value)) {
798
+ value.forEach(redactUploadHeaders);
799
+ return;
800
+ }
801
+ if (!isRecord2(value)) return;
802
+ for (const [key, item] of Object.entries(value)) {
803
+ if (key.toLowerCase() === "headers" && isRecord2(item)) {
804
+ for (const header of Object.keys(item)) {
805
+ if (["authorization", "cookie", "set-cookie", "proxy-authorization", "x-api-key"].includes(header.toLowerCase())) {
806
+ item[header] = "[REDACTED]";
807
+ }
808
+ }
809
+ }
810
+ redactUploadHeaders(item);
811
+ }
812
+ }
779
813
  function validateUploadMetadata(contract, byteLength, options) {
780
814
  const fileName = options.fileName.trim();
781
815
  const contentType = options.contentType.trim().toLowerCase();
@@ -1417,7 +1451,7 @@ var TopoloClient = class _TopoloClient {
1417
1451
  throw new TopoloSdkError("invalid_upload_response", `Upload action response is missing ${contract.uploadUrlPath}.`);
1418
1452
  }
1419
1453
  await this.putUrlSessionContent(entry, uploadUrl, contract.uploadMethod, bytes, options);
1420
- if (!contract.complete) return initiation;
1454
+ if (!contract.complete) return redactTopoloUrlSessionResult(initiation, contract.uploadUrlPath);
1421
1455
  const completionValue = topoloUploadResponseValue(initiation, contract.complete.responsePath);
1422
1456
  if (typeof completionValue !== "string" || !completionValue.trim()) {
1423
1457
  throw new TopoloSdkError("invalid_upload_response", `Upload action response is missing ${contract.complete.responsePath}.`);
@@ -1944,6 +1978,7 @@ function mergeSignals(a, b) {
1944
1978
  function compactActionCatalogEntry(action) {
1945
1979
  const required = Array.isArray(action.inputSchema["required"]) ? action.inputSchema["required"].filter((value) => typeof value === "string") : [];
1946
1980
  const examples = action.agentContract?.examples ?? [];
1981
+ const example = compactExample(examples[0]?.input);
1947
1982
  return {
1948
1983
  actionId: action.actionId,
1949
1984
  title: action.title,
@@ -1955,13 +1990,75 @@ function compactActionCatalogEntry(action) {
1955
1990
  requiredPermission: action.requiredPermission,
1956
1991
  requiredInput: required,
1957
1992
  resourceType: action.agentContract?.resource?.kind ?? null,
1958
- exampleInput: examples[0]?.input ?? null,
1993
+ exampleInput: example.value,
1994
+ exampleInputAvailable: example.available,
1959
1995
  docsUrl: action.agentContract?.docsUrl ?? null
1960
1996
  };
1961
1997
  }
1998
+ function compactExample(input) {
1999
+ if (!input) return { value: null, available: false };
2000
+ const serialized = JSON.stringify(input);
2001
+ if (serialized.length > 512) return { value: null, available: true };
2002
+ return { value: input, available: true };
2003
+ }
1962
2004
  function compactActionValidation(actionId, validation) {
1963
2005
  return { actionId, valid: validation.valid, errors: validation.errors };
1964
2006
  }
2007
+ var ACTION_RESULT_MAX_DEPTH = 4;
2008
+ var ACTION_RESULT_MAX_KEYS = 16;
2009
+ var ACTION_RESULT_MAX_ITEMS = 5;
2010
+ var ACTION_RESULT_MAX_SCALARS = 120;
2011
+ var ACTION_RESULT_MAX_STRING = 512;
2012
+ function resultKeyPriority(key) {
2013
+ if (/^(id|requestId|status|state|url|slug|name|title|version)$/i.test(key)) return 0;
2014
+ if (/(Id|Ids|Url|Uri|Status|State|Count|Total|Version|CreatedAt|UpdatedAt)$/i.test(key)) return 1;
2015
+ return 2;
2016
+ }
2017
+ function compactResultValue(value, depth, budget) {
2018
+ if (value === null || typeof value === "boolean" || typeof value === "number") {
2019
+ budget.scalars += 1;
2020
+ return value;
2021
+ }
2022
+ if (typeof value === "string") {
2023
+ budget.scalars += 1;
2024
+ if (value.length <= ACTION_RESULT_MAX_STRING) return value;
2025
+ return `${value.slice(0, ACTION_RESULT_MAX_STRING)}\u2026 (${value.length} chars)`;
2026
+ }
2027
+ if (depth >= ACTION_RESULT_MAX_DEPTH || budget.scalars >= ACTION_RESULT_MAX_SCALARS) {
2028
+ return Array.isArray(value) ? { count: value.length, omitted: true } : { omitted: true };
2029
+ }
2030
+ if (Array.isArray(value)) {
2031
+ const items = value.slice(0, ACTION_RESULT_MAX_ITEMS).map((item) => compactResultValue(item, depth + 1, budget));
2032
+ return value.length > ACTION_RESULT_MAX_ITEMS ? { count: value.length, items, omittedItems: value.length - ACTION_RESULT_MAX_ITEMS } : items;
2033
+ }
2034
+ if (typeof value === "object") {
2035
+ const record = value;
2036
+ const allKeys = Object.keys(record);
2037
+ const keys = allKeys.sort((left, right) => resultKeyPriority(left) - resultKeyPriority(right)).slice(0, ACTION_RESULT_MAX_KEYS);
2038
+ const compact = Object.fromEntries(keys.map((key) => [key, compactResultValue(record[key], depth + 1, budget)]));
2039
+ if (allKeys.length > keys.length) compact["_omittedKeys"] = allKeys.length - keys.length;
2040
+ return compact;
2041
+ }
2042
+ budget.scalars += 1;
2043
+ return String(value);
2044
+ }
2045
+ function compactActionResult(actionId, result) {
2046
+ let normalized = result;
2047
+ if (typeof result === "string") {
2048
+ try {
2049
+ normalized = JSON.parse(result);
2050
+ } catch {
2051
+ normalized = result;
2052
+ }
2053
+ }
2054
+ return { actionId, result: compactResultValue(normalized, 0, { scalars: 0 }) };
2055
+ }
2056
+ function preferExactActionMatches(actions, query) {
2057
+ const expected = query?.trim().toLowerCase();
2058
+ if (!expected) return actions;
2059
+ const exact = actions.filter((action) => [action.actionId, action.name, action.toolName].some((candidate) => candidate?.trim().toLowerCase() === expected));
2060
+ return exact.length ? exact : actions;
2061
+ }
1965
2062
  function compactActionPlan(plan) {
1966
2063
  return {
1967
2064
  actionId: plan.action.actionId,
@@ -2169,7 +2266,7 @@ function createTopolo(options) {
2169
2266
  };
2170
2267
  }
2171
2268
 
2172
- // node_modules/@topolo/sdk/dist/node/credential-store.js
2269
+ // ../../node_modules/.pnpm/@topolo+sdk@0.11.3/node_modules/@topolo/sdk/dist/node/credential-store.js
2173
2270
  import { mkdir, readFile, writeFile, chmod, rm, stat } from "fs/promises";
2174
2271
  import { existsSync } from "fs";
2175
2272
  import { homedir, platform } from "os";
@@ -2950,7 +3047,8 @@ var TOOLS = [
2950
3047
  },
2951
3048
  query: { type: "string" },
2952
3049
  cursor: { type: "string" },
2953
- limit: { type: "integer", minimum: 1, maximum: 100, default: 25 }
3050
+ limit: { type: "integer", minimum: 1, maximum: 100, default: 25 },
3051
+ detail: { type: "boolean", description: "Return full schemas and agent contracts. Defaults to compact action guidance." }
2954
3052
  },
2955
3053
  required: ["service"],
2956
3054
  additionalProperties: false
@@ -2958,13 +3056,15 @@ var TOOLS = [
2958
3056
  handler: async (topolo, args) => {
2959
3057
  const service = typeof args["service"] === "string" ? args["service"].trim() : "";
2960
3058
  if (!service) throw new TopoloMcpPublicError("service is required for bounded action discovery.");
3059
+ const query = typeof args["query"] === "string" ? args["query"] : void 0;
2961
3060
  const result = await topolo.client.searchActions({
2962
3061
  service,
2963
- query: typeof args["query"] === "string" ? args["query"] : void 0,
3062
+ query,
2964
3063
  cursor: typeof args["cursor"] === "string" ? args["cursor"] : void 0,
2965
3064
  limit: typeof args["limit"] === "number" ? args["limit"] : void 0
2966
3065
  });
2967
- return args["detail"] === true ? result : { actions: result.actions.map(compactActionCatalogEntry), page: result.page };
3066
+ const actions = preferExactActionMatches(result.actions, query);
3067
+ return args["detail"] === true ? { ...result, actions } : { actions: actions.map(compactActionCatalogEntry), page: result.page };
2968
3068
  }
2969
3069
  },
2970
3070
  {
@@ -3052,7 +3152,8 @@ var TOOLS = [
3052
3152
  input: { type: "object", additionalProperties: true },
3053
3153
  resourceType: { type: "string", description: "Optional resource-scoping alias published in the app resource catalog (e.g. workspace, brand, merchant)." },
3054
3154
  resourceId: { type: "string", description: "Resource id. Required when resourceType is provided." },
3055
- confirm: { type: "boolean" }
3155
+ confirm: { type: "boolean" },
3156
+ detail: { type: "boolean", description: "Return the complete action response. Defaults to a bounded agent projection." }
3056
3157
  },
3057
3158
  required: ["actionId"],
3058
3159
  additionalProperties: false
@@ -3062,7 +3163,7 @@ var TOOLS = [
3062
3163
  if (!actionId) throw new TopoloMcpPublicError("actionId is required.");
3063
3164
  const input = coerceObjectParam(args["input"], "input");
3064
3165
  const resource = optionalResourceContext(args);
3065
- return topolo.client.callAction(
3166
+ const result = await topolo.client.callAction(
3066
3167
  actionId,
3067
3168
  input,
3068
3169
  {
@@ -3070,6 +3171,7 @@ var TOOLS = [
3070
3171
  ...resource ? { resource } : {}
3071
3172
  }
3072
3173
  );
3174
+ return args["detail"] === true ? result : compactActionResult(actionId, result);
3073
3175
  }
3074
3176
  },
3075
3177
  {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@topolo/mcp",
3
- "version": "0.11.1",
4
- "topoloPublishedVersion": "0.11.1",
3
+ "version": "0.11.3",
4
+ "topoloPublishedVersion": "0.11.3",
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",
@@ -29,13 +29,13 @@
29
29
  "dev": "tsup --watch",
30
30
  "typecheck": "tsc --noEmit",
31
31
  "start": "node dist/index.js",
32
- "test": "vitest run",
32
+ "test": "if test \"$TOPOLO_BUILD_READY\" = 1; then vitest run; else python3 ../../tooling/with-resource-lock.py ../topolo-sdk -- sh -c 'pnpm --dir ../topolo-sdk build && exec vitest run'; fi",
33
33
  "test:watch": "vitest",
34
- "coverage": "vitest run --coverage"
34
+ "coverage": "if test \"$TOPOLO_BUILD_READY\" = 1; then vitest run --coverage; else python3 ../../tooling/with-resource-lock.py ../topolo-sdk -- sh -c 'pnpm --dir ../topolo-sdk build && exec vitest run --coverage'; fi"
35
35
  },
36
36
  "dependencies": {
37
37
  "@modelcontextprotocol/sdk": "^1.0.0",
38
- "@topolo/sdk": "0.11.1"
38
+ "@topolo/sdk": "0.11.3"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@types/node": "^20.12.0",