Direct answer

For a shared agent integration, keep upstream OAuth access and refresh tokens in an encrypted server-side store owned by the service that calls the upstream API. Give each agent a separate credential for that service, and keep token values out of model context, repositories and sandbox images. A desktop client connecting directly can use the operating system’s credential store, but its refresh tokens still need an explicit owner, refresh handling and revocation path.

Separate the agent credential from the upstream grant

A coding agent connecting through an MCP gateway crosses two authorization boundaries: agent to gateway, then gateway to the connected system. Draw those separately before choosing storage. In RFC 6749, an access token authorizes resource access; a refresh token obtains replacement access tokens from the authorization server and is not sent to resource servers. They have different destinations even when one connection record holds both.

The MCP authorization specification requires servers to validate that access tokens were issued for their own resources. Its security guidance explicitly forbids token passthrough: accepting a token intended for another resource and forwarding it downstream. The gateway should hold its own upstream authorization, rather than reuse the agent’s incoming bearer token.

My recommended ownership rule is simple: the component making upstream requests owns that upstream token lifecycle. Keep the agent-facing credential independently revocable. Removing a laptop’s gateway access should not require replacing the team’s upstream connection.

Choose storage for the component that owns refresh

Google’s OAuth best practices recommend platform credential storage, including Keychain Services on macOS, and encrypted token storage for server applications serving many users. Apply that distinction to your deployment:

  • Direct desktop integration: use the client’s supported OS credential store. Check its documented fallback before installing it on a headless VM.
  • Shared integration service: keep upstream tokens in a private encrypted datastore or suitable secret store accessible to that service’s identity.
  • Disposable agent runner: provision its separate integration-service credential at runtime. Do not bake a developer’s upstream refresh token into the base image.

These are architectural recommendations, not a claim that every MCP client uses the same storage. Inspect the actual client implementation and deployment configuration. If agent-generated shell commands run as the same user that can retrieve a local credential, a credential-store entry alone does not establish separation from that agent.

For remote runners, trace image creation, mounted home directories and persisted volumes using the guidance on credentials in agent sandboxes and VMs.

Encrypt recoverable tokens and isolate decryption

An integration service must recover an upstream token to use it. A one-way hash alone cannot serve that purpose. Use authenticated encryption through a maintained library or managed service. Google Cloud’s envelope-encryption documentation describes a data encryption key protecting the data, with that key wrapped by a centrally managed key encryption key. Store the ciphertext and wrapped key; restrict access to the wrapping key separately.

For a token store, I would associate each encrypted record with a workspace, connection, provider issuer, OAuth client identifier, granted scopes, expiry and version. Require the caller’s workspace authorization before loading the record. Bind its identity into authenticated encryption where supported so moving ciphertext between connection records cannot silently change its meaning.

Grant decryption to the upstream-calling service, rather than every agent worker or reporting job. Treat backup access and key access as separate permissions. Encryption protects stored copies within its threat model; a compromised process that can legitimately decrypt tokens can still steal them. Keep plaintext lifetimes short and exclude token values from request tracing, exception messages and administrative views.

Give refresh one coordinated writer per connection

RFC 6749 defines refresh requests at the authorization server’s token endpoint using grant_type=refresh_token. Confidential clients authenticate there. If a replacement refresh token is returned, the client must replace the old one. Store the returned access token, expiry and replacement refresh token as one consistent connection version.

RFC 9700 requires refresh tokens for public clients to use sender constraints or rotation. With rotation, the authorization server invalidates the previous refresh token and can detect its reuse. Copying one rotating token into several agent machines is therefore a poor coordination strategy.

For a shared service, I recommend coordinating refresh across replicas by connection identifier. After acquiring the refresh lease, reread the record: another replica may already have refreshed it. Publish the new version only if the connection is still enabled and its version matches. A disconnect racing with refresh must win over a late response.

Do not blindly retry after an ambiguous timeout: the provider may have rotated the token before the response disappeared. Follow its documented recovery behavior. Handle an invalid refresh grant by marking the connection as requiring authorization, rather than repeatedly attempting the same token or asking the model to repair credentials.

Make disconnect stop use and revoke the upstream token

