Cobalt Docs

MCP Server

Connect AI agents to your financial data using the Model Context Protocol.

What is the Cobalt MCP server?

A hosted Model Context Protocol (MCP) server that gives AI agents programmatic access to your financial data — accounts, transactions, investments, brokerage positions, tags, and market research.

Instead of one-tool-per-endpoint, the server exposes a single cobalt_execute_code tool. The agent writes a short JavaScript program that calls the cobalt.* SDK (accounts, transactions, brokerage, tags, research) and the program runs in a sandboxed V8 isolate. One turn replaces what used to take many round-trip tool calls.

  • Authenticated via OAuth — the access token is bound to one Cobalt user.
  • All cobalt.* calls are auto-scoped to that user; sandboxed code cannot supply or override userId.
  • Mostly read; cobalt.transactions.update and the cobalt.tags.* mutators are the only write paths.
  • Accessible from any MCP client that supports Streamable HTTP transport.

Server URL:

https://api.cobaltpf.com/api/mcp

Quick start

Authentication

The MCP server uses OAuth via Better Auth. When you connect for the first time, your client will redirect you to sign in with your Cobalt account.

  • Each client (Cursor, Claude Code, VS Code, etc.) registers as an OAuth application with Cobalt.
  • After sign-in, the OAuth token is cached by your client.
  • All queries are automatically scoped to your user via row-level security — you can only access your own financial data.

Security

  • Code runs in a Cloudflare V8 isolate — no filesystem, no network, no access to host secrets. Only the cobalt.* SDK is exposed.
  • The OAuth subject is captured server-side and injected into every cobalt.* call. Sandboxed code cannot pass or spoof a userId.
  • Execution timeout is 180 seconds.
  • Write surface is limited to cobalt.transactions.update (patch only — cannot create) and cobalt.tags.* (create / update / archive / attach to transactions). Everything else is read-only.

cobalt.transactions.update and cobalt.tags.* can modify your data. Review what your agent plans to run before approving destructive edits. Tag deletes are soft (archived), so history is preserved.

Installation instructions

Claude

Works the same on both Claude.ai (Pro/Max) and Claude Desktop.

Go to Settings → Connectors → Add Custom Connector

Fill in:

  • Name: Cobalt
  • Remote MCP Server URL: https://api.cobaltpf.com/api/mcp

Click Add, then follow the connection steps to sign in with your Cobalt account

ChatGPT

Go to Settings → Apps → Create app

Fill in:

  • Name: Cobalt
  • Description: Tools for Financial Data
  • MCP Server URL: https://api.cobaltpf.com/api/mcp
Make sure OAuth is selected as the authentication method
Click Create, then follow the connection steps to sign in with your Cobalt account

Cursor

Cursor — One-click install

Click to install the Cobalt MCP server in Cursor.

Manual installation:

Open the command palette and type "Cursor Settings"

Under "Tools & MCP" click "New MCP Server"

Paste the following JSON into the configuration file:

{
  "mcpServers": {
    "cobalt": {
      "url": "https://api.cobaltpf.com/api/mcp"
    }
  }
}

Save the file. Cursor will prompt you to authenticate — follow the browser flow to sign in with your Cobalt account.

You may need to restart Cursor to load the new configuration.

VS Code

VS Code — One-click install

Click to install the Cobalt MCP server in VS Code.

Manual installation:

Open the Command Palette (Ctrl+Shift+P on Windows/Linux, Cmd+Shift+P on macOS)

Type "MCP: Add Server" and select HTTP

Enter the URL https://api.cobaltpf.com/api/mcp and name it Cobalt

Start the server via "MCP: List Servers" > select Cobalt > "Start Server"

When prompted to authenticate, click "Allow" and complete the sign-in flow in your browser.

Claude Code

Ensure Claude Code is installed:

claude --version

Add the Cobalt MCP server:

claude mcp add --transport http cobalt https://api.cobaltpf.com/api/mcp

Start Claude Code and verify:

claude
/mcp

You should see cobalt listed. If it shows "needs authentication", select it and follow the browser flow.

OpenCode

Ensure OpenCode is installed:

opencode --version

