Skip to main content
When building AI agents that use MCP Gateway, you often need to access MCP servers on behalf of your end users. This guide explains how to handle authentication for different MCP server types and user scenarios.
This guide is for developers building their own AI agents that connect to MCP Gateway directly using the MCP SDK (streamable-http transport). If you’re using TrueFoundry’s Agent API, authentication is handled automatically.

Overview

The approach for on-behalf-of authentication depends on two factors:
  1. MCP Server Auth Type: No Auth, Header Auth, OAuth2, or Token Passthrough
  2. End User Type: TrueFoundry platform user or non-TrueFoundry user
MCP Auth TypeEnd User TypeApproach
No AuthAnyVirtual Account token
Header AuthAnyVirtual Account token
OAuth2TrueFoundry userUser’s TF token + OAuth flow
OAuth2Non-TrueFoundry userExternal Identity + OAuth flow
Token PassthroughAnyExternal Identity + user’s JWT

Understanding MCP Auth Types

When registering an MCP server, you choose an authentication type that determines how requests are authenticated with the upstream MCP server.
Terminology: In this guide, “you” refers to the developer building an AI agent using TrueFoundry. “Your end users” refers to the people who use your AI agent (your customers).

No Auth

The MCP server doesn’t require any authentication. Use for public APIs or demo servers.

Header Auth (Static Headers)

The MCP server requires a static API key or token that is the same for all requests. You configure this credential once, and TrueFoundry injects it into every request regardless of which end user is calling. Example: An MCP server that uses a shared API key.

OAuth2

The MCP server requires end-user-specific OAuth tokens to access external services like GitHub, Slack, or Atlassian. Each end user must complete an OAuth consent flow to grant your agent access to their resources on those services. Use OAuth2 when:
  • The MCP server connects to third-party services (GitHub, Slack, Atlassian, Google, etc.)
  • End users need to access their own accounts on those services (their GitHub repos, their Slack channels)
  • You want TrueFoundry to manage OAuth token storage and refresh
How it works:
  1. End user authenticates with your system (TrueFoundry account or via External Identity)
  2. End user calls your agent → MCP Gateway returns auth error with authorization URL
  3. End user visits URL and completes OAuth consent on the third-party service (e.g., GitHub)
  4. TrueFoundry stores the OAuth token for that end user
  5. Future requests automatically include the end user’s OAuth token
Key point: End users authenticate twice - once with your system, and once with the third-party service (GitHub, Slack, etc.) to grant access to their resources there.

Token Passthrough

The MCP server expects to receive a JWT that you issue to your end users. TrueFoundry passes this JWT directly to the MCP server without any transformation. Use Token Passthrough when:
  • You have your own IdP (e.g., Auth0, Okta, Cognito) that authenticates your end users
  • The MCP server is configured to validate JWTs from your IdP
  • You want to control authentication entirely through your own system
How it works:
  1. Your end user authenticates with your IdP and receives a JWT
  2. End user calls your agent with their JWT
  3. TrueFoundry passes the JWT directly to the MCP server
  4. The MCP server validates the JWT against your IdP’s configuration
Key point: This is for when the MCP server trusts your IdP. Your end users authenticate with you, and that authentication is passed through.

OAuth2 vs Token Passthrough: Which to Choose?

QuestionIf Yes →
Does the MCP server connect to third-party services (GitHub, Slack, etc.)?OAuth2
Does the MCP server validate tokens from your IdP?Token Passthrough

1. Virtual Account

Use when: MCP server has No Auth or Header Auth, and you want a shared token for all your end users. Virtual Accounts allow you to create a service account with specific permissions to access MCP servers. Your application uses the Virtual Account token to authenticate all requests, regardless of which end user is making the request.
1

Create a Virtual Account

  1. Navigate to Settings > Virtual Accounts in the TrueFoundry UI
  2. Click Create Virtual Account
  3. Give it a descriptive name (e.g., my-agent-mcp-access)
  4. Add permissions for the MCP servers your agent needs to access
2

Generate a token

After creating the Virtual Account, generate a token. This token will be used by your application to authenticate with MCP Gateway.
3

Use the token in your agent

from fastmcp import Client
from fastmcp.client.transports import StreamableHttpTransport

# Virtual Account token - same for all end users
VA_TOKEN = "your-virtual-account-token"

async def call_mcp_tool(tool_name: str, tool_args: dict):
    mcp_url = "https://<control-plane-url>/api/llm/mcp/<integration-id>/server"
    
    transport = StreamableHttpTransport(
        url=mcp_url,
        headers={"Authorization": f"Bearer {VA_TOKEN}"}
    )
    
    async with Client(transport) as client:
        result = await client.call_tool(tool_name, tool_args)
        return result
Virtual Account tokens provide the same level of access for all requests. For MCP servers that require user-specific access (like accessing a user’s GitHub repositories), use OAuth2 instead.

Handling OAuth Flows

When connecting to an OAuth2-protected MCP server, if the user hasn’t completed OAuth consent, the MCP Gateway returns an error containing the authorization URL. Your agent needs to extract this URL and redirect the user.
import re
from fastmcp import Client
from fastmcp.client.transports import StreamableHttpTransport

def get_auth_error_data(exception: Exception) -> str | None:
    """Traverse exception chain to find auth error with URL."""
    current = exception
    while current is not None:
        try:
            data = current.error.data
            if data and "Please visit:" in str(data):
                return str(data)
        except AttributeError:
            pass
        current = current.__cause__
    return None


