# MCP clients: Claude Desktop, Cursor, VS Code, Claude Code

Source: https://immiscible.fly.dev/docs/sdks/integrations/mcp

# MCP clients: Claude Desktop, Cursor, VS Code, Claude Code

Immiscible speaks MCP (Streamable HTTP, JSON-RPC 2.0, protocol `2025-06-18`) at two kinds of address:

| Address | What it is | Who uses it |
|---|---|---|
| `<base>/mcp` | Immiscible's own server: `authorize_action`, `request_payment`, `request_personal_data`, `check_action_status`, `explain_decision`, `settle_action` | an agent that asks before it acts, and wants receipts |
| `<base>/mcp/proxy/<upstream id>` | the MCP proxy: a tool server you registered under **Settings**, **Tools (MCP)** in the console, with Immiscible holding its credential | an agent that should reach a tool server only through the gate |

Through the proxy there is no path to the tool that skips the gate: every `tools/call` is authorised first, an approval pauses the call (nothing reaches the upstream), and a refusal comes back as an error with the reasons. The agent only sees the tools its mandates could allow.

Authentication is `Authorization: Bearer <agent key>`. For `<base>/mcp`, apps that add connectors by URL can sign in with OAuth instead (no key to copy; see [Claude and ChatGPT as connectors](https://immiscible.fly.dev/docs/guides/mcp-proxy.md#claude-and-chatgpt-as-connectors)).

Every file below is in [`docs/sdk/integrations/mcp/`](https://github.com/efr7-7/immiscible/blob/main/docs/sdk/integrations/mcp/). They point at the hosted service; if you run your own server, replace `https://immiscible.fly.dev` with its address and `ups_REPLACE_ME` with the upstream id from the console.

## Claude Desktop

**Immiscible's own server**, by OAuth: **Settings**, **Connectors**, **Add custom connector**, paste `<base>/mcp`, **Connect**. You sign in and choose which agent Claude acts as.

**A proxied tool server**: Claude Desktop's config file runs local (stdio) servers, so bridge to the remote proxy with `mcp-remote`, which forwards the header. `claude_desktop_config.json` (macOS `~/Library/Application Support/Claude/`, Windows `%APPDATA%\Claude\`):

```json
{
  "mcpServers": {
    "immiscible-shop": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://immiscible.fly.dev/mcp/proxy/ups_REPLACE_ME", "--header", "Authorization:${IMMISCIBLE_AUTH}"],
      "env": { "IMMISCIBLE_AUTH": "Bearer ask_REPLACE_WITH_YOUR_AGENT_KEY" }
    }
  }
}
```

The header is passed through an environment variable with no space after the colon because some platforms split arguments on spaces. File: [`claude_desktop_config.json`](https://github.com/efr7-7/immiscible/blob/main/docs/sdk/integrations/mcp/claude_desktop_config.json). Restart Claude Desktop after editing.

## Cursor

`~/.cursor/mcp.json` for every project, or `.cursor/mcp.json` in one:

```json
{
  "mcpServers": {
    "immiscible": {
      "url": "https://immiscible.fly.dev/mcp",
      "headers": { "Authorization": "Bearer ${env:IMMISCIBLE_AGENT_KEY}" }
    },
    "immiscible-shop": {
      "url": "https://immiscible.fly.dev/mcp/proxy/ups_REPLACE_ME",
      "headers": { "Authorization": "Bearer ${env:IMMISCIBLE_AGENT_KEY}" }
    }
  }
}
```

`${env:...}` reads the key from Cursor's environment; on versions without interpolation, write the key in place and keep the file out of version control. File: [`cursor-mcp.json`](https://github.com/efr7-7/immiscible/blob/main/docs/sdk/integrations/mcp/cursor-mcp.json).

## VS Code

`.vscode/mcp.json` in the workspace (or **MCP: Open User Configuration**). VS Code prompts for the key once and stores it securely:

```json
{
  "inputs": [
    { "type": "promptString", "id": "immiscible-agent-key", "description": "Immiscible agent key", "password": true }
  ],
  "servers": {
    "immiscible": {
      "type": "http",
      "url": "https://immiscible.fly.dev/mcp",
      "headers": { "Authorization": "Bearer ${input:immiscible-agent-key}" }
    },
    "immiscible-shop": {
      "type": "http",
      "url": "https://immiscible.fly.dev/mcp/proxy/ups_REPLACE_ME",
      "headers": { "Authorization": "Bearer ${input:immiscible-agent-key}" }
    }
  }
}
```

File: [`vscode-mcp.json`](https://github.com/efr7-7/immiscible/blob/main/docs/sdk/integrations/mcp/vscode-mcp.json).

## Claude Code

```shell
claude mcp add --transport http immiscible https://immiscible.fly.dev/mcp --header "Authorization: Bearer $IMMISCIBLE_AGENT_KEY"
claude mcp add --transport http immiscible-shop https://immiscible.fly.dev/mcp/proxy/ups_REPLACE_ME --header "Authorization: Bearer $IMMISCIBLE_AGENT_KEY"
```

Or check a `.mcp.json` into the project (Claude Code expands `${IMMISCIBLE_AGENT_KEY}` from the environment, so the key is not in the file): [`claude-code-mcp.json`](https://github.com/efr7-7/immiscible/blob/main/docs/sdk/integrations/mcp/claude-code-mcp.json). MCP tools run when the model chooses to call them; for a check on every tool call whatever the model chooses, add the [PreToolUse hook](https://immiscible.fly.dev/docs/sdks/integrations/claude-code.md) too.

## What the model sees

| | `<base>/mcp` tools | through the proxy |
|---|---|---|
| allowed | the decision, with the receipt in the text and in `structuredContent` | the upstream tool's own result |
| a person must approve | "A person has been asked. Poll check_action_status ..." | a tool error with the approval link and `structuredContent.decision: "approval_required"`; nothing was sent upstream |
| refused | the decision with reasons, and "Do not proceed. Tell the person why." | a JSON-RPC error (`-32003`) with the reasons |

After a person approves a proxied call, retry the same tool with the same arguments and `_meta: { "immiscible/approvalId": "apr_..." }`; the proxy forwards it exactly once. The SDKs do this for you: `mcpProxy(id).callWithApproval(tool, args)` and `mcp_proxy(id).call_with_approval(tool, args)`.

## Check a connection

```shell
curl -s "$IMMISCIBLE_URL/mcp" -H "authorization: Bearer $IMMISCIBLE_AGENT_KEY" -H "content-type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
```

The SDK integration test (`packages/immiscible-js/test/integration.test.mjs`) does the same against the real server: `initialize`, then `tools/list` with an agent key.