Design two controls: blocking calls through your integration service and invalidating authorization at the provider. Deleting a database row only addresses your stored copy. RFC 7009 defines an HTTPS revocation request; this illustrative request uses fake credentials and assumes a confidential client whose provider supports HTTP Basic authentication:

POST /revoke HTTP/1.1
Host: authorization.example.com
Authorization: Basic ZGVtby1jbGllbnQ6ZGVtby1zZWNyZXQ=
Content-Type: application/x-www-form-urlencoded
Content-Length: 57

token=example-refresh-token&token_type_hint=refresh_token

Use the provider’s actual endpoint and authentication method. RFC 7009 allows propagation delay, and revoking one token does not universally guarantee that every related access token is immediately rejected. Document the provider’s behavior and test it.

My recommended disconnect flow first disables the connection and stops refresh, then invalidates service caches and attempts provider revocation. If that attempt fails, keep a restricted encrypted revocation job with bounded retries and a visible failure state; erase token material after completion under your retention policy. Plan separately for calls already in flight. The guide to revoking agent access in one place covers the agent-facing control.

Where SPYN3 keeps the upstream credentials

SPYN3 stores upstream OAuth tokens in the workspace with envelope encryption; those credentials never reach the agent, browser or logs. The agent holds only a SPYN3 API key or OAuth token for the workspace’s MCP endpoint. Keys can be scoped to chosen sources. Revoking a key or disconnecting a source in the dashboard takes effect on the next call. This gives the workspace one place to remove access through SPYN3 while keeping upstream credentials out of individual agent installations.

Test the token lifecycle before adding more runners

Use a disposable provider account to exercise the storage decision. These acceptance checks should produce evidence without printing any token:

  1. Authorize a connection, restart the owning service and confirm it can resume without a token pasted into agent context.
  2. Expire an access token and trigger simultaneous tool requests from two runners. Confirm refresh coordination preserves the current connection version.
  3. Disconnect while refresh is in progress. Confirm the delayed response cannot re-enable the connection or repopulate a usable cache.
  4. Revoke authorization at the provider. Confirm the service reports a reconnect requirement and stops repeated refresh attempts.
  5. Inspect runner images, persisted directories, traces and backup access paths for unintended credential copies.

Record which service owns refresh, who can decrypt, how revocation failures surface and what happens after restoring an older backup. A restore should reconcile disabled connections before making calls. Once those answers are concrete, assign one scoped key per agent so adding runners does not multiply upstream refresh-token owners.

Built for the next step

Put the boundary in one place

One endpoint holds the credentials, scopes and audit log, so agents only ever hold a revocable key.

Read the developer guide
About SPYN3

SPYN3 is a consolidated MCP layer: one hosted endpoint in front of every system you connect — MCP servers, Postgres, REST APIs, Google Drive — with tools foldered by source and read/write permissions per key. Switching agents means moving one URL. Free plan available; current capabilities are documented on the product facts page.

Sources

  1. RFC 6749: OAuth 2.0 Authorization Framework — authorization grants, access tokens, refresh tokens and scopes. Verified 27 Oct 2026
  2. MCP specification: authorization — OAuth-based authorization for HTTP MCP servers. Verified 27 Oct 2026
  3. MCP project: Security Best Practices, Token Passthrough — MCP token-passthrough prohibition, audience validation and risks of forwarding tokens intended for other resources. Verified 27 Oct 2026
  4. Google: OAuth 2.0 Best Practices — Platform credential stores, encrypted server-side user-token storage, token deletion and handling refresh-token invalidation. Verified 27 Oct 2026
  5. Google Cloud: Envelope encryption — Data encryption keys, centrally managed wrapping keys, storage of ciphertext with wrapped keys and separate key access controls. Verified 27 Oct 2026
  6. IETF RFC 9700: Best Current Practice for OAuth 2.0 Security — Public-client refresh tokens require sender constraints or rotation; rotation invalidates prior tokens and supports replay detection. Verified 27 Oct 2026
  7. IETF RFC 7009: OAuth 2.0 Token Revocation — HTTPS POST revocation, form fields and client authentication, propagation delays and provider-dependent handling of related tokens. Verified 27 Oct 2026