Agent MCP + MCP Apps OAuth Guide
Set up an AI chat app so users can discover, install, approve, and OAuth-authorize MCP servers — all without leaving the chat. The approval and OAuth flows render inline as HTML cards in MCP Apps-compatible clients.
Who this is for
Developers building or configuring an AI chat app that supports MCP Apps UI (inline HTML rendering for tool results). This guide covers the server-side setup (registering OAuth configs with Pharos) and the user-facing flow (what the user sees in chat).
Install the Pharos MCP Server
The MCP server runs alongside your chat app as a stdio process. One command installs it and auto-configures supported clients.
pip install pharos-mcpFor manual configuration (clients not auto-detected), add this to your client's MCP config:
{
"mcpServers": {
"pharos": {
"command": "pharos-mcp"
}
}
}Register OAuth providers
If you publish an MCP server that requires OAuth (Salesforce, Google Drive, Slack, etc.), register your OAuth provider details with the Pharos registry. This lets Pharos handle the OAuth flow inline — the user authorizes without leaving the chat.
# Register your OAuth provider with Pharos
pharos oauth configure my-salesforce-server \
--auth-url https://login.salesforce.com/services/oauth2/authorize \
--client-id 3MVG9...your-client-id \
--scopes api,refresh_token \
--pkce
# With a server-side secret (optional — for confidential clients)
pharos oauth configure my-google-drive-server \
--auth-url https://accounts.google.com/o/oauth2/v2/auth \
--client-id your-client-id.apps.googleusercontent.com \
--scopes openid,email,profile,drive.file \
--pkce \
--client-secret your-client-secretFlags
--auth-url— OAuth authorization endpoint URL (required)--client-id— Your registered client ID (required)--scopes— Comma-separated scopes to request--pkce— Require PKCE (default: true, recommended for public clients)--client-secret— For confidential clients (server-side secret handling)
The user flow in chat
Here is the exact sequence that happens when a user asks your chat app to work with an OAuth-enabled MCP server. The agent chains tool calls; the user only interacts with the inline approval and OAuth cards.
# Step 1: Agent searches for a server
pharos_search(query="salesforce", limit=5)
→ Returns results, including:
{ "id": "my-salesforce-server", "name": "Salesforce MCP Server", ... }
# Step 2: Agent installs it
pharos_install(server_id="my-salesforce-server")
→ { "status": "installed" }
# Step 3: Agent requests connection (returns pending — NOT auto-approved)
pharos_connect(server_id="my-salesforce-server", purpose="Access Salesforce CRM data")
→ Returns:
{
"status": "pending_approval",
"approval_token": "pc-my-salesforce-server-1723540800-a3b1c2",
"expires_in": 300,
"approval_data": {
"server": {
"id": "my-salesforce-server",
"display_name": "Salesforce MCP Server",
"version": "1.0.0",
"publisher": { "name": "io.github.yourusername", "verified": true }
},
"purpose": "Access Salesforce CRM data",
"scopes": ["tools:call"],
"capabilities": ["tools"]
}
}The inline approval card
When pharos_connect returns pending_approval, MCP Apps-compatible clients render an HTML approval card inline. The server attaches this via the ui://pharos/approval resource. The card shows the server name, publisher verification status, requested scopes, and purpose.
┌──────────────────────────────────────────┐ │ 🔒 PHAROS — Connection Approval │ │ │ │ Salesforce MCP Server v1.0.0 │ │ Publisher: io.github.yourusername ✓ │ │ Purpose: Access Salesforce CRM data │ │ │ │ Requested scopes: │ │ → tools:call │ │ │ │ [ Deny ] [ Approve ] │ └──────────────────────────────────────────┘
The user clicks Approve. The client calls pharos_approve with the token. The token is HMAC-signed and expires after 5 minutes.
The inline OAuth consent screen
If the server has an OAuth config registered (via pharos oauth configure), the client also renders the OAuth consent screen inline via the ui://pharos/oauth resource. The user authorizes the OAuth scopes without leaving the chat — no browser redirect, no separate login window.
# The MCP Apps client renders the OAuth consent UI inline.
# The server returns an HTML resource at ui://pharos/oauth:
🔐 PHAROS — OAuth Authorization
┌──────────────────────────────────────┐
│ Salesforce MCP Server │
│ is requesting access to your account.│
│ │
│ → api │
│ → refresh_token │
│ │
│ [ Cancel ] [ Authorize ] │
└──────────────────────────────────────┘
# The user clicks "Authorize" — the client sends the approval back
# via postMessage. The server completes the OAuth flow with the
# provider and returns the connection result.Complete the flow
After both approvals (connection + OAuth), the agent can call tools on the server. The OAuth token is managed by Pharos — the agent never sees it.
# Step 4: User approves the connection
pharos_approve(approval_token="pc-my-salesforce-server-1723540800-a3b1c2")
→ Returns:
{
"status": "connected",
"server_id": "my-salesforce-server",
"tools_count": 12,
"tools": [
{ "name": "query_soql", "description": "Run a SOQL query" },
{ "name": "create_record", "description": "Create a Salesforce record" },
...
]
}
# Step 5: Agent calls a tool — the OAuth token is already handled
pharos_call_tool(
server_id="my-salesforce-server",
tool_name="query_soql",
arguments={"query": "SELECT Name, Email FROM Contact LIMIT 5"}
)
→ { "result": [{ "Name": "Jane Doe", "Email": "[email protected]" }, ...] }How the pieces fit
- 1. Publisher registers OAuth config via
pharos oauth configure. This stores the auth URL, client ID, scopes, and PKCE setting in the registry. - 2. User asks the chat app to find and install a server. The agent calls
pharos_search+pharos_install. - 3. Agent calls
pharos_connect. Gets back a pending approval token — not connected yet. - 4. Client renders the inline approval card (
ui://pharos/approval). User reviews and clicks Approve. - 5. If OAuth: Client renders the OAuth consent screen (
ui://pharos/oauth). User authorizes scopes. Pharos completes the OAuth dance with the provider. - 6. Agent calls
pharos_approvewith the token. Connection established. Tools available viapharos_list_toolsandpharos_call_tool.
Client requirements
The inline approval and OAuth cards require an MCP Apps-compatible client — one that renders HTML resources with the text/html;profile=mcp-app content type. The client must:
- ✓Render HTML tool results inline in the chat (not as a separate window)
- ✓Handle
postMessageevents from the embedded HTML (for Approve/Deny and Authorize/Cancel button clicks) - ✓Support the
ui/getToolResultJSON-RPC method to receive tool result data for rendering
Clients without MCP Apps support can still use Pharos — they just get JSON responses instead of inline cards. The agent can still call pharos_approve programmatically; the user just does not get a visual approval card.