Context
The github-workflow MCP server currently authenticates all calls via a single GH_TOKEN env var loaded at startup. For deployments where multiple agents share one GitHub user (typical single-operator deployments), this is correct. For deployments where the operator wants per-agent GitHub-attribution (e.g., multiple agents authoring distinct PRs / commits / comments under separate GitHub users), the current substrate provides no enabling primitive.
This ticket is preventive operational substrate / MX hardening. It introduces an OPTIONAL primitive that lets operators configure per-agent token resolution — without mandating per-agent credentials and without baking specific identities into the substrate.
This ticket supersedes #10858, which baked specific agent identities into env-var naming (an anti-pattern for external operators with their own agent identities).
The Problem
For multi-agent deployments where per-agent GitHub-attribution matters:
- All agents currently appear as the same GitHub user in PR/commit/issue authorship.
- Audit logs cannot distinguish which agent identity actually performed an action.
- Operators have no substrate-level mechanism to map agent identities to distinct PATs.
The Architectural Reality
RequestContextService.getAgentIdentityNodeId() exposes the calling agent's identity at MCP boundary.
- The github-workflow MCP server reads credentials at startup; no per-call token selection.
- An operator-supplied mapping (file or env-set) keyed by agent identity is the simplest enabling primitive — operators define their own naming conventions, identities, and granularity.
The Fix
- Optional config file at operator-defined path:
- Env var
NEO_GH_CREDENTIALS_FILE (default unset → falls through to legacy single-token behavior)
- File format: JSON object mapping agent identity → PAT
{
"@neo-opus-4-7": "ghp_xxx",
"@neo-gemini-3-1-pro": "ghp_yyy",
"@external-deployment-agent-1": "ghp_zzz"
}
- Gitignored; operator-managed; permission 0600 recommended.
- Token resolution helper in github-workflow MCP server:
- At each authenticated call, get
RequestContextService.getAgentIdentityNodeId().
- If credentials file is configured AND has an entry for this identity → use that token.
- Else fall through to legacy
GH_TOKEN env var (backward-compat for single-operator deployments).
- Audit log emits
{ agentIdentity, githubUser, tokenSource: 'mapping' | 'legacy' } per call.
- Documentation in
learn/agentos/per-agent-credentials.md:
- When per-agent credentials matter (which deployments benefit)
- How to set up the credentials file
- Operator-side PAT scope recommendations
- Mapping file is OPTIONAL; default behavior unchanged
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback / Edge Case |
Docs |
Evidence |
NEO_GH_CREDENTIALS_FILE env var |
Operator |
Path to JSON mapping file; unset → legacy behavior |
Unset / file-missing → legacy GH_TOKEN used; logged once at startup |
learn/agentos/per-agent-credentials.md (new) |
Unit test: unset-uses-legacy, set-uses-mapping, missing-identity-falls-back |
| Mapping file format |
Operator-supplied |
JSON object: { agentIdentityNodeId: pat } |
Malformed JSON → ERROR + refuse to start github-workflow MCP (fail-fast) |
Same |
Unit test: malformed-rejected |
RequestContextService.getAgentIdentityNodeId() → token resolution |
github-workflow MCP server helper |
Maps agent ID via mapping file; falls through to legacy |
Returns null if no source available; tools refuse with structured error |
Same |
Unit test: identity-without-token-refuses-cleanly |
| Audit log per call |
github-workflow MCP server |
Logs { agentIdentity, githubUser, tokenSource } |
Standard log level |
Same |
Verify in integration test |
Acceptance Criteria
Out of Scope
- Per-agent PAT generation/rotation tooling (operator-side concern)
- Encrypted credentials file (separate hardening primitive)
- Cross-machine credentials replication
- Mandating per-agent credentials (this is an OPTIONAL enabling primitive; default behavior unchanged)
Avoided Traps / Gold Standards Rejected
- Per-agent env-var naming convention (e.g.,
GH_TOKEN_<NORMALIZED_AGENT_ID>) — superseded #10858; bakes specific identities into substrate, breaks for external operators with arbitrary agent identities. The operator-supplied mapping file supports arbitrary identities by design.
- Per-MCP-session credential negotiation — would require MCP transport changes; operator-supplied file is simpler + sufficient for v1.
- Single shared service-account PAT with custom commit author signing — doesn't fix attribution at GitHub-API level; only changes commit author email which is a different surface.
- OAuth flows replacing PAT — out of scope; current substrate uses PAT.
Related
- Supersedes #10858 (closed as design-flawed)
- Pairs conceptually with #10859 (per-domain env-file split) — both improve operator credential hygiene without mandating any single deployment shape
Origin Session ID: 8b31fd62-6a53-40b5-aae2-c5288f8ced09
Retrieval Hint: "optional per-agent GitHub credentials operator-supplied mapping file github-workflow MCP token resolution"
Context
The github-workflow MCP server currently authenticates all calls via a single
GH_TOKENenv var loaded at startup. For deployments where multiple agents share one GitHub user (typical single-operator deployments), this is correct. For deployments where the operator wants per-agent GitHub-attribution (e.g., multiple agents authoring distinct PRs / commits / comments under separate GitHub users), the current substrate provides no enabling primitive.This ticket is preventive operational substrate / MX hardening. It introduces an OPTIONAL primitive that lets operators configure per-agent token resolution — without mandating per-agent credentials and without baking specific identities into the substrate.
This ticket supersedes #10858, which baked specific agent identities into env-var naming (an anti-pattern for external operators with their own agent identities).
The Problem
For multi-agent deployments where per-agent GitHub-attribution matters:
The Architectural Reality
RequestContextService.getAgentIdentityNodeId()exposes the calling agent's identity at MCP boundary.The Fix
NEO_GH_CREDENTIALS_FILE(default unset → falls through to legacy single-token behavior){ "@neo-opus-4-7": "ghp_xxx", "@neo-gemini-3-1-pro": "ghp_yyy", "@external-deployment-agent-1": "ghp_zzz" }RequestContextService.getAgentIdentityNodeId().GH_TOKENenv var (backward-compat for single-operator deployments).{ agentIdentity, githubUser, tokenSource: 'mapping' | 'legacy' }per call.learn/agentos/per-agent-credentials.md:Contract Ledger Matrix
NEO_GH_CREDENTIALS_FILEenv varGH_TOKENused; logged once at startuplearn/agentos/per-agent-credentials.md(new){ agentIdentityNodeId: pat }RequestContextService.getAgentIdentityNodeId()→ token resolution{ agentIdentity, githubUser, tokenSource }Acceptance Criteria
NEO_GH_CREDENTIALS_FILEenv var honored by github-workflow MCP at startupGH_TOKEN{ agentIdentity, githubUser, tokenSource }per authenticated calllearn/agentos/per-agent-credentials.mddocumenting setup + when-to-useGH_TOKENdeploymentsOut of Scope
Avoided Traps / Gold Standards Rejected
GH_TOKEN_<NORMALIZED_AGENT_ID>) — superseded #10858; bakes specific identities into substrate, breaks for external operators with arbitrary agent identities. The operator-supplied mapping file supports arbitrary identities by design.Related
Origin Session ID: 8b31fd62-6a53-40b5-aae2-c5288f8ced09 Retrieval Hint: "optional per-agent GitHub credentials operator-supplied mapping file github-workflow MCP token resolution"