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.

15 min

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 Server

One 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_file

But if the account actually owns multiple agents:

user_123
├── research-agent
├── coding-agent
└── execution-agent

Then 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_B

The 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 identity
That'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 ecosystem

At 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.

MCP Agent Identity: How to Identify Which AI Agent Made a Tool Call · LIME