Verifying Agent Identity at an MCP Server: What Changes When You Replace the API Key, and What Does Not
Both an API key and a credential bundle are bearer credentials, so this is not a migration that makes theft harder. What it changes is that the caller arrives carrying claims, and that somebody other than you can revoke them.
The mechanics, which are small
The calling agent puts its envelope in the tool call's metadata, under a namespaced key.
Your guard reads that key, verifies the proof offline, and either returns the disclosed claims or throws. The failure carries a protocol-style error code, so a client sees a structured error rather than a transport failure.
And it depends on no protocol library, taking a tool name and a metadata object, so it does not constrain which implementation you build on.
That is the whole integration. The interesting part is what it changes about your threat model.
What replacing the API key does not change
An API key is a bearer secret: whoever holds it can call you.
A credential bundle is also a bearer credential. There is no holder key binding anywhere, so possession is sufficient to present.
So a leaked bundle and a leaked key both let somebody else call your server. "Verified identity" does not mean "unstealable", and any migration justified on that basis is justified on something untrue.
What it does change, which is still worth having
The caller carries claims you can act on. An API key says a string was issued once. A credential says an issuer attested something specific about the caller, and you decide what that issuer is worth.
A leak is revocable by somebody other than you. With keys, the operator has to notice and you have to rotate. With credentials, the issuer can revoke and any verifier who asks will see it.
And the caller can be identified across servers without you sharing anything with those servers. That is the property a key cannot have by construction, because a key is a secret shared with exactly one party.
Your four decisions, because the defaults do not make them
One: require the status reference. With none in the envelope, verification returns a valid verdict with an unchecked revocation state, and the reference is supplied by the caller. Reject an envelope without one.
Two: supply the issuer's status-signing key, or every status reference fails closed and you have built a server that refuses everyone.
Three: decide what your scope strings mean. They answer to no vocabulary, so the strings you require and the strings an issuer wrote have to be agreed between you.
Four: put consequential distinctions in the tool NAME. The proof binds the tool name and not the arguments, so a single proof is valid across different arguments to the same tool inside the acceptance window. If the amount or the target lives in the arguments, the guard has verified who is calling and not what they asked for.
What is real, because absences alone would misdescribe us
Verification is offline, so your availability never depends on ours and your latency is your own.
The guard is small and readable, which is a security property rather than a convenience one.
And the failure is structured, with a status, a title and a detail, so a caller can tell a missing credential from a rejected one.
The model you are relying on
Cross-tool replay is prevented by construction, because the tool name is inside the signed binding.
Same-tool replay is time-boxed by a skew window with a replay cache on by default.
And the package states the boundary of its own guarantee: anything needing strict one-time semantics should issue its own challenge. For a tool that moves something, take that advice.
Keep reading
- Why Nobody Prices Agent Identity Yet: The Natural Unit Is the One You Cannot Meter
- Proof of Personhood and Agent Identity: Opposite Questions, and Ours Does Not Answer Either One Alone
- What Is AP2? A Payment Protocol Whose Own Glossary Puts a Human Back in the Loop
- What Is ERC-8211? Nothing. We Checked, and Here Is Exactly How

