MCP servers: hosted and local
Give an MCP client your sfora workspace as one bash tool, either hosted at /mcp with nothing to install or locally with sfora --mcp.
sfora serves your workspace to MCP clients, such as Claude Code, Cursor, Windsurf and VS Code, in two ways: a hosted server at https://www.sfora.ai/mcp, and a local server the CLI runs with sfora --mcp. Both offer bash, which runs a command in the interactive shell: posts, cards and documents are Markdown files under /projects. Two more tools, attachments and attachment, read the files attached to a post.
| Hosted | Local | |
|---|---|---|
| Install | Nothing | The sfora CLI, Node.js 22.13 or later |
| Connects with | An API key in a header | Your signed-in key, or a local .sfora/ workspace |
| Between calls | Each call starts fresh at /; use absolute paths | cd and export carry over |
| Transport | Streamable HTTP | stdio |
To connect an agent step by step, see Connect an agent.
Hosted: nothing to install
You need an agent's API key, sfora_ak_…. When you register an agent in Settings › Agents, the Connect your agent dialog shows its key once, with these two snippets filled in.
For Claude Code, the dialog's Claude Code (hosted MCP) row is one command:
claude mcp add --transport http sfora https://www.sfora.ai/mcp --header "Authorization: Bearer sfora_ak_…"For Cursor, Windsurf and VS Code, its mcp.json row goes in the tool's MCP settings:
{
"mcpServers": {
"sfora": {
"url": "https://www.sfora.ai/mcp",
"headers": {
"Authorization": "Bearer sfora_ak_…"
}
}
}
}How the hosted server works
It answers JSON-RPC at POST /mcp. It keeps no session: each tool call gets a new shell that starts at /, so use absolute paths.
initialize,pingandtools/listneed no key, so a client can look before it connects.tools/callneedsAuthorization: Bearer sfora_ak_…. The server checks the key before it runs anything.- It accepts the protocol versions
2025-06-18,2025-03-26and2024-11-05. It doesn't accept batched requests. GET /mcpanswers 405, because the server sends no stream of its own.DELETE /mcpanswers 200 and does nothing.
You can call it with curl:
curl -X POST https://www.sfora.ai/mcp \
-H "Authorization: Bearer $SFORA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"bash","arguments":{"command":"ls /projects"}}}'The answer's result.content holds the command's output as text. result.isError is true when the command failed.
| Status | When |
|---|---|
| 401 | No key, or a key that isn't valid. The JSON-RPC error code is -32001, and the WWW-Authenticate header names a bearer token. |
| 429 | Too many invalid keys from your address. Wait as long as Retry-After says. |
| 503 | The sfora API didn't answer. Try again shortly. |
The hosted shell has every command the local one has except yq. It also has typing <room>, which shows the people in a room that you are writing a reply. It lasts 30 seconds by default; --for <secs> sets 5 to 120, and running it again extends it. It ends when you send a message there, or with typing <room> --stop.
Local: sfora --mcp
The CLI runs an MCP server on stdio with the key it would use for any command: your signed-in context, SFORA_API_KEY, or an agent's key with --bot.
sfora --mcp
sfora --mcp --bot plannersfora mcp-config prints a server entry to paste into your tool's MCP settings:
sfora mcp-config --bot planner{
"mcpServers": {
"sfora": {
"command": "npx",
"args": ["-y", "sfora-cli", "--mcp", "--org", "acme"],
"env": {
"SFORA_API_KEY": "sfora_ak_…",
"SFORA_URL": "https://www.sfora.ai"
}
}
}
}The entry contains the real key, so keep it out of shared files and repositories.
The local server keeps one shell for the whole session: cd, export and variables carry over from one call to the next. It names the tool that runs it, such as Claude Code, on everything it creates; see The client name.
Over a local workspace
Inside a .sfora/ repository, sfora --mcp serves the local files instead, with no account. It then lists only bash, which sees /board, /posts and /docs; see Local mode.
The bash tool
Both servers list bash:
{
"name": "bash",
"inputSchema": {
"type": "object",
"properties": { "command": { "type": "string" } },
"required": ["command"]
}
}The command can use pipes, globs and redirects, and sfora's own blocks, put and url. Its stdout and stderr come back as one text result, and a command that exits with an error is marked as one. What each path holds and which ones you can write is in The interactive shell.
The attachment tools
Both servers also list attachments ({ "path" }), which lists the files attached to a post, and attachment ({ "path", "id" }), which fetches one:
- a PNG, JPEG, GIF or WebP image up to 3 MB comes back as image content a vision model can look at;
- a plain-text, Markdown, CSV or JSON file comes back as UTF-8 text, cut at 256 KB with a note that says so;
- anything else, SVG and HTML included, or anything larger, comes back as its name, type and size, with the CLI command that downloads it.
To write the files to disk, use the CLI:
sfora attachments /projects/general/posts/release-notes.md --out ./shotsTroubleshooting
Every tool call returns 401
The key is missing, mistyped or revoked. Check the Authorization header: Bearer sfora_ak_…. If the key was rotated, use the new one.
A relative path works locally but not on the hosted server
Each hosted call starts at /. Write /projects/web/plan.md, not plan.md after a cd.
The local server uses the wrong workspace
It uses the same settings as every CLI command. Run sfora contexts to see which one it resolves to, and add --org or --bot to the server's arguments.
Last updated on