// MCP SERVER

MCP server

Preview: it works, but expect rough edges. matmul-mcp is an MCP server that lets Claude Code, Claude Desktop, Cursor, VS Code or any other MCP client find and launch GPU machines on MatMul. Ask for "the cheapest H100 under $3/hr with an SSH command" and the assistant drives the MatMul API for you. It can't launch anything until you've approved a price quote.

What it does

The server runs on your own machine over stdio. Your MCP client starts it as a subprocess, and it calls the MatMul API (https://api.matmul.cloud/v1) with your API key. There's no hosted MCP endpoint yet. Through it, an assistant can:

Instances bill per hour from your prepaid balance until they're terminated, the same as instances launched from the console or the CLI.

Install

You need uv (for uvx) or Python 3.10+ with pip. The config snippets below use uvx, which fetches and runs the server without a separate install step:

uvx matmul-mcp --version      # run without installing (recommended)
pip install matmul-mcp         # or install the matmul-mcp command

If you install with pip, use matmul-mcp as the command in your client config instead of uvx with ["matmul-mcp"].

Authentication: your API key

The server acts as you, with your own API key. Create one on the CLI & API page of the console at app.matmul.cloud/app/cli. Keys start with lmk_. Keys belong to a person, so your role in the organization (owner, admin, member or viewer) applies to what the assistant can do, the same as in the console.

The server looks for the key in this order:

  1. the MATMUL_API_KEY environment variable, which you set in the client config below;
  2. the key saved by matmul login --api-key <key> in ~/.config/matmul/config.json (or the file named by MATMUL_CONFIG).

Other settings, also environment variables:

Treat the key like a password. It's stored in plain text in most client config files, so keep those files out of git. VS Code can prompt for it instead (below).

Set up your client

In each snippet, replace lmk_... with your key. You can leave out MATMUL_API_KEY if you've run matmul login on this machine. The cap of $5/hr is an example: set your own, or leave it out.

Claude Code

claude mcp add matmul \
  -e MATMUL_API_KEY=lmk_... \
  -e MATMUL_MCP_MAX_HOURLY_USD=5 \
  -- uvx matmul-mcp

Add --scope user to make it available in every project. Check it with claude mcp list, or /mcp inside a session.

Claude Desktop

Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows), then restart Claude Desktop:

{
  "mcpServers": {
    "matmul": {
      "command": "uvx",
      "args": ["matmul-mcp"],
      "env": {
        "MATMUL_API_KEY": "lmk_...",
        "MATMUL_MCP_MAX_HOURLY_USD": "5"
      }
    }
  }
}

Cursor

Edit ~/.cursor/mcp.json (all projects) or .cursor/mcp.json (one project):

{
  "mcpServers": {
    "matmul": {
      "command": "uvx",
      "args": ["matmul-mcp"],
      "env": {
        "MATMUL_API_KEY": "lmk_...",
        "MATMUL_MCP_MAX_HOURLY_USD": "5"
      }
    }
  }
}

VS Code (GitHub Copilot agent mode)

Add .vscode/mcp.json to your workspace. VS Code prompts for the key the first time the server starts and stores it securely, so the key never sits in the file:

{
  "inputs": [
    { "type": "promptString", "id": "matmul-key", "description": "MatMul API key", "password": true }
  ],
  "servers": {
    "matmul": {
      "type": "stdio",
      "command": "uvx",
      "args": ["matmul-mcp"],
      "env": {
        "MATMUL_API_KEY": "${input:matmul-key}",
        "MATMUL_MCP_MAX_HOURLY_USD": "5"
      }
    }
  }
}

How launches are confirmed

launch_instance rents a real machine that bills every hour until it's terminated, so it always takes two calls:

  1. Quote. The first call launches nothing. It picks the cheapest live offer at or under your price ceiling and returns a quote: the offer, its estimated $/hr, the ceiling, the image (or plain VM), the SSH key, your balance and runway at that price, and a line saying it bills until terminated. It also returns a confirm_token.
  2. Confirm. The assistant shows you the quote and asks. Only a second call carrying that token, with the same arguments, launches the machine.

The token is what makes the quote a required step:

The server's instructions tell the assistant to get your explicit yes before the second call. The token proves a quote was shown; it can't prove you said yes. If your client lets you approve tool calls one by one, keep approval on for launch_instance.

The price guard

max_hourly_usd is required on every launch. The assistant is told to use the budget you gave and not invent one. It's sent to the API as the max_hourly_price_cents price guard: if the live price has risen above it by the time the machine is created, the API refuses the launch. The error starts with "Price guard:" and says nothing was launched and nothing was charged, and the assistant can get a fresh quote or ask whether to raise the ceiling.

MATMUL_MCP_MAX_HOURLY_USD is a second limit on top. It lives in your client config, so the assistant can't change it, and the server checks it on both the quote and the launch.

No automatic top-ups

add_credit only returns a Stripe checkout link for you to open and pay in your browser. The server never pays for anything. If a launch fails because the balance is too low or the account is frozen, the error says so and points at get_balance and add_credit.

Tools

Read tools are marked read-only, and terminate_instance (and cancel_job) are marked destructive, so clients that auto-approve reads still ask before those calls. Prices are in US dollars per hour for the whole machine.

find_gpus

find_gpus(gpu?, max_hourly_usd?, region?, count?, limit=10): live GPU offers, cheapest first, up to 100. Read-only and free. Offers change minute to minute, so the launch re-checks the price. Filtered to MATMUL_MCP_MAX_HOURLY_USD when it's set.

get_balance

get_balance(include_ledger?, ledger_limit=10): prepaid balance, whether the account is frozen (and the grace time left), new-account limits if your account isn't verified yet, running instances, burn rate and runway in hours. Optionally recent charges and credits. Read-only.

add_credit

add_credit(amount_usd): returns a Stripe checkout URL. It charges nothing itself; you open the link and pay. The API's minimum and maximum top-up apply. The balance updates a few seconds after payment.

list_instances and get_instance

list_instances(include_deleted?): your organization's instances with status, GPU, region, image, $/hr, accrued cost and SSH command, plus the total burn rate. get_instance(id_or_name): one instance. New instances take a few minutes to provision; the assistant polls this until the status is running and then gives you ssh_command. For an instance launched with an image, container_shell_command opens a shell inside the container (the SSH command lands on the host VM). Both are read-only and warn if the account is frozen.

launch_instance

launch_instance(name, gpu, max_hourly_usd, image?, keep_alive?, ssh_key?, count=1, region?, confirm_token?): quote, then launch, as described above. Spends money.

terminate_instance

terminate_instance(id_or_name): deletes the instance and stops its billing from that moment. Returns the accrued cost. Destructive: the machine and everything on its disks are gone. The assistant is told to confirm with you first unless you just asked for exactly this.

list_ssh_keys and add_ssh_key

list_ssh_keys() lists the public keys installed on new instances. add_ssh_key(name, public_key) registers one. It must be a public key line (ssh-ed25519 ..., ssh-rsa ... or ecdsa-..., as in ~/.ssh/id_ed25519.pub); the server refuses anything that looks like a private key.

Managed jobs (off by default)

With MATMUL_MCP_ENABLE_JOBS=1 the server adds run_job, list_jobs, job_logs and cancel_job (destructive). They're opt-in because run_job doesn't take a price ceiling yet (the API and CLI do), and managed jobs aren't switched on for MatMul accounts yet. Leave them off unless you're testing jobs.

Resources and a prompt

Example prompts

Troubleshooting

Still stuck? See Support. New to MatMul? Start with the docs overview or how to rent a cloud GPU in 5 minutes.

← All docs