Add the server to your configuration file:

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "cobalt": {
      "type": "remote",
      "url": "https://api.cobaltpf.com/api/mcp"
    }
  }
}

Authenticate:

opencode mcp auth cobalt

Verify connection:

opencode
/mcp

Codex CLI

Ensure Codex CLI is installed:

codex --version

Add the Cobalt MCP server:

codex mcp add cobalt --url https://api.cobaltpf.com/api/mcp

You will be prompted to authenticate immediately.

Verify:

codex
/mcp

Gemini CLI

Ensure Gemini CLI is installed:

gemini --version

Add the Cobalt MCP server:

gemini mcp add --transport http cobalt https://api.cobaltpf.com/api/mcp

Authenticate:

gemini
/mcp auth cobalt

Verify:

/mcp list

Amp CLI

Ensure Amp CLI is installed:

amp --version

Add the Cobalt MCP server:

amp mcp add cobalt https://api.cobaltpf.com/api/mcp

Start Amp and authenticate when prompted:

amp

Verify:

/mcp list tools

Zed

Zed talks to remote MCP servers directly from settings.json — no Rust extension required. Rust is only needed when packaging a locally-spawned MCP server as a Zed extension; Cobalt's MCP is hosted and uses OAuth, so JSON config is enough.

Open the Command Palette (Cmd+Shift+P on macOS, Ctrl+Shift+P on Windows/Linux) and run zed: open settings.

Add Cobalt under context_servers:

{
  "context_servers": {
    "cobalt": {
      "url": "https://api.cobaltpf.com/api/mcp"
    }
  }
}

Save the file. Zed picks up the change immediately and opens the OAuth flow in your browser — sign in with your Cobalt account.

Open the Agent Panel and confirm cobalt appears under available context servers. Cobalt's tools are now usable from any Zed agent.

Zed forwards context_servers to external agents (Claude Agent, Codex) via ACP. Remote OAuth-based MCP servers occasionally hit forwarding issues with external agents — if a tool call fails there, run it from Zed's built-in Agent Panel instead.

Pi

Pi is a minimal terminal coding agent from earendil-works. Pi does not speak MCP — tools are registered as TypeScript extensions, so there is no transport for Cobalt's hosted MCP server to plug into.

Support is planned via a dedicated cobalt-pi extension that runs the OAuth2 authorization-code + PKCE flow against https://api.cobaltpf.com/api/auth/oauth2/*, caches the resulting access token, and registers cobalt.* tools that call the Cobalt API on your behalf. No tokens to paste.

Track progress on the Cobalt GitHub. Once cobalt-pi ships, install instructions land here.

Other clients

Any MCP client that supports Streamable HTTP transport can connect to:

https://api.cobaltpf.com/api/mcp

The server uses OAuth — your client will need to handle the browser-based authentication flow.

Local development

For local development, point to your dev server:

claude mcp add --transport http cobalt http://localhost:3000/api/mcp
{
  "mcpServers": {
    "cobalt": {
      "url": "http://localhost:3000/api/mcp"
    }
  }
}

Use http://localhost:3000/api/mcp as the server URL in your client's MCP configuration.

Available tools

The server exposes two tools. Almost all real work happens through cobalt_execute_code.

cobalt_execute_code

Runs a JavaScript program against the cobalt.* SDK inside a sandboxed V8 isolate. Top-level await is supported. cobalt is pre-injected — do not import it.

ParameterTypeDescription
codestringJavaScript source (no TypeScript syntax). Pre-injected cobalt.*

Return value of the program is serialized back as the tool result. Use console.log for intermediate output.

Example — savings rate this month:

const txns = await cobalt.transactions.list({
  startDate: "2026-05-01",
  endDate: "2026-05-31",
  limit: 500,
});

let spent = 0, earned = 0;
for (const t of txns.transactions) {
  if (t.amount > 0) spent += t.amount;
  else earned += -t.amount;
}

return { spent, earned, savingsRate: (earned - spent) / earned };

cobalt_get_session_subject

Returns the Cobalt user id (sub) for the current OAuth access token. Useful for debugging auth.

Input: None

The cobalt.* SDK

All calls below are auto-scoped to the authenticated user.

Accounts

