Skip to content
8 min read

Should MCP agent authentication use static secrets?

MCP agent authentication needs short-lived tokens, narrow scopes, approval gates, tested rotation, and audit logs that explain every production action.

Should MCP agent authentication use static secrets?
Table of Contents

Production MCP agents should use short-lived OAuth access tokens, not static production secrets. A static key may be simpler during a prototype, but in production it gives an unpredictable process durable authority with weak attribution and an awkward revocation path. That trade is rarely defensible when customer data or production state is reachable.

OAuth does not make an agent safe by itself. A ten-minute bearer token with production.admin still gives the agent ten minutes to do damage, and an approval dialog that exposes only a tool name gives the human almost nothing useful to judge. Safe access comes from five controls working together: identity, scope, lifetime, approval, and evidence.

The distinction I enforce is simple. The agent may hold an expiring capability for one resource and one bounded task. The system around the agent owns durable credentials, policy, refresh, and revocation. That boundary keeps a prompt injection, a bad plan, or a leaked trace from turning into permanent production access.

Static secrets solve the wrong operational problem

Static secrets optimize for connection setup, not for production control. An engineer creates a key once, stores it in an environment variable, and every run succeeds until somebody rotates it. That convenience hides four costs: the key often has broad permissions, every agent instance looks like the same principal, expiry never limits an incident, and rotation becomes a coordinated deployment.

The worst version is a vendor API key copied into an MCP server configuration and then inherited by every process that launches the server. A local agent, a CI job, and an unattended production worker now share one identity. When an unexpected write appears, the vendor log can tell you which key acted, but not which agent run, user request, policy decision, or approval produced it.

A short-lived token changes the failure from "the attacker can keep using this until we find and rotate it" to "the attacker can use this until expiry, within the token's audience and scopes." That is a material reduction, but only if the issuer refuses broad refresh and the resource server validates the restrictions. Expiry without enforcement is decoration.

There is one legitimate reason teams reach for static credentials: many production tools still offer nothing else. Treat that as a compatibility constraint, not as the desired architecture. Put the secret behind a narrow broker, let the broker issue an internal capability to the agent, and keep the original value out of prompts, tool results, traces, and agent process memory where possible.

Do not confuse a secret stored in a vault with a short-lived credential. A vault improves storage and retrieval, but once an agent reads a static secret, the secret is still durable bearer authority. The safer pattern lets the workload prove its identity to the vault or token service and receive a limited token without ever receiving the root credential.

Agent identity and user authority are different

An agent needs its own workload identity even when it acts for a signed-in user. The workload identity answers which deployed process made the call. Delegated user authority answers whose request allowed it. Collapsing those identities makes incident review and offboarding unreliable.

Consider a deployment tool invoked by an engineering assistant. The user may have permission to deploy the billing service, while the agent workload may run only from the production orchestrator. The authorization decision should require both facts. A token or the server-side grant behind it should bind the user, agent instance, environment, target service, and requested operation.

For a fully unattended job, there may be no current user. Do not fabricate one or borrow the account of the engineer who created the job. Issue a service grant to a named workload, record who configured it, and cap its permissions to the scheduled task. Human ownership belongs in the configuration and audit trail; runtime authority belongs to the service identity.

This distinction also controls revocation. If an employee leaves, revoke their grants without disabling unrelated service jobs. If one agent deployment is compromised, revoke that workload without signing every user out. If a user withdraws consent for a connected service, terminate that delegation even if the agent itself remains healthy.

I use a principal tuple rather than one vague actor field:

{
  "workload": "agent:release-orchestrator:v17",
  "subject": "user:1842",
  "session": "run:01JZ8F4M2K",
  "tenant": "acme-prod"
}

The exact names do not matter. Keeping the dimensions separate does. The resource server must make its decision from verified claims or trusted server-side state, not from identity strings supplied by the model in tool arguments.

Scopes must map to real operations

A scope should grant an operation a resource server can enforce, not express a vague job title. Scopes such as admin, agent, and full_access push the real authorization decision into prompt text. The model may have been told to act carefully, but the server still sees a caller allowed to do almost anything.

