OAuth2 Access to API's in Solace Agent Mesh Enterprise

OAuth2 is one of the most useful building blocks for secure integrations because it lets an application get limited access to a protected resource without sharing a user’s password. In practice, that means you can support both user‑centric access and machine‑to‑machine access while keeping permissions scoped and revocable.

When you start connecting Agent Mesh to external systems, OAuth2 shows up in a few different ways. Some can be handled by Agent Mesh configuration, while others are OAuth2 patterns you build yourself and then plug into Agent Mesh.

What OAuth2 Is

OAuth2 is an authorization framework, not a login protocol by itself. It is designed to let a client obtain an access token from an authorization server and use that token to call an API or protected service.

The important idea is that the client gets only the access it needs. That makes OAuth2 a good fit for agent systems, where you often want to distinguish between access on behalf of a user and access the agent uses on its own.

Three OAuth2 patterns

Agent Mesh supports OAuth2 in different ways depending on where the auth flow lives. The key distinction is whether Agent Mesh is simply configured to use OAuth2, or whether you are building the OAuth2 behavior inside your own MCP server or custom agent.

Pattern Where logic lives Who the agent acts as Agent Mesh config Best fit Main tradeoff
Remote MCP server In the upstream MCP server and its auth server; the MCP server already supports OAuth2. Usually the signed-in user recognized by that remote MCP Configure the MCP tool in agent config, and Agent Mesh connects to the OAuth2-protected MCP. Fastest path when a provider already exposes an OAuth2-ready MCP server. You depend on the provider’s implementation and feature set, and there are still relatively few MCP providers in this category.
User delegated access In your custom MCP server. The current end user, via that user’s delegated access token. Agent Mesh still just points at your custom MCP server, exactly like Remote MCP Server Enterprise integrations where downstream APIs must enforce per-user permissions, auditing, and least privilege. More implementation work, because you own the custom MCP server and delegated token handling.
Machine access In your custom agent or service, typically using OAuth2 client credentials for non-interactive access. The agent itself, using its own machine identity rather than a user identity. No special Agent Mesh OAuth config is required when the token exchange happens entirely inside custom code. Internal automation, daemon-style jobs, and service-to-service integrations where no user approval flow is needed. Simpler to build, but all users often share the same effective permissions, which can lead to broader access than a delegated design.

1. Remote MCP server

This is the simplest case when the remote MCP server already supports OAuth2. In Agent Mesh, all you need to do is configure the mcp tool for an agent with an auth type of oauth2:

      tools:
        - tool_type: mcp
          connection_params:
            type: streamable-http
            url: "https://mcp.example.com/"
          auth:
            type: oauth2
            authorization_url: "https://auth.example.com/oauth/authorize" # optional
            token_url: "https://auth.example.com/oauth/token" # optional
            client_id: ${OAUTH_CLIENT_ID} # optional
            client_secret: ${OAUTH_CLIENT_SECRET} # optional
            scopes: # optional
              - "read"
              - "write"

and Agent Mesh handles the integration without extra custom work on your side. While OAuth2 support for remote MCP servers is already in Agent Mesh, there are not many remote MCP servers that actually provide it yet; Common Room is a good example.

This implementation is best when the remote MCP server already knows how to handle OAuth2 well. You get a straightforward integration path and avoid building your own token flow.

2. User delegated access

This is the most enterprise-friendly pattern. In this model, you build a custom MCP server that implements user delegated OAuth2 so the server can act on behalf of the current user rather than as a shared service account.

The important distinction is that this is not a built-in Solace Agent Mesh implementation. This is a custom OAuth2 solution that Agent Mesh uses. Agent Mesh still connects using the same configuration as a remote MCP server:

      tools:
        - tool_type: mcp
          connection_params:
            type: streamable-http
            url: "https://mcp.example.com/"
          auth:
            type: oauth2

but the delegated authorization flow itself lives in your custom MCP server. We’re not going to go into building a custom MCP server here, but we’ll use FastMCP with OAuthProxy as an example because it bridges MCP clients to traditional OAuth providers that do not support Dynamic Client Registration, including GitHub, Google, Azure, and most enterprise identity systems.

Example: FastMCP server with OAuthProxy

This is the minimal shape of a custom FastMCP server using OAuthProxy against a traditional OAuth provider.

import os

from fastmcp import FastMCP
from fastmcp.server.auth import OAuthProxy
from fastmcp.server.auth.providers.jwt import JWTVerifier

token_verifier = JWTVerifier(
    jwks_uri=os.environ["OAUTH_JWKS_URI"],
    issuer=os.environ["OAUTH_ISSUER"],
    audience=os.environ["OAUTH_AUDIENCE"],
)

auth = OAuthProxy(
    upstream_authorization_endpoint=os.environ["OAUTH_AUTHORIZATION_ENDPOINT"],
    upstream_token_endpoint=os.environ["OAUTH_TOKEN_ENDPOINT"],
    upstream_client_id=os.environ["OAUTH_CLIENT_ID"],
    upstream_client_secret=os.environ["OAUTH_CLIENT_SECRET"],
    token_verifier=token_verifier,
    base_url=os.environ["BASE_URL"],
    redirect_path="/auth/callback",
)

mcp = FastMCP(
    name="delegated-mcp-server",
    auth=auth,
)

This example follows FastMCP’s documented OAuthProxy model: configure upstream authorization and token endpoints, provide a verifier for the upstream token, and mount the proxy into the FastMCP server as the auth layer.

Example: calling a downstream API as the user

