DorkOS
Integrations

MCP Server

Connect external MCP clients to DorkOS, and render MCP Apps from other servers inline in chat

MCP Server

DorkOS exposes an external MCP server at /mcp so agents running outside DorkOS, standalone Claude Code, Cursor, Windsurf, or any MCP-compatible client, can call the same tools and read the same data DorkOS's own agents use. Separately, DorkOS also acts as an MCP host: when a tool from any MCP server returns an interactive ui:// app (the MCP Apps extension, SEP-1865), DorkOS renders it inline in the chat, sandboxed.

These are two directions of the same protocol, and fittingly symmetric ones: DorkOS as a server other agents connect to, and DorkOS as a host that renders apps other servers ship. This page covers both.

Quick win: flip on Settings → Advanced → Tools → Connect other apps to DorkOS, copy the Claude Code snippet it generates, and paste it into ~/.claude.json. That's the whole setup for the common case. The rest of this page is the reference.

Connecting to the DorkOS MCP server

The endpoint is /mcp on your DorkOS server (http://localhost:4242/mcp by default). It speaks Streamable HTTP and is stateless: every request gets a fresh server instance. There's no session to keep alive and no GET/DELETE lifecycle to manage (both return 405).

Manage it from Settings → Advanced → Tools → Connect other apps to DorkOS: an enable/disable toggle, the live endpoint URL, current auth mode, rate limiting, and copy-pasteable setup snippets for Claude Code, the Claude Code CLI, Cursor, and Windsurf.

Copy the endpoint, don't type it. Every example on this page uses http://localhost:4242, which is right for the CLI and for Docker. The desktop app uses that address too, unless something else on your machine already had port 4242, in which case it moves to the next free one. Settings → Advanced → Tools → Connect other apps to DorkOS and Settings → Advanced → Server both show your real endpoint with a copy button, so copying from there always works.

A screenshot of the Settings → Advanced → Tools → Connect other apps to DorkOS card would help here (toggle, endpoint URL, auth mode, rate limit, and snippets in one view). Flagged for the media pipeline.

Don't add this to agents already running inside DorkOS. DorkOS gives its own managed agents these tools internally. Configuring the external MCP endpoint for one of those same agents registers every tool twice: the Anthropic API rejects the duplicate names with an HTTP 400 ("Tool names must be unique") and every tool call fails. External MCP access is for agents running outside DorkOS.

Authentication

How you authenticate depends on whether you've turned on login, and on where your client runs. A client on another device (through your tunnel, over Wi-Fi or through a proxy) always needs a per-user API key; the local token and the no-token checks below are for clients on this computer.

Login off (the default), on this computer. DorkOS protects the tools that change things on your machine with a local token it makes for you. You paste this token into your MCP client once, as a Bearer header. Read-only checks (listing tools, ping, get_server_info) still work with no token, so a quick health probe needs no setup:

# Read-only: no token needed
curl -X POST http://localhost:4242/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

Any tool that changes state (creating an agent, sending a Relay message, installing from the marketplace) needs the token. Without it, the call comes back with a 401 that tells you where to find the token:

# A tool that changes things: token required
curl -X POST http://localhost:4242/mcp \
  -H "Authorization: Bearer dork_mcp_local_..." \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"create_agent","arguments":{}}}'

Find your token in Settings → Advanced → Tools → Connect other apps to DorkOS: click Reveal token, and the setup snippets fill in with it so you can copy one and paste it straight into your client. It also lives in a file called mcp-local-token in your DorkOS data folder (~/.dork/ by default). Rotating the token from Settings makes a new one and turns off the old one right away, so any client you already set up stops working until you paste in the new token.

One honest note on what this protects: it stops tokenless calls, keeps the token out of reach of anything that can't open a local connection, and guards the endpoint anywhere the port is reachable from outside (a tunnel or proxy). While login is off, though, a program running on your computer can still ask DorkOS for the token the same way the app does. If that matters for your machine, turn on login — that is the boundary that closes it.

A bare GET to /mcp always returns 405, with or without a token. To confirm auth is working, POST a real JSON-RPC request like the ones above.

Login on. Once you turn on login, MCP clients authenticate with a personal API key instead, created and revoked from Settings → Login & security → API keys and sent as a Bearer token. The local token steps aside; per-user keys take over. See Securing Your Instance for the full flow.

Headless deployments (CI, servers no one signs into): set the MCP_API_KEY environment variable. It's a static override that every client uses as its Bearer token; it wins over the local token, and it can only be changed in the environment, never revoked from the UI. It's for setups that leave login off — turn login on and the login gate answers /mcp before this key is ever considered, so clients need a personal API key instead.

Client configuration

Example for Claude Code's ~/.claude.json (the same shape Settings generates for you, with your key filled in):

{
  "mcpServers": {
    "dorkos-external": {
      "type": "http",
      "url": "http://localhost:4242/mcp",
      "headers": { "Authorization": "Bearer dork_..." }
    }
  }
}

Or register it from the CLI:

claude mcp add-json dorkos-external '{"type":"http","url":"http://localhost:4242/mcp","headers":{"Authorization":"Bearer dork_..."}}'