Start from the tool catalog and classify each operation by resource, action, environment, and consequence. Reading a deployment status differs from starting a deployment. Creating a database backup differs from restoring one. Drafting a refund differs from issuing it. If those pairs share one permission, split the permission or put an explicit server-side policy between them.

A practical scope map might look like this:

  • Read deployment status: deployments.read, constrained to a named service, with no approval.
  • Start staging deployment: deployments.start, constrained to staging, with a policy check.
  • Start production deployment: deployments.start, constrained to a release ID and production, with human approval.
  • Read sanitized logs: logs.read, constrained to a service and time range, with no approval.
  • Change runtime configuration: config.write, constrained to approved fields, with human approval.

OAuth scopes alone usually cannot express service names, row ownership, spend limits, or allowed configuration fields. Put those restrictions in an authorization policy evaluated by the MCP server or a policy service. The scope permits a class of action; policy decides whether this call belongs inside the class.

RFC 9700 recommends restricting token privileges to the minimum needed and audience-restricting access tokens to a specific resource server when possible. I would make both requirements for production agents. An access token for the deployment MCP server should fail at the source-control MCP server even if both servers trust the same issuer.

RFC 8707 supplies the resource parameter for that audience restriction. The current MCP authorization specification also requires an MCP server to validate that a token was issued for that server. This blocks a malicious or confused server from collecting a token intended for another production resource and replaying it there.

Avoid minting a union token for an entire multi-tool plan. If a task reads an issue, updates a branch, and proposes a deployment, obtain separate audience-bound tokens as each resource is reached. The planner can retain the plan; it does not need to retain simultaneous authority over every system in the plan.

Token lifetime limits time, not intent

Token lifetime should match the useful execution window of the operation. It should not match the length of a workday merely because eight hours feels familiar. Most MCP calls finish in seconds, while a bounded agent run may need several minutes. Issue enough time for expected retries and network delay, then force a fresh policy decision.

There is no universal correct duration. A read-only token for a long log investigation can live longer than a token that may change production traffic. A token used once to approve a particular deployment can be both short-lived and bound to that deployment ID. The more consequential and replayable the permission, the shorter and more specific the capability should be.

Short lifetimes do not repair overbroad scopes. If an agent can delete every project, five minutes is plenty. Nor do they stop immediate misuse after theft. Audience restriction, resource policy, sender constraints where supported, network controls, and approval gates still carry most of the safety argument.

Use expiry as part of normal operation, not as an exceptional error. The MCP client should handle an HTTP 401 by discarding the expired token, returning to the trusted token path, and obtaining a new grant only if the run is still authorized. It must not fall back to a static key hidden in configuration.

Keep clock skew small and observable. Servers should reject expired tokens and tokens whose activation time is implausibly far ahead, while allowing a documented, modest skew for distributed systems. Log the issuer, token identifier or safe fingerprint, issued time, expiry, audience, and scopes, but never log the bearer value.

A useful policy starts with risk bands rather than one global number:

  • Read-only, sanitized data can receive a task-length token.
  • Reversible writes need a shorter token plus a resource constraint.
  • Irreversible or high-impact writes need transaction binding and approval.
  • Background jobs need renewable workload grants with a maximum session age.

Measure actual task duration and renewal frequency before tuning. If agents renew on nearly every call, the lifetime may be too short or the client may be caching incorrectly. If tokens routinely survive long after runs finish, revoke them at run termination or reduce their lifetime.

Approval gates belong at the side-effect boundary

Price the authentication gap
A $5,000 audit identifies engineering savings before you invest in production agent access.

An approval gate works only when it shows the exact effect the server is ready to execute. Asking "Allow deployment tool?" before planning begins is weak consent. The person cannot yet see the target, version, environment, or policy exceptions, and repeated generic prompts train people to approve by reflex.

Place approval after the agent has prepared a typed request and after automated policy has checked it, but before the side effect. For a production deployment, show the service, release identifier, environment, change summary, expected health check, and rollback target. For a payment, show the payee, amount, currency, invoice, and duplicate check. The MCP server should calculate this preview from trusted data rather than accept a prose summary from the model.