Once the user is authenticated, your tool can pull the current access token and use it to call a downstream service on that user’s behalf.

@mcp.tool
async def list_my_tickets() -> dict:
    from fastmcp.server.dependencies import get_access_token

    token = get_access_token()

    async with httpx.AsyncClient(timeout=20.0) as client:
        response = await client.get(
            f"{os.environ['SUPPORT_API_BASE_URL']}/me/tickets",
            headers={"Authorization": f"Bearer {token.access_token}"},
        )
        response.raise_for_status()
        return response.json()

This is the core delegated-access model: the MCP tool is not using a shared system credential. It is using the authenticated user’s token so the downstream API can apply user-specific authorization and auditing.

How this fits with Agent Mesh

From an Agent Mesh perspective, this still looks like an OAuth2-protected MCP server, so your agent tool yaml can remain simple:

      tools:
        - tool_type: mcp
          connection_params:
            type: streamable-http
            url: "https://mcp.example.com/"
          auth:
            type: oauth2

User delegated access vs impersonation

User delegated access refers to the delegation model where your custom MCP server acts on behalf of the authenticated user by proxying their access token to downstream APIs. Impersonation is a related but distinct implementation where the MCP server generates or exchanges tokens that make it appear as if the server itself is the user, often without clear actor/subject separation in downstream audit logs. While impersonation can achieve similar outcomes, true delegation preserves better traceability. We won’t provide an impersonation example here, but here’s how they compare:

Aspect User Delegated Access Impersonation
Token flow MCP server uses user’s token directly MCP server exchanges for user-as-itself token
Downstream sees Actor + user subject Just the user identity
Auditability Clear who acts for whom Less clear separation
Complexity More explicit consent/scopes Simpler token exchange
Agent Mesh config auth.type: oauth2 Same auth.type: oauth2

3. Machine access

This is the easiest custom implementation, and it is often the first thing teams reach for when they just want the agent to work. In this model, your custom agent authenticates with OAuth2 using its own machine identity, usually with the client credentials flow.

Like the custom MCP server implementation, this is not a built-in Solace Agent Mesh OAuth2 implementation. This is a custom solution that your agent uses. Agent Mesh does not need any special OAuth configuration here because the OAuth2 exchange happens inside your custom agent or service. MCP’s OAuth Client Credentials extension is designed exactly for this kind of machine-to-machine integration, where there is no interactive user approval flow.

Why this pattern is simple

With machine access, the agent gets an access token using its own client ID and secret, then uses that token when calling downstream services. There is no login redirect, no delegated user identity, and no per-user access boundary.

That simplicity is also the main tradeoff. Since the agent is acting as itself, every user of that agent usually gets the same permissions, which often means the machine identity ends up holding broader privileges than you would want in a user-delegated design.

Example: custom agent gets a client credentials token

This is the simplest Python example for a custom agent that needs to call a downstream API with machine access.

import os
import time
import httpx


class ClientCredentialsTokenManager:
    def __init__(self):
        self.token = None
        self.expires_at = 0

    async def get_token(self) -> str:
        now = time.time()
        if self.token and now < self.expires_at - 60:
            return self.token

        async with httpx.AsyncClient(timeout=20.0) as client:
            response = await client.post(
                os.environ["OAUTH_TOKEN_ENDPOINT"],
                data={
                    "grant_type": "client_credentials",
                    "client_id": os.environ["OAUTH_CLIENT_ID"],
                    "client_secret": os.environ["OAUTH_CLIENT_SECRET"],
                    "scope": os.environ.get("OAUTH_SCOPE", ""),
                    "audience": os.environ.get("OAUTH_AUDIENCE", ""),
                },
                headers={"Content-Type": "application/x-www-form-urlencoded"},
            )
            response.raise_for_status()
            payload = response.json()

        self.token = payload["access_token"]
        self.expires_at = now + int(payload.get("expires_in", 3600))
        return self.token

This follows the standard OAuth2 client credentials model: the service authenticates directly to the authorization server and receives an access token without any user interaction. MCP’s client-credentials extension describes this as the right fit for background services, server-to-server integrations, and daemon-style processes.

Example: use that token in a custom agent tool

Once the token is acquired, your custom agent can attach it to downstream API calls.

import os
import httpx

token_manager = ClientCredentialsTokenManager()

async def search_accounts(account_id: str) -> dict:
    access_token = await token_manager.get_token()

    async with httpx.AsyncClient(timeout=20.0) as client:
        response = await client.get(
            f"{os.environ['CRM_API_BASE_URL']}/accounts/{account_id}",
            headers={
                "Authorization": f"Bearer {access_token}",
                "Accept": "application/json",
            },
        )
        response.raise_for_status()
        return response.json()

How this fits with Agent Mesh

It’s important to remember machine access is a custom OAuth2 implementation in your custom agent or service, not an Agent Mesh feature. Agent Mesh just invokes the agent. The OAuth2 token acquisition and downstream authorization happen entirely inside custom code, so there is no need to configure auth for the agent.

Wrapping it up

When you look across the three patterns, what you’re really choosing is who owns the OAuth2 decision and who the agent is acting as.

  • Remote MCP servers are the easiest path when the upstream service already speaks OAuth2 well;
  • User delegated access is the right choice when the agent needs to carry the user’s identity and follow least‑privilege boundaries;
  • Machine access is the simplest fit for internal automation where the agent itself is the “user” and every human gets the same effective permissions.

In all cases, the real OAuth2 logic is either provided by the remote MCP, implemented in your custom MCP server, or baked into your custom agent—Agent Mesh is the integration layer, not the authorization layer.