How to connect your AI agent to Seal with MCP
You connect an AI agent to Seal by pointing any MCP client at the remote server at https://www.sealmd.net/api/mcp with a Seal API token — one config block, no local process to run. Once connected, the agent can publish Markdown documents, watch for human comments and suggestions, reply, mark feedback addressed, and ship revised versions — a real agent–human review loop. The one thing it can never do is approve: sign-off is always a verified human in the browser. Here’s the setup.
What is the Seal MCP server?
It’s a remote MCP endpoint — streamable HTTP, stateless JSON-RPC 2.0 over POST — at https://www.sealmd.net/api/mcp. There are no SSE sessions (GET returns 405), so any MCP client works: Claude Code, Claude Desktop, Cursor, Windsurf. Every tools/call is authenticated with your API token and checked against the token’s scopes. Seal isn’t a Markdown editor — your agent writes the file; the MCP surface is how it hands each version to humans for review and hears back.
How do I get an API token?
In the web app: Settings → API tokens. The token is shown once, so store it immediately. An expiry is required (90 days maximum), you can revoke it instantly, and it’s scoped:read:shared by default, plus publish, comment, and audit:export as needed. An approve scope does not exist for any token.
How do I add Seal to my MCP client?
For Claude Desktop, Cursor, and similar clients, add this to your MCP config:
{ "mcpServers": { "seal": {
"type": "http",
"url": "https://www.sealmd.net/api/mcp",
"headers": { "Authorization": "Bearer <seal_api_token>" }
} } }For Claude Code it’s a one-liner:
claude mcp add --transport http seal https://www.sealmd.net/api/mcp --header "Authorization: Bearer <seal_api_token>"Windsurf is the one exception: its config file (~/.codeium/windsurf/mcp_config.json) uses serverUrl instead of url — otherwise the same block. Cursor uses ~/.cursor/mcp.json. Claude Desktop / claude.ai need OAuth (not yet), so bridge them with npx mcp-remote, keeping the whole "Authorization: Bearer <token>" as one argument.
Then verify:
- Verify the connection. Ask your agent to call seal_list_docs. It returns your documents with state, stage, and who each one is waiting on — if that works, the token and endpoint are wired correctly.
- Publish and run the feedback loop. The agent calls seal_publish to upload Markdown as a new immutable version, humans comment and suggest on the web, and the agent polls seal_feed, reads seal_get_feedback, revises its local file, replies with seal_reply or seal_mark_addressed, and re-publishes.
What tools does the Seal MCP server expose?
Nine tools — and one deliberate absence:
seal_list_docs— your docs with state, stage, and who each is waiting on.seal_publish— upload Markdown as a new immutable version; the agent’s only edit path. Identical content dedupes; supports an optional idempotency key. Needspublish.seal_doc_info— version count, last publish, content hash, comment counts.seal_feed— cheap change detector (counts + last activity); poll this, not the heavy calls.seal_get_feedback— open comments and suggestions.seal_get_blocks— block ids mapped to the exact text of the published version.seal_reply— reply in a thread, audit-taggedvia:'agent'. Needscomment(orpublish).seal_mark_addressed— resolve or reopen a comment.seal_export_audit— offline-verifiable evidence bundle. Needsaudit:export.seal_approve— does not exist, ever. Approval is a verified-human browser action, pinned by test.
Also deliberately absent: tools to create suggestions or to accept/reject them — human curation stays human — and GitHub connector operations.
How does the agent–human feedback loop work?
The agent publishes the doc with seal_publish. Humans read, comment, and suggest changes on the web — including via a no-login share link. The agent polls seal_feed until activity appears, pulls the detail with seal_get_feedback, revises its local Markdown, answers threads with seal_reply, resolves them with seal_mark_addressed, and re-publishes a new version. When the humans are satisfied, one of them approves in the browser — bound to the exact content hash of that version. If the doc changes afterward, the approval goes stale automatically. That’s the whole contract: agents iterate, humans sign off. See how approval works for the sign-off half.
What errors will I hit, and how do I fix them?
Every failure is legible — the message names the fix. The common ones:
| Symptom | Fix |
|---|---|
401 invalid_token | Token revoked, expired, or mistyped — the three are indistinguishable by design. Tokens are one string starting with seal_sk_; re-copy the whole value, or mint a fresh one. |
401 missing_token | No Authorization: Bearer header reached the server — check your client forwards it (Windsurf uses serverUrl, not url). |
403 insufficient_scope | The token lacks a scope the tool needs (named in the message). Scopes are fixed at creation — mint a new token with them. |
403 inactive_principal / insufficient_role | The token’s creator was suspended, removed, or downgraded, so their tokens are inert. Ask a workspace owner/admin to mint a new one. |
400 missing_source | A publish (or per-doc call) with neither source nor source_id. Pass source (a new name creates the document) or source_id. |
400 invalid_source_id | You put a document name in source_id (a uuid field). Pass the name as source instead. |
404 source_not_found | The source name or source_id isn’t in this workspace, or the token is bound to a different workspace. Use seal_list_docs to check; confirm which workspace the token was minted for. |
413 markdown_too_large | A publish body over the 1 MB cap. Split or trim the document — the limit is on the published Markdown bytes. |
405 on connect | Your client probed GET/SSE; this is a POST-only server. Expected — the client falls back to POST. |
429 rate_limited | Per-IP ceiling hit (reads 120/min, publish 20/min, comment 60/min, export 6/min); agents behind one shared IP share it. Back off using Retry-After (also error.data.retry_after_s); poll seal_feed between changes. |
Can my agent approve documents?
No, by design. There is no seal_approve tool, no approve scope exists for any API token, and this is pinned by a test. Approval is always a verified human clicking approve in the browser, bound to the exact content hash of the wording they saw. The agent does everything else — publish, read feedback, reply, resolve — but sign-off stays human.
Which MCP clients work with the Seal MCP server?
Any MCP client that speaks streamable HTTP: Claude Code, Claude Desktop, Cursor, and Windsurf all work. The endpoint is stateless JSON-RPC 2.0 over POST at https://www.sealmd.net/api/mcp — there are no SSE sessions to manage (GET returns 405), so there is nothing client-specific to configure beyond the URL and the Authorization header.
What scopes do I need?
read:shared is the default and covers listing docs and reading feedback. Add publish so the agent can upload new versions (publish also covers replying), comment for seal_reply on its own, and audit:export only if the agent exports evidence bundles with seal_export_audit. There is deliberately no approve scope.
Is MCP available in the free local plugin?
No — MCP is a hosted-only surface. The free open-source plugin (sealmd) reviews Markdown locally and shares review state via git or a zip bundle instead; the doc.seal.md sidecar is committed and collaborators pull it. If your agent needs a live remote feedback loop, that is what the hosted MCP endpoint is for.
Related: MCP setup reference & troubleshooting · Getting started with hosted Seal · How approval works · Set up the free local plugin · Seal vs GitHub PR review