Bind the approval to a digest of the normalized request. If any material field changes after approval, the digest changes and the server asks again. This closes a common gap where a human approves one preview but the agent later alters the arguments during a retry.

{
  "action": "deployments.start",
  "resource": "service:billing-api",
  "environment": "production",
  "release_id": "rel_8421",
  "request_digest": "sha256:7c1d...9a20",
  "expires_in_seconds": 120
}

The approval record should grant this action, not upgrade the whole session. A one-time capability redeemed by the server is cleaner than adding production.write to a general token. Reject it after use, after expiry, or when the request digest differs.

Not every write needs a human. An agent that creates an isolated branch or opens a draft change may operate under policy because the action is contained and reviewable. Escalate when the action crosses a trust boundary: production state, external communication, sensitive data export, security policy, money, destructive operations, or a permission increase.

Do not let the agent decide whether approval is necessary. The resource server or an independent policy service owns that rule. The model can classify intent for routing, but deterministic policy must block execution when required facts or approval are missing.

Batch approval deserves suspicion. Approving "all remaining steps" is acceptable only when the batch contains an explicit, immutable set of operations and the user can inspect it. Open-ended approval for whatever the model decides later is simply a long-lived broad grant with a friendlier interface.

Refresh authority needs stricter custody

A short-lived access token provides little protection if the same agent holds a refresh token that can mint replacements indefinitely. Treat refresh authority as a durable credential. Store it in a hardened broker, bind it to the client where possible, rotate it, and never expose it through MCP tool parameters or model context.

RFC 9700 requires public clients that receive refresh tokens to use sender-constrained refresh tokens or refresh token rotation. Rotation means every successful refresh returns a new refresh token and invalidates the previous one. If both an attacker and the legitimate client try to use the same token family, replay detection can revoke the active family and force a new grant.

That mechanism detects reuse; it does not make theft harmless. A thief who uses the token first may look legitimate until the original client refreshes. Set an inactivity expiry and a maximum grant age, revoke on security events, and alert on reuse. For a high-impact unattended agent, prefer workload identity and token exchange over a user refresh token that survives for months.

There are three credential layers worth separating:

  • The workload proof identifies the approved agent runtime to the token broker.
  • The refresh grant or service authorization represents durable delegated authority.
  • The access token is the short-lived capability presented to one MCP server.

Only the last layer belongs near the agent process. Even then, keep it out of the prompt and pass it through the transport or credential provider. Tool descriptions, arguments, and results should never contain tokens because those surfaces commonly enter traces and debugging transcripts.

For machine-to-machine MCP work, OAuth client credentials can remove interactive consent, but a client secret copied into every agent container recreates the static-secret problem. Use the platform's workload identity, a private key held by a managed signer, or another sender-bound mechanism when the infrastructure supports it. The authorization server should still issue a limited access token, not expose the upstream credential to the MCP server.

Do not pass through a token received for some other API. The MCP server is its own resource server and should accept only tokens intended for its canonical resource identifier. Token exchange or a broker can derive a downstream token while preserving the subject and workload chain in trusted claims.

Rotation must be exercised before an incident

Find savings in five days
The audit guarantees at least $50,000 in annual identified savings, or the $5,000 fee is waived.

A rotation procedure that nobody has run is documentation, not a control. Teams often rotate a static key by adding a second key, redeploying clients, watching errors, and deleting the first. The fragile part is discovering every hidden consumer before deletion, which is exactly why static shared keys persist.

For unavoidable static upstream secrets, place the rotation behind the broker and test this sequence on a schedule:

  1. Create the replacement secret with permissions no broader than the old one.
  2. Configure the broker to recognize both versions while issuing the same narrow internal capabilities.
  3. Shift new upstream calls to the replacement and watch authentication errors by credential version.
  4. Revoke the old secret, then verify that a synthetic call using its fingerprint fails.
  5. Remove the overlap configuration and record who completed the rotation.

The agent should not notice this rotation. If agent containers need a restart or configuration update, the durable secret has leaked across the boundary. Fix the distribution path before congratulating the team on rotating quickly.

