Skip to content
API Reference

Access APIs on Behalf of Users

Build an app or custom MCP server that accesses APIs on behalf of your users without storing tokens.

Ask Claude to build an app for you. By the end you’ll have a running application, whether a custom MCP server or any backend service, where users sign in once and every API call to a third-party service (like Linear) uses a short-lived token scoped to that user. Tokens are minted per-request and never stored.

MCP ServerYour server running locally
User AuthUsers sign in once via Keycard
Per-Request TokensScoped, short-lived, never stored
Audit TrailEvery access logged in Console
  • Keycard session active. If you haven’t set one up yet, complete the Quickstart first (steps 1-4).
  • Your keycard run -- claude session should be running
  1. Ask Claude to create your MCP server

    Inside your Keycard session, send a prompt like:

    Create a mcp server which will allows access to linear on behalf of users.

    The Keycard skill plans the execution and provides you with an action plan before proceeding.

  2. Claude scaffolds the project and provisions Keycard resources

    Claude generates the code, wires it to your Keycard instance, and provisions the resources needed for credential brokering through the Management API.

    Look at how the scaffolded code handles auth and resource access:

    // server.ts — every request is authenticated before any tool runs
    const bearerAuth = requireBearerAuth({
    issuers: KEYCARD_URL,
    requiredScopes: ["mcp:tools"],
    });
    app.post("/mcp", bearerAuth, async (req, res) => { ... });
  3. Claude registers the server in Claude’s MCP config

    Claude runs claude mcp add to register your server so it’s available in future sessions.

  4. Start the server

    Claude will provide detailed instructions with the exact steps to start and test your server. In a new terminal, start your MCP server with Keycard:

    Terminal window
    cd <project-name>
    keycard run -- npm start

    keycard run injects short-lived credentials from your Keycard instance at startup. No secrets live in a file. Keep this terminal open.

    You may see a consent screen like this when the CLI requests access to your application’s credentials:

    Keycard consent screen

  5. Connect and test

    In another terminal, start a new Claude session from the same project directory:

    Terminal window
    keycard run -- claude

    Your MCP server is registered but needs you to authenticate before Claude can call its tools on your behalf. Type /mcp to open the server list, then select your server and complete the login flow:

    > /mcp
    Local MCPs
    ❯ <project-name> · △ needs authentication

    Select the server and authorize in your browser when prompted. You may see a consent screen from Linear that establishes trust between Keycard and Linear:

    Linear consent screen

    Once connected, you’ll see ✔ connected next to it.

    Now try calling a tool:

    List my Linear issues.

    Each tool call triggers a fresh credential exchange. The upstream token is minted per-request and never stored.

  6. Verify in Keycard Console

    Open Keycard ConsoleAudit Log. You should see entries for:

    users:authenticateYou logged in successfully
    users:authorizeYour access was authorized
    credentials:issueAccess token was issued

    These entries map directly to the delegation chain described below.

Your server is running. Here’s what Claude set up and why it matters when you build your own servers or debug this one.

Every request to your MCP server is authenticated before any tool runs. Whether the caller is a human or another agent, the flow is the same.

The Keycard SDK handles the protocol details: advertising how callers should authenticate, validating credentials, and passing the verified identity into your tool handlers.

The audit log entries from step 6 show this in action: users:authenticate confirms the caller’s identity was verified, users:authorize confirms their access was checked against policy.

For the full authorization model, see How Keycard Works.

Your MCP server needs its own identity to access upstream resources. Keycard provisions application credentials and stores them on the platform. Nothing lives locally or gets committed to source control.

At startup, keycard run resolves the credentials declared in keycard.toml and injects them into the runtime environment. Your server uses those credentials to authenticate itself to Keycard when requesting resources on behalf of callers.

How credentials are delivered depends on where you’re running. Locally, keycard run pulls them from the Keycard platform. In production, your service needs to authenticate itself. The next guide covers how. See Applications for the full identity and credential model.

When a tool call needs to reach an upstream API, your server doesn’t hold a long-lived token. It uses delegated token exchange to get a short-lived, scoped token for that single request.

User
MCP Client
MCP Server
Resource

Keycard

ephemeral credential
User
authorize
MCP Client
authenticateKeycard
caller identity
MCP Server
exchangecaller identityKeycard
ephemeral credential
Resource

The credentials:issue audit log entry from step 6 is the result of this exchange. Every tool call repeats the same cycle. Nothing is stored. Every access is scoped and logged.

Your application is running locally with per-user token exchange. In production, nobody is there to inject credentials at startup, so your application needs to authenticate itself. The next guide covers that:

Why am I seeing so many ITL prompts?

During the walkthrough, Claude provisions Keycard resources using keycard agent api commands. If your policy has a broad @itl("prompt") rule on Bash, every one of those provisioning calls triggers an approval prompt.

  • Ask Claude to update your policy to allow keycard CLI commands without ITL.
  • Check your active policy with: “What’s my current Keycard policy?”
keycard agent api POST returns “Unsupported Media Type” or 400

Provisioning commands run inside a keycard run session can fail with a 400 or "Unsupported Media Type" error.

  • Prompt Claude to look up the Keycard API reference and retry the command with the correct invocation.