@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/dispatch.js +12 -5
- package/dist/http.js +232 -672
- package/dist/index.js +111 -9
- package/package.json +5 -5
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:
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
4
|
-
"topoloPublishedVersion": "0.11.
|
|
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.
|
|
38
|
+
"@topolo/sdk": "0.11.3"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/node": "^20.12.0",
|