def extract_auth_urls(error_data: str) -> list[str]:
    """Extract authorization URLs from MCP Gateway error."""
    match = re.search(r"Please visit:\s*(.+)$", error_data)
    if match:
        # URLs are separated by " , " (space-comma-space)
        return [url.strip() for url in match.group(1).split(" , ")]
    return []


async def call_mcp_with_oauth(
    mcp_url: str,
    auth_token: str,  # User's TrueFoundry PAT or External Identity JWT
    tool_name: str,
    tool_args: dict
):
    transport = StreamableHttpTransport(
        url=mcp_url,
        headers={"Authorization": f"Bearer {auth_token}"}
    )
    
    try:
        # Note: OAuth errors occur during connection, not tool calls
        async with Client(transport) as client:
            result = await client.call_tool(tool_name, tool_args)
            return {"success": True, "result": result}
            
    except Exception as e:
        error_data = get_auth_error_data(e)
        if error_data:
            auth_urls = extract_auth_urls(error_data)
            if auth_urls:
                return {
                    "success": False,
                    "auth_required": True,
                    "authorization_urls": auth_urls
                }
        raise


# Usage
result = await call_mcp_with_oauth(mcp_url, user_token, "get_repositories", {})

if result.get("auth_required"):
    # In a web app: redirect user to the authorization URL
    # In a CLI: print the URL for user to visit
    for url in result["authorization_urls"]:
        print(f"Please authorize access: {url}")
    
    # After user completes OAuth, retry the request
    # The MCP Gateway stores the OAuth token and future requests will succeed

2. OAuth2 for TrueFoundry Users

Use when: MCP server uses OAuth2 authentication, and your end users are TrueFoundry platform users. When your end users have TrueFoundry accounts, they can use their Personal Access Token (PAT) to authenticate. The MCP Gateway manages OAuth2 tokens for each user automatically.
1

User generates their PAT

Each end user generates their own Personal Access Token from TrueFoundry:
  1. Navigate to Settings > API Keys
  2. Generate a new API key
  3. Provide this token to your application
2

Call MCP Gateway with user's PAT

Use the user’s PAT as the auth_token in the call_mcp_with_oauth function shown in Handling OAuth Flows.If the user hasn’t completed OAuth consent for this MCP server, you’ll get back auth_required: True with the authorization URL. Redirect them to complete OAuth, then retry.

3. OAuth2 for Non-TrueFoundry Users

Use when: MCP server uses OAuth2 authentication, and your end users are NOT TrueFoundry platform users (e.g., your own customers in a B2B scenario). In this scenario:
  1. You have your own platform with your own IdP (e.g., Auth0, Okta, Cognito)
  2. Your end users login to your platform and get a JWT
  3. You pass that JWT to the MCP Gateway
  4. TrueFoundry validates the JWT and allows access to MCP servers
You need to set up an External Identity in TrueFoundry that trusts JWTs from your IdP.
1

Configure SSO Integration

Register your IdP with TrueFoundry:
  1. Navigate to Settings > SSO
  2. Create a new SSO configuration pointing to your IdP
  3. Important: Set login enabled to false (this SSO is only for external identity, not platform login)
  4. Note the SSO FQN for the next step
2

Create External Identity

Create an External Identity that maps JWTs from your IdP to TrueFoundry access:
  1. Navigate to Access > External Identities
  2. Click Add External Identity
  3. Select the SSO FQN from step 1
  4. Optionally add claims to match specific users/tenants
For detailed instructions, see Setup External Identity.
3

Grant MCP server access

Add the External Identity as a collaborator on the MCP server:
  1. Navigate to the MCP server’s settings
  2. Go to Collaborators
  3. Add the External Identity with appropriate permissions
4

Call MCP Gateway with end user's JWT

When your end user logs into your platform, your application receives their JWT. Use that JWT as the auth_token in the call_mcp_with_oauth function shown in Handling OAuth Flows.If the user hasn’t completed OAuth consent for this MCP server, you’ll get back auth_required: True with the authorization URL. Redirect them to complete OAuth, then retry.After completing OAuth, the token is stored and associated with their External Identity.

4. Token Passthrough

Use when: MCP server uses Token Passthrough authentication, where the user’s JWT is passed directly to the MCP server. Token Passthrough is used when the MCP server itself validates the user’s token (rather than relying on OAuth2 token exchange). This requires an External Identity setup.
1

Set up External Identity

Follow steps 1-3 from OAuth2 for Non-TrueFoundry Users to create an External Identity for your users.
2

User obtains JWT from OAuth provider

Your end user authenticates with the OAuth provider that the MCP server expects and obtains a JWT token.
3

Pass the token through

The user’s JWT is automatically passed through to the MCP server:
from fastmcp import Client
from fastmcp.client.transports import StreamableHttpTransport

async def call_passthrough_mcp(
    mcp_url: str,
    user_jwt: str,  # JWT from the OAuth provider the MCP server expects
    tool_name: str,
    tool_args: dict
):
    transport = StreamableHttpTransport(
        url=mcp_url,
        headers={"Authorization": f"Bearer {user_jwt}"}
    )
    
    async with Client(transport) as client:
        result = await client.call_tool(tool_name, tool_args)
        return result
With Token Passthrough, the MCP server validates the token directly. Make sure the JWT is valid and has the required claims/scopes for the MCP server.