Cursor and Windsurf use the same url + headers shape under their own config file (mcp.json, mcp_config.json). See Settings for the exact snippet.

Rate limiting is on by default (60 requests per 60-second window) and configurable from the same settings card. Changes take effect after a server restart.

What it exposes

Connecting gives a client DorkOS's full external tool surface: 70 tools in total, covering sessions, account usage, scheduled tasks, Relay delivery and tracing, agent bindings, Mesh discovery, agent/extension management, the operator surface, agent memory, agent promises, agent heartbeats, agent permissions, document events and live state, and the marketplace. Seven read-only resources round it out, for browsing sessions, agents, skills, and the capability catalog without a tool round-trip.

DomainToolsExamples
Core4ping, get_server_info, get_session_count, get_agent
Accounts2accounts_usage, accounts_probe
Sessions1session_start
Scheduled tasks5tasks_list, tasks_create, tasks_update, tasks_delete, tasks_get_run_history
Relay7relay_list_endpoints, relay_list_adapters, relay_get_trace, relay_get_metrics
Bindings3binding_list, binding_create, binding_delete
Mesh (discovery)8mesh_discover, mesh_register, mesh_list, mesh_status, mesh_query_topology
Agents & extensions7create_agent, list_extensions, create_extension, test_extension
Operator11activity_list, config_get, config_patch, check_update, feedback_draft
Memory2memory_write, memory_read
Commitments3commitment_add, commitment_update, commitments_list
Heartbeats1heartbeat_status
Permissions3request_permission, list_my_permissions, change_permission
Self-description1list_capabilities
Document events2canvas_send, canvas_patch_state
Marketplace10marketplace_search, marketplace_install, marketplace_update, marketplace_link

The last eight rows (operator, memory, commitments, heartbeats, permissions, self-description, document events, marketplace: 33 tools) are generated from the Capability Registry, so they are reachable by id through dorkos call. The other 37 are registered directly on this server and are not. list_capabilities says which is which, so a client never has to infer it.

Being off the registry does not mean running unchecked. All 37 of them carry a permission tier too, and the same approval gate reads it: tasks_delete and mesh_unregister are destructive. On a Trusted install (the default), a registered agent's own token runs them and DorkOS records it, the same as any other in-circle destructive action with an Allowed area; on Careful, or from a caller DorkOS cannot verify as one of its own agents, each answers with a pending approval instead of doing the work. A destructive tool advertises an approvalToken argument, so a client can tell one from its own schema without asking.

The tools agents use to message each other's chats (chat_send, chat_read, chat_stop) are not on this server. Each one acts as the chat that calls it, and a client connected from outside has no chat to act as.

Inside a DorkOS session, Claude Code is also told which tier a tool sits at and that the call already passed this check, so its auto mode stops asking about things DorkOS has already decided. This server gets no equivalent: it has no session, so there is nothing to tell. Your client makes its own decisions about when to ask you, exactly as it does today.

The 10 marketplace_* tools only appear when Relay is enabled and extensions have initialized successfully. Without that, the marketplace branch is silently skipped and the server still boots with the other 55. On a Trusted install, marketplace_install, marketplace_uninstall, and marketplace_update with apply: true run straight through from a source you have already configured, recorded rather than approved; on Careful, each returns a pending approval with a token instead, and a person decides it in the DorkOS UI before the client retries with the token. Without apply, marketplace_update only says which packages have a newer version. marketplace_create_package always returns a pending approval, on either preset, since it puts new code on your machine for the first time. marketplace_link, which runs a package from a folder on your computer, is destructive-tier with no setting that can approve it ahead of time: every call waits for you.

Every tool's full name, description, and input schema is visible from any connected MCP client's tool list. This table is a map of the territory, not the source of truth.

Tool annotations & structured output

Every tool is registered with MCP tool annotations: readOnlyHint, destructiveHint, idempotentHint, and openWorldHint. A client can reason about what a tool does before calling it:

  • readOnlyHint: the tool only reads state; it never mutates anything.
  • destructiveHint: the tool deletes or unregisters a resource.
  • idempotentHint: calling it again with the same input converges rather than compounding. A second mesh_register call for the same path adopts the manifest the first one wrote instead of minting a second agent, so registration is idempotent, and so is a second mesh_deny for the same path.
  • openWorldHint: the tool reaches an external system (a configured marketplace source, an external adapter) rather than only local DorkOS state.

Most tools return plain text content, matching the underlying handler. A handful of read/list tools also declare a Zod outputSchema and return matching structuredContent, so a client can parse the result directly instead of scraping text:

  • get_agent
  • accounts_usage
  • mesh_list
  • mesh_status
  • mesh_inspect
  • mesh_query_topology
  • relay_get_metrics
  • tasks_list

Resources

Alongside tools, the server registers seven read-only dorkos:// resources: four list views and three id-scoped templates.

