Model Context Protocol (MCP)

Connecting a client

Two credentials reach this server, and the choice is about who is at the keyboard.

Sign inAPI key
What you handleNothingA secret you place and rotate
LifetimeOne hour, refreshed silentlyUntil you revoke it
Read or writeChosen by the approver, per connectionFixed by the key's scope
Scoped toOne organizationOne organization
Where you revoke itSettings > API Keys > Connected ApplicationsSettings > API Keys
Good forA person at an assistantCI, a shared runner, a script
Prefer signing in. An API key is a password with no expiry that reads every cost figure in your organization, so save it for the cases where no browser is involved.

Both reach the same tools, and both resolve your organization from the credential, never from an argument. Signing in is the only route your client can start on its own, which is why l4 mcp install installs a key instead.

Configure your client

claude mcp add --transport http levelfour https://mcp.levelfour.ai/mcp

Run /mcp, choose levelfour, then Authenticate. Your browser opens the approval screen.

--scope local is the default and keeps the server to the current project. --scope user makes it available in every project. --scope project writes .mcp.json into the repository for your team, which works here because no credential is in the file: each teammate authenticates as themselves.

Confirm it registered, and remove it:

claude mcp list
claude mcp get levelfour
claude mcp remove levelfour

To send a key instead, add the header at registration time:

claude mcp add --transport http levelfour https://mcp.levelfour.ai/mcp \
  --header "Authorization: Bearer $LEVELFOUR_TOKEN"
--header is variadic, so it has to come after the name and the URL. Put it first and it swallows both, and the command exits having done nothing.

Claude connects over OAuth and has no field for a header, so signing in is the only route. In Settings > Connectors, add a custom connector with the URL https://mcp.levelfour.ai/mcp. Claude registers itself and sends you to the approval screen.

Claude identifies itself with a metadata document served from its own domain, so this connection does not carry the unreviewed-application notice. Approving a connection explains why a locally installed client does.

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

~/.cursor/mcp.json
{
  "mcpServers": {
    "levelfour": {
      "url": "https://mcp.levelfour.ai/mcp"
    }
  }
}

With no header, Cursor discovers that the server wants authorization and offers to sign you in. To send a key instead, add a headers block:

~/.cursor/mcp.json
{
  "mcpServers": {
    "levelfour": {
      "url": "https://mcp.levelfour.ai/mcp",
      "headers": {
        "Authorization": "Bearer ${env:LEVELFOUR_TOKEN}"
      }
    }
  }
}
Cursor interpolates ${env:...} in url and headers, so export LEVELFOUR_TOKEN in the shell that launches Cursor and the key stays out of the file. LEVELFOUR_TOKEN is the variable l4 itself reads, so one export serves the CLI and the client. You commit a project-level .cursor/mcp.json with your code, which is the reason to keep keys out of it.

Run MCP: Add Server from the Command Palette, or write .vscode/mcp.json in the workspace:

.vscode/mcp.json
{
  "servers": {
    "levelfour": {
      "type": "http",
      "url": "https://mcp.levelfour.ai/mcp"
    }
  }
}

VS Code prompts you to sign in on first use. To send a key instead, add a headers block with an inputs entry, which keeps the value out of the file and in the editor's own secret storage:

.vscode/mcp.json
{
  "servers": {
    "levelfour": {
      "type": "http",
      "url": "https://mcp.levelfour.ai/mcp",
      "headers": {
        "Authorization": "Bearer ${input:levelfour-api-key}"
      }
    }
  },
  "inputs": [
    {
      "id": "levelfour-api-key",
      "type": "promptString",
      "description": "LevelFour API key",
      "password": true
    }
  ]
}

Write ~/.codeium/windsurf/mcp_config.json. Windsurf spells the endpoint serverUrl where the others use url:

~/.codeium/windsurf/mcp_config.json
{
  "mcpServers": {
    "levelfour": {
      "serverUrl": "https://mcp.levelfour.ai/mcp",
      "headers": {
        "Authorization": "Bearer l4_live_your_key_here"
      }
    }
  }
}
Windsurf does not interpolate environment variables, so a key sits in the file. Keep that file out of version control, and prefer the sign-in route if your version offers it.

Restart Windsurf after saving.

A client that speaks MCP over streamable HTTP connects the same way: point it at https://mcp.levelfour.ai/mcp. If it implements MCP authorization it will discover the server, register itself and send you to the approval screen. If it does not, give it an Authorization: Bearer <key> header. Goose, Zed and Cline all work. You write their config yourself.

For a client that starts a local process rather than calling a URL, l4 mcp serve runs the read tools on your own machine against the credential in your keychain.

Getting an API key

From the dashboard. Open Settings > API Keys, create a key, and copy it. You see the value once. A read key reaches every tool that only reads. The ones that record a decision need a read-write key. See Key scopes for the full split.

From the CLI. l4 mcp install uses the credential l4 already holds and only opens the browser to mint a read key when there is none, so it doubles as the quickest way to get one. Pass --token to install a specific key instead.

Treat the key like a password. It reads every cost figure and every recommendation in your organization. Keep it in an environment variable or your client's secret storage rather than in a file you commit.

Troubleshooting

A rejected credential returns 401 with a WWW-Authenticate header pointing at the server's protected resource metadata, which is how a client discovers where to authorize. The status alone does not separate the causes, so work through them in order.

SymptomCauseFix
401, and the client offers to sign inNormal on a first connection. The server is telling the client where to authorizeComplete the approval in the browser
401 after signing inThe token expired and the refresh failed, or somebody closed the connection in the dashboardAuthenticate again. A closed connection stays closed until you approve a new request
401 with a keyThe key never arrived, is not an l4_live_ or l4_test_ value, or is revoked or expired. A dashboard session token is rejected before any lookupRestart the client, confirm the header is present and starts with Bearer , and that the value is the API key itself. Create a new key if it is revoked
401 that no valid credential fixesThe organization does not have the MCP module. The server refuses the credential rather than naming the reasonStart the connection and read the approval screen, which does name it. Then talk to your LevelFour contact
The approval screen says only an administrator can approveApproving hands out a credential, so it is limited to administratorsAsk an administrator to start the connection themselves, rather than forwarding the link
The approval request has expiredA request is live for ten minutes and can be used onceStart the connection again from the client
421 Misdirected RequestThe request arrived under a host the server does not answer on, usually a proxy rewriting HostPoint the client straight at mcp.levelfour.ai
429Too many requests on this credential or from this addressBack off for the seconds in Retry-After. Reading a result has the limits
A tool reports a plan restrictionYour plan does not include that contentCall get-identity to see what the plan covers
Tools missing after editing configThe client read its config at startupRestart the client

Next