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.
One escalation, every hop
Section titled “One escalation, every hop”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.
-
You sign in. Your coding agent now holds a token that proves you are the one driving it.
-
Your agent calls
escalate_ticketwith a ticket ID and your token. The agent has no credentials of its own, just proof of who it’s acting for. -
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.
-
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.
-
The MCP server exchanges your token for a Linear API credential in order to
readand resolve the label names to IDs. -
The MCP server exchanges your token for a Linear API credential in order to exercise the
issues:createscope 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.
The denial, traced
Section titled “The denial, traced”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.
The complete audit log
Section titled “The complete audit log”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.
| Tool | Resource | Scope | Status |
|---|---|---|---|
escalate_ticket | Supabase Database | — | Issued |
escalate_ticket | Anthropic API | — | Issued |
escalate_ticket | Linear API | read | Issued |
escalate_ticket | Linear API | issues:create | Issued |
delete_issue | Linear API | write | Failure |
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).
Sessions
Section titled “Sessions”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.
Before and after
Section titled “Before and after”In Chapter 0, your .env file had one powerful downstream API key that did everything:
# .env (Chapter 0)LINEAR_API_KEY= # full workspace access, shared on every laptopNot only that, /mcp could be called by anyone or anything on the internet.
Now, all downstream resources go through Keycard:
# .env (finished)KEYCARD_URL= # your Keycard (issuer)MCP_RESOURCE_URL=... # this MCP serverKEYCARD_CLIENT_ID= # which app is askingKEYCARD_CLIENT_SECRET= # can be removed in multiple ways not covered in this workshopSUPABASE_URL= # identifier, not a key; key in the vaultANTHROPIC_API_URL=... # identifier, not a key; key in the vaultLINEAR_API_URL=... # identifier, not a key; access delegated per callLINEAR_TEAM_ID=... # config, not a credentialWhat’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!
How to get rid of all the secrets
Section titled “How to get rid of all the secrets”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.
Workshop feedback
Section titled “Workshop feedback”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!
What’s next?
Section titled “What’s next?”The primitives you used here aren’t specific to MCP servers:
- The
keycardCLI 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!