@topolo/mcp
v0.14.31
Published
Model Context Protocol server for the Topolo platform. Exposes scope-gated tools that third-party agents (Claude, Codex, etc.) can call natively.
Readme
TopoloMCP
Model Context Protocol server for the Topolo platform. Lets MCP-capable agents (Claude Desktop, Claude Code, Codex, Cursor, etc.) call Topolo APIs as native tools rather than shelling out.
Why both a CLI and an MCP server?
- The CLI (
@topolo/cli) is for humans, shell scripts, and agents without MCP support. - The MCP server (
@topolo/mcp) is for agents that speak the protocol natively — it gives them typed tool schemas, scope-filtered tool advertisement, and structured error responses. Both wrap the same@topolo/sdk.
Install and register
npm install -g @topolo/cli
topolo setup
topolo auth login
topolo doctorCustomers install only @topolo/cli; it owns an exact compatible MCP and SDK.
topolo setup registers the resolved local Node entry point, so agent startup
does not depend on npx, network access, or a separately synchronized package.
Register an accountable agent installation
For Codex, Claude, and other persistent agent hosts, register the host once from an organization context:
topolo installations register \
--name "Codex on my workstation" \
--provider openai \
--host codex \
--grant app_topolo_agent:actionsThe CLI creates an accountable external principal, generates a device-local
DPoP key, stores the client secret and private key in the operating system's
protected credential store, and configures the host with only
TOPOLO_AGENT_INSTALLATION_ID. The MCP exchanges short-lived credentials for
the exact service, task, and resource being accessed. No reusable secret is
written to Codex or Claude configuration.
Human OAuth contexts and explicit API keys remain supported for interactive and compatibility use, but they are never used as a fallback when an installation ID is configured.
Register with an MCP client
Run topolo setup; it configures Claude Code and Codex with an absolute Node
entry point resolved from the installed CLI dependency graph. Advanced hosts
may execute the exported @topolo/mcp/stdio entry directly, but customer setup
must not use an npx launcher because that makes startup network-dependent.
Hosted HTTP transport
@topolo/mcp/http exports createTopoloMcpHttpHandler, an MCP Streamable
HTTP handler over Web-Standard Request/Response. Every request carries its
own credential (Authorization: Bearer <access token> or X-Api-Key: <key>);
there is no process-wide credential and no fallback. A request without a usable
credential answers 401.
import { createTopoloMcpHttpHandler } from '@topolo/mcp/http';
const handler = createTopoloMcpHttpHandler({
environment: 'production',
sessionMode: 'stateless',
});
export default { fetch: handler.fetch };sessionMode chooses how MCP sessions are held:
'stateful'(default) keeps an in-memoryMcp-Session-Idregistry bound to the credential that created each session. Every request of a session must reach the same process, so use it only for one long-lived process.'stateless'serves every POST with a fresh server built from that request's credential, answers with JSON, issues no session id and keeps nothing after the response. Any instance can answer any request, so use it in a Cloudflare Worker or behind a load balancer without session affinity. GET and DELETE answer 405 withAllow: POST.
The hosted Topolo endpoint runs this handler in stateless mode from the
Topolo-io/TopoloMCPHost Worker.
Shared local hub (@topolo/mcp/local)
Every stdio registration is its own Node process. A machine with many agent
threads and many pinned identities therefore runs threads x identities MCP
processes. topolo mcp serve (in @topolo/cli) replaces them with ONE
loopback process that mounts each identity at /mcp/<name>; topolo setup
--shared registers those URLs with Claude Code and Codex. See the CLI README
for setup, security and the exact host configuration.
This package supplies the per-identity handler the hub mounts:
import { createTopoloMcpLocalHandler } from '@topolo/mcp/local';
const handler = createTopoloMcpLocalHandler({
identity: { environment: 'production', context: 'org:org_topolo' },
agentName: 'codex',
});
const response = await handler.fetch(request); // Web-standard Request/Response- Same credential path as stdio. The identity resolves its credential from
the local store exactly as a stdio server pinned with
TOPOLO_ENV+TOPOLO_CONTEXTdoes (omitcontextonly when the environment has a single signed-in context; with several the handler refuses), including refresh before expiry and a forced refresh plus retry on 401. It never readsTOPOLO_API_KEY/TOPOLO_ACCESS_TOKEN,TOPOLO_CONTEXT,TOPOLO_AGENT_INSTALLATION_IDor request headers, so one process can serve many identities without any of them borrowing another's credential. - One handler per identity. A handler owns its identity's client, scopes and cache; nothing is shared between handler instances.
- Stateless Streamable HTTP. Each
POSTgets a fresh MCP server over the identity's cached ready state (re-resolved every five minutes, or afterinvalidate()), with JSON responses and noMcp-Session-Id, so a hub restart never strands a client on a lost session.GET/DELETEanswer 405. - Ended sign-in. As with stdio, an identity whose saved sign-in needs
re-authorization is not an outage: it serves
topolo_reauthorization_statusand every tool call returns a structuredreauth_requiredresult with the verification link, user code and sign-in command (SSE responses). Each request re-checks only that identity's approval. Once approved, requests get the full tool set; a call to the status tool answersreauthorizedand carriesnotifications/tools/list_changed, so the host re-lists without a restart. - Failures. A missing or rejected credential, or a public refusal such as an unpinned context with several signed in, answers 503 with its message; anything else answers a generic 500. Neither is cached.
- No transport security of its own. The hub that mounts the handler owns loopback binding, its bearer token and Host/Origin checks. Do not expose a local handler on a network interface.
Protected action handoff
Local stdio action results return topolo-secret:<id> references instead of
bearer credentials. Pass these references unchanged to subsequent published
actions; SDK 0.21.4 resolves nonempty string credentials internally using the OS protected store and
masks them in plans and diagnostics. Entries are bound to the verified
principal, organization, API key identity when present, and environment.
References expire after 30 minutes; accessing an expired entry deletes it.
Release finished workflow references with topolo_release_action_secret
and confirm: true. This deletes local storage only, not the upstream grant.
HTTP/Worker hosts remain redacted unless they explicitly configure a protected
SDK store; local references cannot be transferred to a different host.
Supported env vars
| Var | Purpose |
| ---------------------------- | ----------------------------------------------------- |
| TOPOLO_API_KEY | Platform API key (preferred) |
| TOPOLO_ACCESS_TOKEN | Short-lived JWT (dev/testing) |
| TOPOLO_ENV | Credential environment (production by default) |
| TOPOLO_CONTEXT | Pin one stored personal/org context without activating it |
| TOPOLO_AGENT_INSTALLATION_ID | Use a protected accountable agent installation |
| TOPOLO_AGENT_TASK_ID | Optional stable host task identifier |
| TOPOLO_AGENT_NAME | Human-readable agent label for audit logs |
| TOPOLO_SERVICE_URL_<ID> | Override a service base URL (e.g. _AUTH, _MAIL) |
An installation ID cannot be combined with a human context, API key, or access token. The MCP rejects ambiguous configuration instead of choosing one.
For concurrent organization or environment operation, run one named MCP server
per stored context. TOPOLO_CONTEXT accepts an exact key from topolo context
list --json (for example personal or org:org_topolo) or a unique stored
organization id, slug, or name. The server reads and refreshes that credential
without changing the CLI's active context, so separate agent processes cannot
race one shared context pointer.
No implicit default context. Without TOPOLO_CONTEXT, the server uses the
environment's stored context only while it is the only one signed in. Once
several contexts are signed in for the environment, it refuses to start and asks
for a pin, because the stored active context is just whichever sign-in happened
last:
{
"mcpServers": {
"topolo-topolo": {
"command": "topolo-mcp",
"env": { "TOPOLO_ENV": "production", "TOPOLO_CONTEXT": "org:org_topolo" }
},
"topolo-personal": {
"command": "topolo-mcp",
"env": { "TOPOLO_ENV": "production", "TOPOLO_CONTEXT": "personal" }
}
}
}Do not combine TOPOLO_CONTEXT with an injected API key or access token: those
credentials already carry their own immutable context.
Startup sequence
- Resolve a protected installation, injected credential, pinned stored context, or the environment's active stored credential. Refuse to start if none is available. Installation mode exchanges a short-lived Auth token bound to the MCP task, organization, and local DPoP key.
- Call
GET /api/meto load the user's granted scopes + role. - Connect the stdio transport and begin serving MCP requests.
- Advertise a fixed, bounded discovery/invocation surface. Agents search one application at a time and call exact action IDs; the server never expands one MCP tool per action or materializes the global catalog.
Startup is synchronous and fast (< 500 ms typical). If the credential is rejected, the process logs the error to stderr and exits non-zero — host clients surface that as a failed server launch.
Data privacy boundary
TopoloMCP is a transport adapter and does not persist customer data. It has no database, KV, R2, Durable Object, filesystem-write, analytics, or log-storage binding. Credentials remain in the caller-owned environment, shared CLI credential store, or operating-system protected installation store. Installation secrets and private keys are never copied into MCP host configuration. Every request is authorized by the target Topolo service.
Only messages raised by TopoloMCP's own input validators are returned verbatim. Authentication, SDK, provider, session-construction, and unexpected runtime errors are replaced with stable non-sensitive messages before they reach MCP responses or stderr. Target services remain responsible for retention, export, deletion, encryption, and audit storage for the data they own.
Tools
| Tool | Required scopes | Destructive hint |
| ---------------------------- | --------------------- | ---------------- |
| topolo_whoami | (none) | no |
| topolo_search_applications | (none) | no |
| topolo_get_application | (none) | no |
| topolo_get_resource_types | credential-scoped | no |
| topolo_list_resources | credential-scoped | no |
| topolo_search_actions | (credential-scoped) | no |
| topolo_discover_capabilities | credential-scoped | no |
| topolo_get_action | (credential-scoped) | no |
| topolo_get_action_examples | credential-scoped | no |
| topolo_validate_action | credential-scoped | no |
| topolo_plan_action | credential-scoped | no |
| topolo_prepare_action | credential-scoped | no |
| topolo_read_action | catalog or target policy | no |
| topolo_call_action | catalog or target policy | yes |
| topolo_run_agent | Agent runtime policy | yes |
| topolo_upload_action | catalog or target policy | yes |
| topolo_list_workspaces | target app runtime policy | no |
| topolo_create_workspace | target app runtime policy | yes |
| topolo_rename_workspace | target app runtime policy | yes |
| topolo_set_default_workspace | target app runtime policy | yes |
| topolo_api_call | (service-enforced) | yes |
Applications publish machine-action definitions through Topolo Developers. TopoloAuth projects those into a credential-scoped action catalog using the same service permissions already used for API keys and application access. MCP keeps six stable application/action tools regardless of whether the platform contains ten actions or one billion: search applications, search actions within one app, inspect exact applications/actions, safely invoke automatic reads, and invoke confirmation-gated actions.
Most actions use authorizationMode: static and are filtered by one declared
application permission before discovery. Operations whose authority depends on
request input (for example, organization admin OR grants:write in the target
application) use authorizationMode: runtime: the catalog exposes the typed
capability without inventing a global permission, and the target service checks
the current database-backed policy for the supplied organization/application on
every call.
Workspace lifecycle is first-class so agents do not have to know that Auth is
the canonical owner. The tools accept the target application, derive the
organization from the credential, and retain Auth's per-app runtime decision.
topolo_list_workspaces exposes policy.canCreate and policy.reason; the
write tools never infer access from organization ownership.
topolo_api_call is the escape hatch for endpoints without typed MCP tools
yet. GET requests work without confirmation; mutating HTTP methods must pass
confirm: true or the SDK rejects the call. Backing services still enforce
permissions for every request. The tool schema intentionally does not embed a
large service enum; agents should call topolo_search_applications first, then pass an
app ID/slug from that credential-scoped result. New services become callable
without an MCP release once Auth grants the organization and credential the
relevant permissions, and new actions become discoverable/callable without an
MCP release once the application publishes them.
topolo_read_action and topolo_call_action accept an optional paired
resourceType / resourceId context that scopes the call to one workspace (or
another cataloged resource alias). Workspaces are platform-owned — the record
lives once in TopoloAuth and a workspace belongs to exactly one app — so this
only selects which workspace the call is scoped to; it does not create or own
one. The SDK sends it as resource headers without changing the credential's
personal or organization context. This lets agents address many workspaces
independently; providing only one of the pair is rejected before the request is
sent.
topolo_run_agent is the bounded external-harness path for Codex, Claude, and
other MCP clients. It creates or replays one invocation using the caller's
stable idempotencyKey, polls the same invocation, and returns either its
terminal result or state: "approval_required" with the exact Topolo
verification URL. Calling it again with the same input resumes the durable
invocation instead of starting duplicate work.
Safety rails
- No
orgIdparameter on any tool. The organization is derived entirely from the credential by each backend app. There is no path for Org A's agent to see Org B's data. - Credential-bound context. OAuth tokens and refresh rotation retain their own personal/org context. CLI/MCP context switches do not move the user's web session, and browser switches cannot redirect a running agent credential.
- Scope-gated tool advertisement. Static actions are removed unless their permission is granted. Input-dependent runtime actions remain discoverable and are authorized by the target service for the concrete request.
- Credential-scoped catalog discovery.
topolo_search_applicationsuses TopoloAuth's live organization/permission catalog. They do not expose the global generated platform catalog. - Credential-scoped action discovery.
topolo_search_actionsreads only one app partition and returns at most 100 permission-filtered actions per page. Search, capability discovery, validation, and planning return compact agent guidance by default; passdetail: trueonly when full contracts are needed. - Audit headers. Every request sends
X-Topolo-Client: topolo-mcp/<ver>,X-Topolo-Agent: <label>,X-Topolo-Request-Id: <uuid>. - Write-action confirmation. The SDK refuses mutating HTTP methods unless
the call explicitly passes
confirm: true. Mutating action tools require the host client to approve before invocation.
Troubleshooting
"TOPOLO_API_KEY or TOPOLO_ACCESS_TOKEN must be set" — no credential reached the subprocess. In Claude Desktop, double-check the
envblock inclaude_desktop_config.json. In Claude Code, the server inherits your shell env, so make sure the var is exported in the profile your launcher reads."credential rejected" — token is expired or revoked. For API keys, mint a fresh one. For access tokens, re-run
topolo auth loginand copy the new token.Tool missing from the list — the credential doesn't grant the required scope. Check
topolo whoamito see the current scopes; either broaden the API key's permissions or switch to one that already has them.Tool call returns
Permission denied— the scope check on the backend disagrees with startup introspection (rare, but possible across scope changes). Re-spawn the server to refresh the cached scope set.Optional
--provenanceon npm publish once the repo is public.