Run a second drill for token revocation. Start an agent task, mint a token, revoke the workload or grant, and confirm the next call fails even if the JWT has not expired. Self-contained JWT validation can leave a revocation delay unless the server uses short expiry, introspection, a deny list, or another revocation signal. Choose that trade consciously for each risk band.

A recognizable failure goes like this. A support agent receives a vendor key that can read and update every account. A debugging trace captures an HTTP header. The trace enters a searchable log store, and rotation is delayed because three unrelated jobs use the same key. Redacting the header helps, but the architecture created the incident: durable shared authority crossed into a highly observable process.

The corrected path gives the support workload a verified identity, mints a token for one MCP server and one tenant, permits reads by policy, and requires a bound approval for an account update. A leaked trace contains no bearer value; a stolen access token expires soon and fails outside its audience. Rotation affects the broker's upstream connection rather than every agent.

Test failure behavior too. When issuance is unavailable, the agent must stop or continue only with already authorized read operations. It must not select a more powerful fallback credential, skip approval, or repeat a side effect whose result is unknown. Authentication degradation should reduce authority, never increase it.

Audit logs must reconstruct the decision

A useful audit trail can answer who asked, which workload acted, what the agent proposed, what policy allowed, what a human approved, which credential authorized the call, and what the resource changed. A vendor log containing a timestamp and shared API key answers only the easiest part.

Record one correlation ID across the user request, agent run, token issuance, approval, MCP tool call, and downstream result. Keep the original user instruction or a protected reference to it, the selected tool, normalized arguments with sensitive values redacted, the policy version, approval digest, token audience and scopes, server decision, and result identifier.

A structured event can use this shape:

{
  "event": "mcp.tool.completed",
  "correlation_id": "corr_01JZ8EYKQ9",
  "run_id": "run_01JZ8F4M2K",
  "workload": "agent:release-orchestrator:v17",
  "subject": "user:1842",
  "tool": "deployments.start",
  "resource": "service:billing-api",
  "decision": "allow",
  "policy_version": "prod-deploy-12",
  "approval_id": "apr_771",
  "request_digest": "sha256:7c1d...9a20",
  "token_audience": "mcp://deployments.prod",
  "token_scopes": ["deployments.start"],
  "result": "deployment:dep_9914"
}

Do not log access tokens, refresh tokens, authorization codes, client assertions, session cookies, or raw secrets. Hashing a low-entropy API key does not make it safe because an attacker can test guesses. Use an issuer-provided token ID, a random credential version, or a keyed fingerprint calculated inside the trusted boundary.

Logs also need integrity and access control. If the same compromised agent can edit its history, the trail cannot support an investigation. Send security events to append-oriented storage, restrict deletion, record administrative access, and define retention from the incident and compliance needs of the system rather than keeping everything forever.

Alert on behavior that links the controls: refresh-token reuse, an audience mismatch, repeated 403 responses followed by a newly requested broad scope, approvals near expiry, changed request digests, calls after run termination, and one workload touching an unusual number of tenants. A single failed call may be noise. A sequence often explains intent.

Audit the denies, not just successful writes. Denials reveal probing, broken scope maps, stale clients, and agents trying operations their plan did not justify. They also prove that a gate worked when the downstream system never received a call.

The production design keeps durable power outside the agent

Replace shared keys deliberately
Move from ad hoc MCP experiments to an AI team transformation led by an experienced CTO.

A production design should force every consequential call through a small number of verifiable transitions. The user or scheduler starts a run. The runtime proves its workload identity. A broker evaluates the grant and issues an audience-restricted access token. The MCP server validates the token, evaluates resource policy, requests bound approval when required, invokes the downstream tool, and writes a correlated audit event.

The model participates in planning and argument generation. It does not mint its own scopes, label its own call safe, handle refresh credentials, or write the authoritative audit decision. Those controls sit in deterministic services with tests and owners.

