# Migrating MCP servers into swarmvia

A swarmvia MCP pool fronts one remote MCP server. The pool is the server's
client: it holds the OAuth connection or token, and callers reach the tools
through the swarmvia MCP server with `pool_list_tools` and `pool_call_tool`.
The harness keeps one MCP entry, swarmvia, instead of one per server.

Move one server at a time. Report first, change nothing until the human
agrees, and never move a secret through chat or an MCP argument.

## 1. Inventory

Read every config the harness uses. Project files override user files.

| Harness | Files | Servers live under |
|---------|-------|--------------------|
| Cursor | `.cursor/mcp.json`, `~/.cursor/mcp.json` | `mcpServers.<name>`: `url` + `headers`, or `command` + `args` |
| Claude Code | `.mcp.json`, `~/.claude.json` (top-level `mcpServers`, and `projects["<path>"].mcpServers`) | `mcpServers.<name>`: `type` `http`/`sse` + `url` + `headers`, or `command` |
| VS Code | `.vscode/mcp.json`, the user profile `mcp.json` | `servers.<name>`: `type` is required. Secrets come from `inputs` (`${input:id}`) |
| Codex | `.codex/config.toml`, `~/.codex/config.toml` | `[mcp_servers.<name>]`: `url`, `bearer_token_env_var`, `http_headers`, or `command` |
| Claude Desktop | `claude_desktop_config.json` (macOS `~/Library/Application Support/Claude/`, Windows `%APPDATA%\Claude\`) | stdio only. Remote servers appear as `npx mcp-remote <url>`. Connectors added in Settings are not in the file: ask the human to list them |
| Windsurf | `~/.codeium/windsurf/mcp_config.json` | `mcpServers.<name>.serverUrl` |
| Gemini CLI | `.gemini/settings.json`, `~/.gemini/settings.json` | `mcpServers.<name>`: `httpUrl` (streamable HTTP) or `url` (SSE) |

For each server, note its name, the file, the URL, and how it authenticates:
none, OAuth in the harness, or a static header. Write a header as
`Authorization (set)`. Never copy a token value into the report.

## 2. Verdicts

| Verdict | When |
|---------|------|
| **Migrate** | A public `https://` MCP server. Includes a stdio `mcp-remote https://…` wrapper: migrate the URL inside it. |
| **Migrate, token** | Same, but it sends a static token header. The pool gets the token from the human on the pool page, not from you. |
| **Keep local** | A stdio server that runs a local process (filesystem, git, shell, browser, docker). Any `localhost`, `127.0.0.1`, `*.local`, or private-network URL. A pool cannot reach them. |
| **Skip** | swarmvia itself. A server a granted pool already fronts: go straight to cut-over. |

SSE-only servers (`type: sse`, Gemini `url`, a path ending in `/sse`)
usually also serve streamable HTTP, often at `/mcp` on the same host. Try
that URL. If `create_pool` says it did not answer an MCP initialize
request, Keep.

For servers that act as a person (Linear, Notion, GitHub, Sentry), ask the
human: one shared account for everyone, or each member connects their own?
Each member is `each_member_connects: true`, and calls act as whoever calls.

Show the table and wait for the human to agree.

## 3. Move one server

1. Add the swarmvia MCP server if this session cannot call its tools. URL:
   `https://swarmvia.com/mcp`.
2. **`list_pools`.** A pool whose `mcp_server.url` matches is a reuse. Skip
   to step 4.
3. **`create_pool`** with `mcp_url` (and `each_member_connects` if chosen).
   It needs **This agent may create pools** and an owner or admin. It
   reuses a pool that already fronts the server. If it says the server
   needs an OAuth app (GitHub does), a team admin creates the pool in the
   dashboard (New pool → MCP server) with that app's client ID and secret.
   Wait, then `list_pools` again.
4. **Authorize.** Read the pool's `auth` block, or call **`connect_pool`**.
   - `ready` or `not_needed`: go on.
   - `connect_required`, `reconnect_required`, or `needs_credential`: give
     the human the `url`. They connect the account, or paste the server's
     token on that page. Call `connect_pool` again when they say done.
   - `who_can_fix: a team admin`: tell the human an admin has to do it.
   Never ask for the token, never read it out of the old config to send,
   and never pass one to `adopt_connection`. MCP pools own their
   credential, so `adopt_connection` refuses them.
5. **Verify.** `pool_list_tools` should show the tools the harness had. If
   some are missing, an admin trimmed the pool's tool list; say so. Call
   one `read-only` tool with `pool_call_tool` and show the result.

## 4. Cut over

Only after step 3 passes for that server.

1. Back up each file you touch next to it (`mcp.json.bak`). Show the diff.
   Remove only that server's entry. Leave the others and the swarmvia
   entry.
   - Claude Code: `claude mcp remove <name> -s <scope>` also works.
   - Codex: `codex mcp remove <name>`.
   - Claude Desktop connectors: the human removes them in Settings.
2. If a token for that server now lives only on the pool, tell the human
   they can delete the old one from `.env` or the harness `inputs`. Do not
   delete it for them.
3. Run the `use_pool` prompt for the pool and offer its lines for
   `AGENTS.md` (or `CLAUDE.md`, `.cursor/rules`). Future sessions then call
   `pool_call_tool(pool_id, name, arguments)` instead of the old tool names.
4. The harness reloads MCP config on restart. Say so.

To roll back, restore the backup. The pool can stay.

## Limits

- Tools only. Resources and prompts from the server do not pass through.
- A tool call holds one of the pool's concurrency slots until the server
  answers, and fails with `request_timeout` past the pool's request
  timeout. Keep servers whose tools run for minutes local.
- Creating a pool uses a plan slot. Prefer reuse.