URIKindReturns
dorkos://sessionsListSession metadata (id, title, runtime, cwd, updatedAt) across every registered runtime, with per-runtime degradation warnings. No transcript content.
dorkos://sessions/{id}TemplateMetadata for one session by id.
dorkos://agentsListEvery agent manifest registered with Mesh, the same data mesh_list returns.
dorkos://agents/{id}TemplateA single agent manifest (.dork/agent.json content) by agent ULID.
dorkos://skillsListSkills under the server's default project's .agents/skills, including ones that came from an installed package: name, description, kind only. Skills marked disable-model-invocation are left out.
dorkos://skills/{name}TemplateFull SKILL.md frontmatter and body for one skill, by name.
dorkos://capabilitiesListThe live capability catalog: id, title, description, permission tier, input/output schema, and surfaces for every capability on the registry.

Session and skill resources are scoped to the server's default working directory; the external surface has no per-request cwd parameter to key on. The session and agent lists are the complete enumeration for their kind, so the item templates aren't re-listed separately; a client reads those once it already has an id or name. The skill list has one deliberate gap: a skill whose author wrote disable-model-invocation: true meant "do not reach for this on your own", so it is not advertised there. Asking for it by name still works — dorkos://skills/{name} serves every skill, because naming one is your choice, not the model's. dorkos://capabilities is the exception: it enumerates the capability registry, not the tool list, so the 43 tools registered directly on this server are absent from it. The resource's own description says so, so a client reading the list does not have to infer the gap.

The server is stateless per request (a fresh instance is built for every /mcp call), so it never advertises resources/listChanged or supports resources/subscribe. There's no long-lived connection to push a notification down.

MCP Apps host

Separately from the tools above, DorkOS can act as an MCP Apps host, which is a fancier way of saying: when a tool from any connected MCP server returns a ui:// app reference, DorkOS renders that app inline in the chat as a live, sandboxed panel. No configuration needed beyond having the MCP server connected to your session.

Works with Claude Code sessions today. The Codex and OpenCode runtimes don't surface MCP Apps yet. Sessions on those runtimes never show the renderer, regardless of what the connected MCP server ships.

How it renders

The first app from a given MCP server shows a consent card before anything renders:

Interactive app provided by <server name>. This runs sandboxed code from the MCP server. It cannot access your session, files, or credentials. Render it?

A screenshot of this consent card, plus a rendered app's fullscreen control, would make the behavior obvious at a glance. Flagged for the media pipeline.

Clicking Render app remembers that choice for the server (stored per-server, so a trusted server never asks twice) and renders the app inline in the message. Leaving the card alone simply skips the app for now. A fullscreen control on a rendered app pops it out to the full canvas for apps that need more room. A pop out control keeps a live app in view while you move around DorkOS: on a computer it floats in a small window that stays on top, and on a phone it docks to a bottom sheet you can drag up to use, or drag down to tuck into a small bar at the bottom; tap the bar to bring it back. These are all backed by the same sandboxed frame.

Security model

Apps run in a strict-sandbox iframe (sandbox="allow-scripts", deliberately never allow-same-origin), so they get an opaque origin and can only talk to DorkOS through a validated postMessage bridge:

  • No access to your session, files, cookies, or credentials. The opaque origin can't reach host storage or the parent DOM.
  • A Content-Security-Policy is enforced: the app's own declared policy if it ships one, otherwise a locked-down default (default-src 'none', inline scripts/styles from 'self' only, no network, no framing).
  • External links go through the same confirmation dialog used everywhere else in DorkOS before a link opens in a new tab.
  • Apps cannot run tools. A tools/call request from an app is refused with a spec-compliant JSON-RPC error: v1 is render-only.
  • Feature permissions (camera, microphone, geolocation, clipboard) are opt-in per app, granted only for what the app explicitly declared, and otherwise absent from the frame entirely.

The app's HTML itself is never trusted from the tool-call text the model sees. DorkOS's server opens its own short-lived MCP connection to fetch the resource directly by URI, recovering the correct mime type, CSP, and permissions before anything reaches the iframe. That fetch is exposed as its own endpoint; see the API reference for the request/response shape.

Next Steps

Document tools inside a runtime turn

A DorkOS-hosted Claude Code turn supplies its verified, live runtime identity to configure_doc_channel, approve_doc_route, revoke_doc_route, canvas_patch_state and canvas_send. Codex and OpenCode use the authenticated internal runtime MCP listener for the same five operations. A tool argument cannot choose the agent, session, installation or permissions.

An agent may configure its own document route and approve its own route where the existing document policy permits it. It may patch state or send a downstream event only on a document it may write, including a room canvas while it is a current room member. Other targets still require their existing exact approval; this identity projection grants no new route, operator permission or file-write approval. Missing or retired turn authority and lost membership are refused. The recorded opener may also call revoke_doc_route to stop its own route; another member cannot revoke a different opener’s grant.

Room app acknowledgments remain unavailable in this delivery. A room-scoped canvas_send with type app.ack returns ROOM_APP_ACK_RESPONDER_UNAVAILABLE (409 at the document service); it does not record an acknowledgment or mark inputs handled. Membership and a successful state patch do not prove which active room turn was admitted to respond to an app event. Until that correlation is installed and verified, consumers must keep those inputs unacknowledged rather than report them handled or automatically repeat the effect. Ordinary downstream sends and state patches do not settle app inputs or start a new turn.