Use this review checklist on each production tool:

  • Can the server distinguish workload, user, tenant, and run?
  • Does the token target one resource and the minimum useful scope?
  • Can policy constrain the specific object, environment, amount, or field?
  • Does approval bind the final normalized request and expire after one use?
  • Can one correlation ID reconstruct issuance, decision, call, and result?

Implement read paths before write paths, but do not mistake read-only for harmless. Source code, customer records, logs, and infrastructure metadata can carry secrets. Sanitize responses, cap query ranges, enforce tenant boundaries, and prevent the agent from requesting arbitrary URLs through a convenient fetch tool.

Keep authorization metadata discoverable and standards-compliant. The MCP authorization specification uses protected resource metadata and authorization server discovery so clients can find the correct issuer. Discovery reduces hand-written configuration, but clients must validate issuer relationships and servers must reject tokens minted for a different resource.

Static secrets can remain behind a broker when a downstream vendor leaves no alternative. They should have one owner, one purpose, minimal vendor permissions, a version, a rotation deadline, and usage telemetry. The exception ends at the broker. It does not justify placing the secret in an agent environment variable.

Static credentials have a narrow exception

A static credential can be acceptable for a contained local integration when the target has no OAuth support, the credential is low privilege, the environment has a short life, and no sensitive production data is reachable. Even there, use a distinct key per workload, store it outside prompts, redact transport headers, and automate deletion when the environment ends.

Production breaks that exception quickly. A read key may expose source, logs, or customer data. A test environment may share an identity provider, artifact store, or network path with production. A supposedly temporary agent often becomes a scheduled job. Classify the reachable consequences rather than trusting labels such as "read only" and "staging."

Do not build a full OAuth service merely to wrap one harmless internal script. A broker-issued random capability with a short expiry and server-side policy may provide the needed properties. The goal is not protocol purity. The goal is to prevent durable, reusable production authority from sitting inside a probabilistic and heavily logged runtime.

When choosing between OAuth and a static secret, ask where revocation, scope enforcement, and attribution happen. If the answer is "in the prompt" or "during the next deployment," the design is unfinished. Put durable authority behind a trusted boundary, issue the agent the smallest capability that can finish the current action, and make the resulting decision possible to reconstruct without reading the model's mind.

Frequently Asked Questions

Can an MCP agent safely use a static API key?

Only in a contained, low-impact integration where the target offers no better option. Use a separate key per workload, keep it outside the prompt, and put production vendors behind a broker whenever possible.

How short should an MCP access token lifetime be?

Match it to the expected task plus modest retry time, then shorten it for high-impact or replayable actions. Measure real run duration instead of choosing one lifetime for every tool.

Does OAuth make MCP agents secure?

No. OAuth supplies a framework for issuing and validating limited credentials, but scopes, audience checks, resource policy, approval binding, and audit evidence determine whether the deployment is safe.

Should an agent receive a refresh token?

Usually not. Keep refresh authority in a broker or credential provider and give the agent only the access token intended for the current MCP server.

What scopes should production MCP tools use?

Use enforceable operation scopes such as deployments.read and deployments.start, then apply server-side constraints for service, tenant, environment, amount, or fields. Avoid vague grants such as agent or admin.

When does an MCP tool call need human approval?

Require approval when the call changes production, sends external communication, moves money, exports sensitive data, weakens security, deletes data, or increases permission. Bind approval to the exact normalized request.

Can read-only MCP access skip approval?

Sometimes, but read-only access still needs tenant boundaries, response sanitization, range limits, and narrow scopes. Source, logs, and customer records can cause serious harm without a write.

What should an MCP audit log record?

Record the user, workload, run, tool, normalized resource, policy version, approval, token audience and scopes, decision, and result under one correlation ID. Never record bearer tokens or refresh credentials.

How do I rotate a production secret without breaking agents?

Rotate the upstream secret inside a broker that temporarily accepts both credential versions, shift traffic, revoke the old version, and test that it fails. Agents should continue receiving the same narrow internal capabilities.

Should each MCP server get a different token?

Yes. Issue tokens for one intended resource so a token captured by one server fails at another, and avoid union tokens that cover an agent's entire multi-tool plan.

Related Posts