cobalt.accounts.list({ type?, subtype? })
cobalt.accounts.getById({ accountId })

type is a Plaid type ("depository" | "credit" | "loan" | "investment" | "other"). subtype is the Plaid subtype ("checking" | "savings" | "mortgage" | "401k" | ...). Omit both for everything. SnapTrade brokerage data lives under cobalt.brokerage.*.

Transactions

cobalt.transactions.list({
  startDate?, endDate?, primaryCategory?, accountType?,
  minAmount?, maxAmount?, searchQuery?, pendingFilter?,
  limit?, cursor?,
})
// returns { transactions, nextCursor, hasMore }

cobalt.transactions.update({
  transactionId,
  patch: { name?, date?, notes?, categoryId?, tags?,
           merchantName?, website?, location? },
})

Cursor-paginate by passing nextCursor back as cursor. update is patch-only — pass null for name / date / notes / categoryId / merchantName / location to restore the original Plaid value. website accepts a bare domain or full URL and is normalized to bare lowercase. patch.tags is a full replace of the tag set — for single-tag add/remove use the tag helpers below. location is a composite object: { address, city, region, postal_code, country, lat, lon, store_number }.

Tags

cobalt.tags.list()
cobalt.tags.get({ tagId })
cobalt.tags.create({ name, color })          // returns { id }
cobalt.tags.update({ tagId, patch: { name?, color?, archived? } })

cobalt.tags.forTransaction({ transactionId })
cobalt.tags.addToTransaction({ transactionId, tagIds })       // merge
cobalt.tags.removeFromTransaction({ transactionId, tagIds })
cobalt.tags.setOnTransaction({ transactionId, tagIds })       // full replace; [] clears

archived: true is a soft delete — hidden from the picker, history preserved. Hard delete is not exposed.

Brokerage (SnapTrade)

cobalt.brokerage.balances()
cobalt.brokerage.accounts()
cobalt.brokerage.userBrokerages()
cobalt.brokerage.userTickers()
cobalt.brokerage.positions({ accountId?, limit?, offset? })
cobalt.brokerage.activities({ accountId?, limit?, offset? })
cobalt.brokerage.portfolioSnapshots({ accountId?, startDate?, endDate? })

Snapshots

cobalt.snapshots.balances({ accountId?, startDate?, endDate? })

Research (global market data, not user-scoped)

cobalt.research.quote({ symbol })
cobalt.research.overview({ symbol })
cobalt.research.news({ symbol })

Example workflows

Once connected, try asking your AI agent:

  • "What are my top 10 largest transactions this month?"
  • "Show me my total balance across all bank accounts."
  • "Tag every Whole Foods transaction this year as groceries."
  • "What recurring subscriptions do I have and how much do they cost?"
  • "Break down my spending by category for the last 3 months."
  • "What's my current brokerage allocation by ticker?"
  • "Compare my spending this month vs last month."
  • "Rename the merchant on transaction txn_… to Costco Wholesale."

Help your agent target your data

Add a hint to your CLAUDE.md / .cursorrules:

## Cobalt MCP

- Use `cobalt_execute_code` — write one program, not many tool calls.
- The `cobalt.*` SDK is pre-injected; do not import it.
- `cobalt.transactions.list` returns `{ transactions, nextCursor, hasMore }`. Paginate with `cursor: nextCursor`.
- `cobalt.transactions.update.patch.tags` is a full replace — use `cobalt.tags.addToTransaction` / `removeFromTransaction` for single-tag edits.

Troubleshooting

Restart your client — After installation, you may need to restart your client or CLI to load the new MCP configuration.

Check the server URLhttps://api.cobaltpf.com/api/mcp.

Re-authenticate — If your token has expired, re-authenticate by restarting the MCP connection in your client.

Code errored — The tool result contains error (with name + message) and any stdout your program produced before the throw. console.log your intermediate values to debug.

Empty results — All cobalt.* calls are scoped to your user. Verify you actually have data linked to your Cobalt account.

Timeout — Execution is capped at 180 seconds. Narrow your startDate / endDate window, lower limit, or paginate with cursor.

userId rejected — Sandboxed code cannot pass a userId to cobalt.*. The subject is injected server-side.

On this page