Summary
ADKTokenPropagationPlugin (agentsts) does an RFC 8693 exchange with subject=<human token from session state> + actor=<agent ServiceAccount token> when a human token is present. On an autonomous (machine-to-machine) run with no human token, before_run_callback returns early (python/packages/agentsts-adk/src/agentsts/adk/_base.py:178-180) and mints nothing, so the downstream MCP request falls back to whatever static Authorization the RemoteMCPServer carries (headersFrom).
Request: add an opt-in mode where, when there is no human subject token, the plugin uses the agent's own pod ServiceAccount token as the subject (no actor), so the agent still presents its own identity to the MCP server on M2M.
Why
To give each agent a distinct identity to an MCP server / gateway on M2M today, operators must provision, per agent: a headersFrom Secret, a token-mint Job + refresh CronJob that mint that agent's SA token, and a dedicated RemoteMCPServer (because headersFrom is per-server, and a projected SA-token volume only mints for its own pod's SA). That is significant per-agent boilerplate and RemoteMCPServer sprawl, purely because the runtime won't forward the agent's own pod SA token on an M2M run.
The agent pod already runs as its own ServiceAccount and already mounts a projected token (the same one used as the OBO actor_token, read from SERVICE_ACCOUNT_TOKEN_PATH in agentsts-core). Letting the plugin present that on M2M removes the per-agent token-mint machinery entirely: one shared RemoteMCPServer, no headersFrom, every agent authenticating as its own pod SA.
Proposed behavior
In before_run_callback, when get_subject_token(...) yields nothing (M2M):
- if the new option is enabled, set
subject_token = <agent pod SA token> (from agentsts-core _actor_service / SERVICE_ACCOUNT_TOKEN_PATH), actor_token = None, and run the exchange (or forward directly, per STS config);
- else keep current behavior (return
None).
| Run |
subject_token |
actor_token |
| OBO (human present) |
human token |
agent pod SA (unchanged) |
| M2M (new, opt-in) |
agent pod SA |
none |
Compatibility / scope
- Opt-in (new flag/option); default behavior unchanged → no regression for existing static-header setups.
- Audience: the pod SA token must be projected with the STS/gateway audience; document that the agent's projected-token audience must match, or have the plugin request the configured audience.
- Files:
python/packages/agentsts-adk/src/agentsts/adk/_base.py (before_run_callback, ~152-209), python/packages/agentsts-core/.../_actor_service.py (pod SA token read), and the STS_WELL_KNOWN_URI wiring in go/core/pkg/env/kagent.go.
Interim (downstream)
We currently work around this with per-agent identity in our Helm chart (chart-owned SA + headersFrom Secret + token Job/CronJob per agent). That bridge works but is exactly the machinery this change would retire.
Summary
ADKTokenPropagationPlugin(agentsts) does an RFC 8693 exchange withsubject=<human token from session state>+actor=<agent ServiceAccount token>when a human token is present. On an autonomous (machine-to-machine) run with no human token,before_run_callbackreturns early (python/packages/agentsts-adk/src/agentsts/adk/_base.py:178-180) and mints nothing, so the downstream MCP request falls back to whatever staticAuthorizationthe RemoteMCPServer carries (headersFrom).Request: add an opt-in mode where, when there is no human subject token, the plugin uses the agent's own pod ServiceAccount token as the subject (no actor), so the agent still presents its own identity to the MCP server on M2M.
Why
To give each agent a distinct identity to an MCP server / gateway on M2M today, operators must provision, per agent: a
headersFromSecret, a token-mint Job + refresh CronJob that mint that agent's SA token, and a dedicated RemoteMCPServer (becauseheadersFromis per-server, and a projected SA-token volume only mints for its own pod's SA). That is significant per-agent boilerplate and RemoteMCPServer sprawl, purely because the runtime won't forward the agent's own pod SA token on an M2M run.The agent pod already runs as its own ServiceAccount and already mounts a projected token (the same one used as the OBO
actor_token, read fromSERVICE_ACCOUNT_TOKEN_PATHinagentsts-core). Letting the plugin present that on M2M removes the per-agent token-mint machinery entirely: one shared RemoteMCPServer, noheadersFrom, every agent authenticating as its own pod SA.Proposed behavior
In
before_run_callback, whenget_subject_token(...)yields nothing (M2M):subject_token = <agent pod SA token>(fromagentsts-core_actor_service/SERVICE_ACCOUNT_TOKEN_PATH),actor_token = None, and run the exchange (or forward directly, per STS config);None).Compatibility / scope
python/packages/agentsts-adk/src/agentsts/adk/_base.py(before_run_callback, ~152-209),python/packages/agentsts-core/.../_actor_service.py(pod SA token read), and theSTS_WELL_KNOWN_URIwiring ingo/core/pkg/env/kagent.go.Interim (downstream)
We currently work around this with per-agent identity in our Helm chart (chart-owned SA +
headersFromSecret + token Job/CronJob per agent). That bridge works but is exactly the machinery this change would retire.