Architecture
Your MCP Server Knows the User. But Does It Know the Agent?
Request authenticated. Who actually acted? A research-style look at agent identity on the MCP resource boundary — with a live experiment you can run yourself.
One user. Several agents. One MCP server. The request is authenticated — but which autonomous actor made it? This article treats that as a testable question, not a sales pitch.
The scenario
Start with a system that looks ordinary:
User
|
+-----------+-----------+
v v v
Agent A Agent B Agent C
| | |
+-----------+-----------+
v
MCP ServerOne user. Three agents. One MCP server.
The server receives an authenticated request. It may know the user account. The harder question remains:
Which agent actually made this request?
What collapses in the logs
Skip abstract "AI security" language. Look at what a log becomes today:
user_123 -> search_database
user_123 -> send_email
user_123 -> delete_fileBut if the account actually owns multiple agents:
user_123
├── research-agent
├── coding-agent
└── execution-agentThen every action collapses into the same label.
That is a concrete attribution failure: the resource server can no longer tell which autonomous actor performed which tool call.
Is agent identity actually necessary?
In a simple application this may change nothing. One user, one agent, one backend — user identity may be enough.
But as soon as several autonomous agents act on behalf of the same user, a separate attribution question appears: is knowing the user enough, or should the resource server distinguish specific agents?
That is the question we wanted to test — not to declare solved for everyone.
A testable hypothesis
We wanted to test one specific hypothesis:
Can an MCP resource server distinguish two agents acting on behalf of the same user without trusting an agent ID supplied by the caller?
User identity ≠ agent identity
User identity answers: who is the human or account?
Agent identity answers: which autonomous software entity is acting?
One does not automatically replace the other. A single user identity can own many agent identities:
user_123
|
├── agent_A
└── agent_BThe tempting bad solution
Why not just pass the agent ID to the tool?
{
"agent_id": "agent_A"
}If agent_id arrives only as a tool argument, the resource server does not receive cryptographic proof that this ID actually represents the caller.
A real server might still try to bind that claim to some credential — but the argument itself remains a claim, not verified identity.
agent_id = claim
vs
agent_id = verified identityThat's attribution theater, not authenticated agent identity.
What a valid solution would require
Before naming any product, derive the requirements. Agent identity should be:
- portable across services
- cryptographically verifiable
- independent from user identity
- verifiable by a resource server without calling an identity provider on every request
- not dependent on trusting tool arguments
Avoiding that per-request identity-provider call is what people mean by "no hot-path callback". One way to meet these requirements is a signed, short-lived agent credential — a passport the resource server can verify locally.
The experiment
To test the hypothesis, we built the smallest possible public MCP resource server and used two different signed agent credentials against the same endpoint.
Passport A → demo.lime.pics/mcp → agent_7f31...
Passport B → demo.lime.pics/mcp → agent_a921...Same resource server. Same tool. Different verified identities.
One server. One endpoint. Two passports.
who_am_i() — the interesting part
The interesting part is not only what who_am_i() returns. It is what the tool does not receive.
There is no agent_id argument.
who_am_i()≠
who_am_i(agent_id="agent_A")The server obtains identity from an already verified credential — not from client-supplied arguments.
That is the entire proof shape of the experiment.
Passport A
{
"verified": true,
"agent_id": "agent_7f31..."
}Passport B
{
"verified": true,
"agent_id": "agent_a921..."
}The experiment confirms that this model can work at the MCP resource boundary.
Implementation: LIME Passport + local JWKS verification
We built LIME around this model. To run the experiment, LIME acts as the identity issuer; the demo MCP server is a minimal public resource server.
Agent
|
| authenticate
v
LIME Core
|
| signed short-lived Passport
v
MCP Resource Server
|
| local verification (JWKS)
v
agent_id (= JWT sub)Important: LIME does not proxy the MCP request. The resource server does not need to call LIME on every tool invocation to ask "is this agent valid?".
It verifies the Passport signature itself, using published JWKS, then reads identity from the verified credential.
What the server knows — and what it does not
After verification, be precise about scope.
MCP Resource Server knows:
- passport is valid
- issuer
- audience
- expiration
- agent_id
- domain binding
It does NOT automatically know:
- what the agent intends to do
- whether the agent is trustworthy
- whether the user authorized this specific action
- what permissions the agent should have
Identity is not authorization.
LIME provides an identity primitive. The resource server still decides authorization, trust, and policy.
When it matters
You may not need this if one user maps to one agent maps to one backend.
It becomes more interesting for multi-agent systems, delegation, audit trails, and permission boundaries — when one user owns many agents across many tools and services:
User
├── Research Agent
├── Coding Agent
├── Browser Agent
└── Execution Agent
|
v
MCP ecosystemAt that point, agent identity becomes a separate architectural concern — not a footnote on user login.
An open question for builders
- If you build MCP servers, what does your resource server actually know about the caller today?
- Can it distinguish between two agents operating on behalf of the same user?
- If yes — how do you do it?
- If not — would agent-level attribution be useful in your architecture?
Here is an architectural problem. You may not need it. If you do — here is one minimal way to solve it, and a live server where you can test the model yourself. We would be interested in how other MCP developers approach it.
Try it / dig deeper
Explore the experiment
Read the open demo, mint a Passport, call who_am_i — or tell us why your boundary solves attribution differently.