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.

HostedLocal
InstallNothingThe sfora CLI, Node.js 22.13 or later
Connects withAn API key in a headerYour signed-in key, or a local .sfora/ workspace
Between callsEach call starts fresh at /; use absolute pathscd and export carry over
TransportStreamable HTTPstdio

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:

mcp.json
{
  "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, ping and tools/list need no key, so a client can look before it connects.
  • tools/call needs Authorization: Bearer sfora_ak_…. The server checks the key before it runs anything.
  • It accepts the protocol versions 2025-06-18, 2025-03-26 and 2024-11-05. It doesn't accept batched requests.
  • GET /mcp answers 405, because the server sends no stream of its own. DELETE /mcp answers 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.

StatusWhen
401No key, or a key that isn't valid. The JSON-RPC error code is -32001, and the WWW-Authenticate header names a bearer token.
429Too many invalid keys from your address. Wait as long as Retry-After says.
503The 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 planner

sfora 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 ./shots

Troubleshooting

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