@topolo/mcp 0.10.2 → 0.11.0
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/README.md +14 -48
- package/dist/dispatch.d.ts +5 -0
- package/dist/dispatch.js +64 -143
- package/dist/http.d.ts +26 -3
- package/dist/http.js +326 -355
- package/dist/index.js +777 -2516
- package/package.json +5 -11
package/README.md
CHANGED
|
@@ -11,14 +11,18 @@ tools rather than shelling out.
|
|
|
11
11
|
them typed tool schemas, scope-filtered tool advertisement, and structured
|
|
12
12
|
error responses. Both wrap the same `@topolo/sdk`.
|
|
13
13
|
|
|
14
|
-
## Install
|
|
14
|
+
## Install and register
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
npm install -g @topolo/
|
|
17
|
+
npm install -g @topolo/cli
|
|
18
|
+
topolo setup
|
|
19
|
+
topolo auth login
|
|
20
|
+
topolo doctor
|
|
18
21
|
```
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
Customers install only `@topolo/cli`; it owns an exact compatible MCP and SDK.
|
|
24
|
+
`topolo setup` registers the resolved local Node entry point, so agent startup
|
|
25
|
+
does not depend on `npx`, network access, or a separately synchronized package.
|
|
22
26
|
|
|
23
27
|
## Get a credential
|
|
24
28
|
|
|
@@ -33,45 +37,10 @@ Either:
|
|
|
33
37
|
|
|
34
38
|
## Register with an MCP client
|
|
35
39
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Then set the credential and (optional) agent label in your shell profile so
|
|
43
|
-
Claude Code inherits them when it spawns the server:
|
|
44
|
-
|
|
45
|
-
```bash
|
|
46
|
-
export TOPOLO_API_KEY=topo_live_...
|
|
47
|
-
export TOPOLO_AGENT_NAME=claude-code
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
### Claude Desktop
|
|
51
|
-
|
|
52
|
-
`claude_desktop_config.json`:
|
|
53
|
-
|
|
54
|
-
```json
|
|
55
|
-
{
|
|
56
|
-
"mcpServers": {
|
|
57
|
-
"topolo": {
|
|
58
|
-
"command": "npx",
|
|
59
|
-
"args": ["-y", "@topolo/mcp"],
|
|
60
|
-
"env": {
|
|
61
|
-
"TOPOLO_API_KEY": "topo_live_...",
|
|
62
|
-
"TOPOLO_AGENT_NAME": "claude-desktop"
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
### Codex / Cursor / generic MCP host
|
|
70
|
-
|
|
71
|
-
Any MCP host that spawns a stdio subprocess works. Point `command` at
|
|
72
|
-
`npx -y @topolo/mcp` and pass the same env vars. Most Codex-style setups also
|
|
73
|
-
read `AGENTS.md` files — see `@topolo/cli`'s `skills/codex/AGENTS.md` for a
|
|
74
|
-
ready-made agent guide that covers both the CLI and this MCP.
|
|
40
|
+
Run `topolo setup`; it configures Claude Code and Codex with an absolute Node
|
|
41
|
+
entry point resolved from the installed CLI dependency graph. Advanced hosts
|
|
42
|
+
may execute the exported `@topolo/mcp/stdio` entry directly, but customer setup
|
|
43
|
+
must not use an `npx` launcher because that makes startup network-dependent.
|
|
75
44
|
|
|
76
45
|
## Supported env vars
|
|
77
46
|
|
|
@@ -147,8 +116,6 @@ deletion, encryption, and audit storage for the data they own.
|
|
|
147
116
|
| `topolo_whoami` | (none) | no |
|
|
148
117
|
| `topolo_search_applications` | (none) | no |
|
|
149
118
|
| `topolo_get_application` | (none) | no |
|
|
150
|
-
| `topolo_list_application_requirements` | (none) | no |
|
|
151
|
-
| `topolo_audit_applications` | (none) | no |
|
|
152
119
|
| `topolo_search_actions` | (credential-scoped) | no |
|
|
153
120
|
| `topolo_get_action` | (credential-scoped) | no |
|
|
154
121
|
| `topolo_read_action` | catalog or target policy | no |
|
|
@@ -217,9 +184,8 @@ sent.
|
|
|
217
184
|
catalog. They do not expose the global generated platform catalog.
|
|
218
185
|
- **Credential-scoped action discovery.** `topolo_search_actions` reads only one
|
|
219
186
|
app partition and returns at most 100 permission-filtered actions per page.
|
|
220
|
-
Search, capability discovery, validation, planning
|
|
221
|
-
|
|
222
|
-
full contracts or findings are needed.
|
|
187
|
+
Search, capability discovery, validation, and planning return compact agent
|
|
188
|
+
guidance by default; pass `detail: true` only when full contracts are needed.
|
|
223
189
|
- **Audit headers.** Every request sends `X-Topolo-Client: topolo-mcp/<ver>`,
|
|
224
190
|
`X-Topolo-Agent: <label>`, `X-Topolo-Request-Id: <uuid>`.
|
|
225
191
|
- **Write-action confirmation.** The SDK refuses mutating HTTP methods unless
|
package/dist/dispatch.d.ts
CHANGED
|
@@ -75,6 +75,11 @@ type ToolDispatchResult = {
|
|
|
75
75
|
reason: ToolDispatchFailureReason;
|
|
76
76
|
durationMs: number;
|
|
77
77
|
requiredScopes?: string[];
|
|
78
|
+
code?: string;
|
|
79
|
+
status?: number;
|
|
80
|
+
requestId?: string;
|
|
81
|
+
details?: unknown;
|
|
82
|
+
hint?: string;
|
|
78
83
|
};
|
|
79
84
|
interface ToolAdvertisement {
|
|
80
85
|
name: string;
|
package/dist/dispatch.js
CHANGED
|
@@ -1,29 +1,24 @@
|
|
|
1
1
|
// src/dispatch.ts
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
TopoloAuthError,
|
|
4
|
+
TopoloHttpError,
|
|
5
|
+
TopoloPermissionError,
|
|
6
|
+
publicTopoloErrorMessage,
|
|
7
|
+
topoloErrorHint
|
|
8
|
+
} from "@topolo/sdk";
|
|
3
9
|
|
|
4
10
|
// src/tools.ts
|
|
5
11
|
import {
|
|
6
|
-
APPLICATION_REQUIREMENTS,
|
|
7
|
-
APPLICATION_REQUIREMENTS_VERSION,
|
|
8
|
-
auditApplicationEntries,
|
|
9
|
-
applicationRequirementScopes,
|
|
10
12
|
compactActionCatalogEntry,
|
|
13
|
+
compactActionPreparation,
|
|
11
14
|
compactActionPlan,
|
|
12
15
|
compactActionValidation,
|
|
13
|
-
compactApplicationAudit,
|
|
14
16
|
friendlyAppId,
|
|
15
|
-
listApplicationDirectory,
|
|
16
|
-
requirementsForApplication,
|
|
17
|
-
resolveApplicationDirectoryEntry,
|
|
18
17
|
resolveCatalogServiceUrl
|
|
19
18
|
} from "@topolo/sdk";
|
|
20
19
|
|
|
21
20
|
// src/gating.ts
|
|
22
|
-
function hasPlatformAccess(set) {
|
|
23
|
-
return (set.role === "platform_super_admin" || set.role === "platform_admin") && set.orgSlug === "admin";
|
|
24
|
-
}
|
|
25
21
|
function hasScope(set, required) {
|
|
26
|
-
if (hasPlatformAccess(set)) return true;
|
|
27
22
|
if (set.permissions.includes("*")) return true;
|
|
28
23
|
const [servicePart, actionPart] = splitPermission(required);
|
|
29
24
|
if (!servicePart) return set.permissions.includes(required);
|
|
@@ -373,6 +368,38 @@ var TOOLS = [
|
|
|
373
368
|
return args["detail"] === true ? result : compactActionValidation(result.action.actionId, result.validation);
|
|
374
369
|
}
|
|
375
370
|
},
|
|
371
|
+
{
|
|
372
|
+
name: "topolo_prepare_action",
|
|
373
|
+
title: "Prepare one Topolo action",
|
|
374
|
+
description: "Fetches one exact live action contract once and returns one valid published example, missing fields, exact request, confirmation state, verification, and rollback without executing it.",
|
|
375
|
+
requiredScopes: [],
|
|
376
|
+
destructive: false,
|
|
377
|
+
inputSchema: {
|
|
378
|
+
type: "object",
|
|
379
|
+
properties: {
|
|
380
|
+
actionId: { type: "string", minLength: 1 },
|
|
381
|
+
input: { type: "object", additionalProperties: true },
|
|
382
|
+
resourceType: { type: "string" },
|
|
383
|
+
resourceId: { type: "string" },
|
|
384
|
+
confirm: { type: "boolean", description: "Include confirmation in the plan without executing the action." }
|
|
385
|
+
},
|
|
386
|
+
required: ["actionId"],
|
|
387
|
+
additionalProperties: false
|
|
388
|
+
},
|
|
389
|
+
handler: async (topolo, args) => {
|
|
390
|
+
const actionId = requiredString(args, "actionId");
|
|
391
|
+
const resource = optionalResourceContext(args);
|
|
392
|
+
const preparation = await topolo.client.prepareAction(
|
|
393
|
+
actionId,
|
|
394
|
+
args["input"] === void 0 ? void 0 : coerceObjectParam(args["input"], "input"),
|
|
395
|
+
{
|
|
396
|
+
confirm: args["confirm"] === true,
|
|
397
|
+
...resource ? { resource } : {}
|
|
398
|
+
}
|
|
399
|
+
);
|
|
400
|
+
return compactActionPreparation(preparation);
|
|
401
|
+
}
|
|
402
|
+
},
|
|
376
403
|
{
|
|
377
404
|
name: "topolo_plan_action",
|
|
378
405
|
title: "Plan one Topolo action",
|
|
@@ -584,87 +611,6 @@ var TOOLS = [
|
|
|
584
611
|
args["confirm"] === true
|
|
585
612
|
)
|
|
586
613
|
},
|
|
587
|
-
{
|
|
588
|
-
name: "topolo_list_application_requirements",
|
|
589
|
-
title: "List Topolo application build requirements",
|
|
590
|
-
description: "Returns the current versioned Topolo application-build contract, optionally filtered to one application. Use this before creating or expanding a Topolo app so the agent follows shared metadata, docs, auth, shell, service registration, deployment, observability, and verification requirements.",
|
|
591
|
-
requiredScopes: [],
|
|
592
|
-
destructive: false,
|
|
593
|
-
inputSchema: {
|
|
594
|
-
type: "object",
|
|
595
|
-
properties: {
|
|
596
|
-
application: {
|
|
597
|
-
type: "string",
|
|
598
|
-
description: "Optional application ID from topolo_search_applications."
|
|
599
|
-
}
|
|
600
|
-
},
|
|
601
|
-
additionalProperties: false
|
|
602
|
-
},
|
|
603
|
-
handler: async (topolo, args) => {
|
|
604
|
-
const application = args["application"];
|
|
605
|
-
if (application === void 0) {
|
|
606
|
-
return {
|
|
607
|
-
version: APPLICATION_REQUIREMENTS_VERSION,
|
|
608
|
-
application: null,
|
|
609
|
-
scopes: ["all", "browser", "api", "tooling", "agent_surface"],
|
|
610
|
-
requirements: APPLICATION_REQUIREMENTS
|
|
611
|
-
};
|
|
612
|
-
}
|
|
613
|
-
const resolved = await resolveDirectoryApplication(topolo, application);
|
|
614
|
-
const app = resolved.application;
|
|
615
|
-
return {
|
|
616
|
-
version: APPLICATION_REQUIREMENTS_VERSION,
|
|
617
|
-
application: app,
|
|
618
|
-
scopes: applicationRequirementScopes(app),
|
|
619
|
-
requirements: requirementsForApplication(app)
|
|
620
|
-
};
|
|
621
|
-
}
|
|
622
|
-
},
|
|
623
|
-
{
|
|
624
|
-
name: "topolo_audit_applications",
|
|
625
|
-
title: "Audit Topolo applications against platform requirements",
|
|
626
|
-
description: "Returns catalog-backed conformance scores and migration-queue items for all Topolo apps, or one application when provided. Use this to see which shared platform requirements need implementation, verification, or deeper review.",
|
|
627
|
-
requiredScopes: [],
|
|
628
|
-
destructive: false,
|
|
629
|
-
inputSchema: {
|
|
630
|
-
type: "object",
|
|
631
|
-
properties: {
|
|
632
|
-
application: {
|
|
633
|
-
type: "string",
|
|
634
|
-
description: "Optional application ID from topolo_search_applications."
|
|
635
|
-
},
|
|
636
|
-
failOn: {
|
|
637
|
-
type: "string",
|
|
638
|
-
enum: ["missing", "needs_review", "partial"],
|
|
639
|
-
description: "Optional conformance gate. Returns conformanceGate.passed=false when findings at this severity or worse exist."
|
|
640
|
-
},
|
|
641
|
-
detail: {
|
|
642
|
-
type: "boolean",
|
|
643
|
-
description: "Include every finding and migration item. Defaults to per-app scores and a queue count."
|
|
644
|
-
}
|
|
645
|
-
},
|
|
646
|
-
additionalProperties: false
|
|
647
|
-
},
|
|
648
|
-
handler: async (topolo, args) => {
|
|
649
|
-
const application = args["application"];
|
|
650
|
-
const failOn = args["failOn"];
|
|
651
|
-
if (failOn !== void 0 && typeof failOn !== "string") {
|
|
652
|
-
throw new TopoloMcpPublicError("failOn must be one of: missing, needs_review, partial");
|
|
653
|
-
}
|
|
654
|
-
const gate = failOn ? normalizeFailOn(failOn) : null;
|
|
655
|
-
let report;
|
|
656
|
-
if (application === void 0) {
|
|
657
|
-
const directory = await listApplicationDirectory(topolo.client);
|
|
658
|
-
report = auditApplicationEntries(directory.applications.map((app) => app.application));
|
|
659
|
-
const output2 = args["detail"] === true ? report : compactApplicationAudit(report);
|
|
660
|
-
return gate ? { ...output2, conformanceGate: evaluateApplicationAuditGate(report, gate) } : output2;
|
|
661
|
-
}
|
|
662
|
-
const resolved = await resolveDirectoryApplication(topolo, application);
|
|
663
|
-
report = auditApplicationEntries([resolved.application]);
|
|
664
|
-
const output = args["detail"] === true ? report : compactApplicationAudit(report);
|
|
665
|
-
return gate ? { ...output, conformanceGate: evaluateApplicationAuditGate(report, gate) } : output;
|
|
666
|
-
}
|
|
667
|
-
},
|
|
668
614
|
{
|
|
669
615
|
name: "topolo_api_call",
|
|
670
616
|
title: "Call any Topolo platform service",
|
|
@@ -764,57 +710,9 @@ async function buildTopoloTools(topolo, scopes, tools = TOOLS) {
|
|
|
764
710
|
void topolo;
|
|
765
711
|
return filterToolsByScopes(tools, scopes);
|
|
766
712
|
}
|
|
767
|
-
function evaluateApplicationAuditGate(report, threshold) {
|
|
768
|
-
const counts = {
|
|
769
|
-
met: 0,
|
|
770
|
-
partial: 0,
|
|
771
|
-
missing: 0,
|
|
772
|
-
needs_review: 0
|
|
773
|
-
};
|
|
774
|
-
for (const audit of report.applications) {
|
|
775
|
-
for (const finding of audit.findings) {
|
|
776
|
-
counts[finding.status] += 1;
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
const failingFindings = statusesAtOrWorse(threshold).reduce(
|
|
780
|
-
(total, status) => total + counts[status],
|
|
781
|
-
0
|
|
782
|
-
);
|
|
783
|
-
return {
|
|
784
|
-
threshold,
|
|
785
|
-
passed: failingFindings === 0,
|
|
786
|
-
message: failingFindings === 0 ? `No findings at or above ${threshold}.` : `${failingFindings} finding(s) at or above ${threshold}.`,
|
|
787
|
-
failingFindings,
|
|
788
|
-
counts
|
|
789
|
-
};
|
|
790
|
-
}
|
|
791
|
-
function normalizeFailOn(value) {
|
|
792
|
-
const normalized = value.trim().toLowerCase().replace(/-/g, "_");
|
|
793
|
-
if (normalized === "missing" || normalized === "needs_review" || normalized === "partial") {
|
|
794
|
-
return normalized;
|
|
795
|
-
}
|
|
796
|
-
throw new TopoloMcpPublicError("failOn must be one of: missing, needs_review, partial");
|
|
797
|
-
}
|
|
798
|
-
function statusesAtOrWorse(threshold) {
|
|
799
|
-
if (threshold === "missing") return ["missing"];
|
|
800
|
-
if (threshold === "needs_review") return ["missing", "needs_review"];
|
|
801
|
-
return ["missing", "needs_review", "partial"];
|
|
802
|
-
}
|
|
803
713
|
function displayAppId(entry) {
|
|
804
714
|
return friendlyAppId(entry);
|
|
805
715
|
}
|
|
806
|
-
async function resolveDirectoryApplication(topolo, application) {
|
|
807
|
-
if (typeof application !== "string" || !application.trim()) {
|
|
808
|
-
throw new TopoloMcpPublicError("`application` must be a non-empty string.");
|
|
809
|
-
}
|
|
810
|
-
try {
|
|
811
|
-
return await resolveApplicationDirectoryEntry(topolo.client, application);
|
|
812
|
-
} catch (error) {
|
|
813
|
-
throw new TopoloMcpPublicError(
|
|
814
|
-
error instanceof Error ? error.message : `Unknown application "${application}".`
|
|
815
|
-
);
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
716
|
|
|
819
717
|
// src/dispatch.ts
|
|
820
718
|
function listAvailableTopoloTools(scopes, tools = TOOLS) {
|
|
@@ -867,8 +765,13 @@ async function dispatchTopoloTool(input) {
|
|
|
867
765
|
return {
|
|
868
766
|
ok: false,
|
|
869
767
|
reason: "permission",
|
|
870
|
-
error:
|
|
768
|
+
error: publicTopoloErrorMessage(err),
|
|
871
769
|
requiredScopes: err.required,
|
|
770
|
+
code: err.code,
|
|
771
|
+
status: err.status,
|
|
772
|
+
requestId: err.requestId,
|
|
773
|
+
details: err.details,
|
|
774
|
+
hint: topoloErrorHint(err),
|
|
872
775
|
durationMs: Date.now() - started
|
|
873
776
|
};
|
|
874
777
|
}
|
|
@@ -877,6 +780,24 @@ async function dispatchTopoloTool(input) {
|
|
|
877
780
|
ok: false,
|
|
878
781
|
reason: "auth",
|
|
879
782
|
error: "Authentication failed.",
|
|
783
|
+
code: err.code,
|
|
784
|
+
status: err.status,
|
|
785
|
+
requestId: err.requestId,
|
|
786
|
+
details: err.details,
|
|
787
|
+
hint: topoloErrorHint(err),
|
|
788
|
+
durationMs: Date.now() - started
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
if (err instanceof TopoloHttpError) {
|
|
792
|
+
return {
|
|
793
|
+
ok: false,
|
|
794
|
+
reason: "handler",
|
|
795
|
+
error: publicTopoloErrorMessage(err),
|
|
796
|
+
code: err.code,
|
|
797
|
+
status: err.status,
|
|
798
|
+
requestId: err.requestId,
|
|
799
|
+
details: err.details,
|
|
800
|
+
hint: topoloErrorHint(err),
|
|
880
801
|
durationMs: Date.now() - started
|
|
881
802
|
};
|
|
882
803
|
}
|
package/dist/http.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { TopoloRuntimeEnvironment, TopoloCredential, Topolo } from '@topolo/sdk';
|
|
1
2
|
import { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';
|
|
2
|
-
import { TopoloCredential, Topolo } from '@topolo/sdk';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Hosted, multi-tenant MCP Streamable HTTP transport for the Topolo platform.
|
|
@@ -18,9 +18,11 @@ import { TopoloCredential, Topolo } from '@topolo/sdk';
|
|
|
18
18
|
* Mcp-Session-Id continuity, GET SSE stream, DELETE to terminate) over pure
|
|
19
19
|
* Web-Standard Request/Response — so it runs unchanged in a Cloudflare Worker.
|
|
20
20
|
*/
|
|
21
|
-
interface
|
|
21
|
+
interface TopoloMcpHttpInternalOptions {
|
|
22
22
|
/** Path the MCP endpoint is mounted at. Defaults to '/mcp'. */
|
|
23
23
|
path?: string;
|
|
24
|
+
/** Effective Topolo environment. Defaults to production and is always surfaced to the MCP client. */
|
|
25
|
+
environment?: TopoloRuntimeEnvironment;
|
|
24
26
|
/** Per-service URL overrides (e.g. staging). Passed through to createTopolo. */
|
|
25
27
|
serviceUrls?: Record<string, string>;
|
|
26
28
|
/** Per-service TopoloAuth app-id overrides. Passed through to createTopolo. */
|
|
@@ -52,12 +54,33 @@ interface McpSession {
|
|
|
52
54
|
* const handler = createTopoloMcpHttpHandler();
|
|
53
55
|
* export default { fetch: handler.fetch };
|
|
54
56
|
*/
|
|
55
|
-
declare function
|
|
57
|
+
declare function createTopoloMcpHttpHandlerInternal(options?: TopoloMcpHttpInternalOptions): {
|
|
56
58
|
/** Cloudflare Worker / fetch-compatible entrypoint. */
|
|
57
59
|
fetch: (request: Request) => Promise<Response>;
|
|
58
60
|
/** Exposed for tests/introspection. */
|
|
59
61
|
sessions: Map<string, McpSession>;
|
|
60
62
|
};
|
|
63
|
+
type TopoloMcpHttpHandlerInternal = ReturnType<typeof createTopoloMcpHttpHandlerInternal>;
|
|
64
|
+
|
|
65
|
+
interface TopoloMcpHttpOptions {
|
|
66
|
+
/** Path the MCP endpoint is mounted at. Defaults to '/mcp'. */
|
|
67
|
+
path?: string;
|
|
68
|
+
/** Effective Topolo environment. Defaults to production and is always surfaced to the MCP client. */
|
|
69
|
+
environment?: TopoloRuntimeEnvironment;
|
|
70
|
+
/** Per-service URL overrides (e.g. staging). Passed through to createTopolo. */
|
|
71
|
+
serviceUrls?: Record<string, string>;
|
|
72
|
+
/** Per-service TopoloAuth app-id overrides. Passed through to createTopolo. */
|
|
73
|
+
appIds?: Record<string, string>;
|
|
74
|
+
/** Human-readable agent label for audit logs (forwarded to the SDK). */
|
|
75
|
+
agentName?: string;
|
|
76
|
+
/** Validated network transport for the SDK client. Defaults to global fetch. */
|
|
77
|
+
fetch?: typeof fetch;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Create the public hosted MCP handler. The SDK client is always constructed
|
|
81
|
+
* here from the validated effective environment and per-request credential.
|
|
82
|
+
*/
|
|
83
|
+
declare function createTopoloMcpHttpHandler(options?: TopoloMcpHttpOptions): TopoloMcpHttpHandlerInternal;
|
|
61
84
|
type TopoloMcpHttpHandler = ReturnType<typeof createTopoloMcpHttpHandler>;
|
|
62
85
|
|
|
63
86
|
export { type TopoloMcpHttpHandler, type TopoloMcpHttpOptions, createTopoloMcpHttpHandler };
|