Skip to content

Chapter 8: The finished server

You’re done. There’s no more code to write: you finished the support-escalation server in Chapter 6 and the policy in Chapter 7. Now walk through a single escalation traced end to end, the denial that proves the policy works, the audit log, and the .env you started with next to the one you ended with.

When your agent calls escalate_ticket, your server never reaches for a static local key. For each thing it touches, it hands Keycard the caller’s token and gets back a credential scoped to exactly that action, issued for exactly that person.

  1. You sign in. Your coding agent now holds a token that proves you are the one driving it.

  2. Your agent calls escalate_ticket with a ticket ID and your token. The agent has no credentials of its own, just proof of who it’s acting for.

  3. The MCP server exchanges your token for a Supabase credential from the vault and reads the ticket. It never stores the credential, discarding it immediately after use.

  4. The MCP server exchanges your token for the Anthropic key from the vault and runs the ticket through the LLM: an engineering-facing title, PII replaced with placeholders, and applicable labels.

  5. The MCP server exchanges your token for a Linear API credential in order to read and resolve the label names to IDs.

  6. The MCP server exchanges your token for a Linear API credential in order to exercise the issues:create scope to file the issue, authored by your real Linear account, with a footer naming the application that acted on your behalf.

Each token exchange carried your identity as the subject, asked for the narrowest access its task needed, and was recorded in the audit log.

When the delete_issue tool is used, the MCP server asks Keycard to exchange your token for a Linear API credential that it will use with a write scope.

That request never becomes a credential. Your limit-linear-scopes policy forbids any Linear exchange whose scopes aren’t in the read / issues:create allowlist, and forbid overrides every permit. Keycard policy refuses at issuance, the tool call fails, and the issue is not moved to the trash.

There are two outcomes decided by policy at the moment of issuance: issues:create passes and write is refused. The credential that could have deleted the issue was never created, so there is nothing to intercept, leak, or steal.

Open your Audit Log in console.keycard.ai. Trace the green successful escalation credentials:issue events and the red denied credentials:issue for the delete tool call. Every token exchange request shows the delegation chain: your application, acting for you.

ToolResourceScopeStatus
escalate_ticketSupabase DatabaseIssued
escalate_ticketAnthropic APIIssued
escalate_ticketLinear APIreadIssued
escalate_ticketLinear APIissues:createIssued
delete_issueLinear APIwriteFailure

In Chapter 0, you had no idea who was calling resources. Requests from different actors all looked the same. Now you not only know who called the resources (your MCP client), you also know who they were acting on behalf of (you).

We didn’t cover the Sessions section of the Keycard console in the workshop, but check it out in the sidebar under Activity (grouped with Audit Log). Sessions show a user’s activity, what is acting on the user’s behalf, what that actor is accessing, and the outcome of events.

In Chapter 0, your .env file had one powerful downstream API key that did everything:

.env
# .env (Chapter 0)
LINEAR_API_KEY= # full workspace access, shared on every laptop

Not only that, /mcp could be called by anyone or anything on the internet.

Now, all downstream resources go through Keycard:

.env
# .env (finished)
KEYCARD_URL= # your Keycard (issuer)
MCP_RESOURCE_URL=... # this MCP server
KEYCARD_CLIENT_ID= # which app is asking
KEYCARD_CLIENT_SECRET= # can be removed in multiple ways not covered in this workshop
SUPABASE_URL= # identifier, not a key; key in the vault
ANTHROPIC_API_URL=... # identifier, not a key; key in the vault
LINEAR_API_URL=... # identifier, not a key; access delegated per call
LINEAR_TEAM_ID=... # config, not a credential

What’s left is identifiers, config, and one client secret that proves which app is asking. No key in this file can read a ticket, call an LLM, or touch Linear. There is no key here worth stealing.

You do have one secret left and several keys stored in an encrypted vault. You can actually get rid of all of these!

KEYCARD_CLIENT_SECRET is the last secret on disk. In Chapter 1 you saw Workload Identity offered right above Client ID & Secret, and were told it comes back at the end.

In production, your deployment platform attests to your server’s identity, Keycard verifies that cryptographically, and the application proves its identity with no secret at all. You used Client ID & Secret only because in local dev, there’s no deployment platform to attest to a server running on your laptop.

In this workshop the vault held the workshop-provided static keys because workload identity needs every issuer registered, which doesn’t scale to many workshop attendees each with their own Keycard issuer (Chapter 4) all using the same Anthropic and Supabase keys. In production you only have your issuer, so providers federate directly. Keycard’s Anthropic access guide shows how to get rid of the Anthropic API key.

Please take a few minutes to fill out the post-workshop feedback survey. This will help us to continue to improve both the product and the developer experience for builders!

The primitives you used here aren’t specific to MCP servers:

  • The keycard CLI brokers the same per-request, attributed credentials for any local process, not just an MCP server you built. Use it to govern your local coding agent’s access to third party MCP servers, tools, and more.
  • Agent-to-agent (A2A) delegation extends the chain across multiple agents: each acts for the one before it, and every hop still traces back to the human who started it.
  • Identity, exchange, vault, and policy can be used to build your own agents and protect your own resources (a custom agent-building workshop is probably on the horizon!).

Check out the guides in Keycard docs to learn about more things you can do with Keycard.

If you haven’t joined the Keycard Discord server yet, accept your invite and continue to engage with your fellow workshop attendees, the Keycard team, and other Early Access and Beta program members!