How to give Cursor access to your API without giving it your keys

Cursor's agent can read your project, run a shell command and call an API, which is useful until a production key is in reach. One key in a file is enough for the agent to send the call on its own.

Porticol Guard puts the key in the keychain, gives Cursor the API over MCP, and holds a risky call until a person approves it.

1. Install

macOS or Linux:

curl -fsSL https://porticol.app/install.sh | sh

Windows, in PowerShell:

irm https://porticol.app/install.ps1 | iex

Open Porticol. Local use needs no account and no sign-in. An account is asked for only when you invite someone, sync, share or use a team gateway.

2. Connect Cursor

On the start screen, choose Connect my coding agent. Porticol finds Cursor, shows the exact lines it will add to ~/.cursor/mcp.json, and writes nothing until you click Add Porticol. Remove restores the file.

By hand, add the server to ~/.cursor/mcp.json (%USERPROFILE%\.cursor\mcp.json on Windows):

{
  "mcpServers": {
    "porticol": {
      "type": "stdio",
      "command": "porticol-cli",
      "args": ["mcp"]
    }
  }
}

Reload MCP or restart Cursor, then ask the agent to list your Porticol environments. The first call proves the connection.

Cursor has no setting that forces its traffic through a proxy. For shell and MCP calls, let Porticol add its hooks to ~/.cursor/hooks.json: beforeShellExecution and beforeMCPExecution call porticol-cli guard hook cursor, with failClosed: true, so a hook that crashes or times out denies the call.

3. Write a policy

Guard reads one YAML policy. Start in shadow mode, where Guard logs what it would do and lets the call through, then switch to enforce when the rules match how your team works. The policy below is in enforce, because the walkthrough holds a call.

version: 1
mode: enforce
hosts:
  api.example.com: { env: production, key: "{{api_key}}" }
rules:
  - match: { method: [GET, HEAD] }
    action: allow
  - match: { env: production, method: [POST, PUT, PATCH] }
    action: ask
agents:
  cursor: { up_to: production }
  "*": { up_to: staging }

{{api_key}} names a secret variable of the production environment. Its value stays in the keychain, and Porticol adds it to the request.

The first matching rule wins. A call above the agent's ceiling is denied before any rule. On a protected environment, a call no rule matches asks by default, so a DELETE to production is held.

4. The held call

When Cursor sends a DELETE to production, send returns:

{
  "status": "pending",
  "approval_id": "8f0c…",
  "reason": "DELETE to production needs a person.",
  "message": "A person must approve this call in Porticol. Call send again with the same request and approval_id once they have. Do not loop and do not ask for a secret."
}

The approval card shows the agent's name, the environment, the exact request with keys masked, and the agent's reason in its own words. Choose Allow once, Allow for today or Deny. A system notification fires while the app is in the background. From a terminal, porticol-cli approve <id> takes the four-letter code shown on the card; the agent's shell has no terminal, so it cannot approve its own call.

The approval is bound to a SHA-256 of the request the policy decided: the method, URL, environment, headers, auth, protocol, settings and body. It is single use and expires after 10 minutes. Cursor retries once with the same request and approval_id; a changed request does not match. A denied call returns {"status": "denied"} and is not retried.

5. Read the log

Every call lands in Agent activity: the agent, method, URL, environment, decision and the rule behind it. The same entries sit in a hash-chained log. porticol-cli guard log verify checks the chain, and the log exports as JSON or CSV. Free keeps 30 days on this computer; on the server, Team keeps 90 days and Business 180, with export.

The honest limit for Cursor

Cursor sits at level 1 with hooks, not level 2. The failClosed hooks can ask and deny on shell and MCP calls, but they are not a forced network path: a command that avoids the hook, or an unsandboxed shell outside Cursor's control, can still reach the network. Level 1 stops mistakes, not an agent that finds a key and runs curl itself. Use porticol-cli guard scan to find production keys on the laptop, and put the production key on the gateway when the call must be stopped for certain. Windows runs levels 1 and 3 only. Cursor's name in the log is self-reported.

Questions

Is Cursor protected at the same level as Claude Code? No. Claude Code has a sandbox that can force traffic through the proxy on macOS and Linux. Cursor has no such setting, so Guard can hold and deny the calls it sees, not every call the machine can make.

What happens when a hook fails? With failClosed: true, a crash or a timeout denies the call. That is deliberate: a guard that fails open is not a guard.

Does Cursor see the key? No. Porticol injects the key when it sends the request and masks every result. A key already in the project is a separate problem, and porticol-cli guard scan finds those.