WorkingAgents implements OAuth 2.1 with PKCE and Dynamic Client Registration. Any compliant MCP client can authenticate users without manual token management. This article covers three connection methods: automatic OAuth flow, manual OAuth, and direct bearer tokens.
How It Works
WorkingAgents publishes its OAuth configuration at:
GET https://workingagents.ai/.well-known/oauth-authorization-server
A compliant MCP client fetches this on first connection, discovers the registration, authorization, and token endpoints, and drives the full flow automatically. The user sees a browser login once. After that, the client holds a bearer token valid for 30 days.
Method 1: Automatic OAuth (Claude Code)
Claude Code supports OAuth natively. Add WorkingAgents to your MCP config:
~/.claude/mcp.json (global) or .mcp.json (project-level):
{
"mcpServers": {
"workingagents": {
"type": "http",
"url": "https://workingagents.ai/mcp",
"auth": {
"type": "oauth",
"clientName": "Claude Code",
"redirectUri": "http://localhost:3000/callback"
}
}
}
}
On first use:
-
Claude Code calls
/.well-known/oauth-authorization-serverto discover endpoints -
It registers itself at
/register– no admin action needed -
It opens a browser window to
https://workingagents.ai/authorize - You log in with your WorkingAgents credentials (or Google)
- Claude Code exchanges the authorization code for a bearer token
-
All subsequent MCP calls use
Authorization: Bearer <token>
The token is stored by Claude Code and refreshed when it expires.
Method 2: Manual OAuth Flow
For custom agents or any HTTP client that supports OAuth 2.1.
Step 1: Register the client
POST https://workingagents.ai/register
Content-Type: application/json
{
"client_name": "my-agent",
"redirect_uris": ["http://localhost:3000/callback"],
"grant_types": ["authorization_code"]
}
Response:
{
"client_id": "wa_abc123...",
"client_secret": "cs_xyz...",
"redirect_uris": ["http://localhost:3000/callback"]
}
Step 2: Generate a PKCE pair
import secrets, hashlib, base64
code_verifier = secrets.token_urlsafe(64)
code_challenge = base64.urlsafe_b64encode(
hashlib.sha256(code_verifier.encode()).digest()
).rstrip(b"=").decode()
Step 3: Redirect the user to authorize
https://workingagents.ai/authorize
?client_id=wa_abc123
&redirect_uri=http://localhost:3000/callback
&response_type=code
&state=<random>
&code_challenge=<SHA256 of verifier>
&code_challenge_method=S256
The user logs in. WorkingAgents redirects to:
http://localhost:3000/callback?code=<auth_code>&state=<your_state>
Step 4: Exchange the code for a token
POST https://workingagents.ai/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=<auth_code>
&client_id=wa_abc123
&redirect_uri=http://localhost:3000/callback
&code_verifier=<original verifier>
Response:
{
"access_token": "...",
"token_type": "bearer",
"expires_in": 2592000
}
Step 5: Call the MCP server
POST https://workingagents.ai/mcp
Authorization: Bearer <access_token>
Content-Type: application/json
{"jsonrpc": "2.0", "method": "tools/list", "id": 1}
Method 3: Direct Bearer Token (Simplest)
Skip OAuth entirely. Generate a long-lived token directly in IEx on the server:
{:ok, user} = User.find_by_username("james")
secret = Plug.Crypto.KeyGenerator.generate(
Application.get_env(:mcp, :secret_key_base),
Application.get_env(:mcp, :cookie_salt),
length: 32
)
token = Plug.Crypto.MessageEncryptor.encrypt(
to_string(user.id),
secret,
Application.get_env(:mcp, :cookie_salt)
)
Use this token directly in any MCP client config:
Claude Code:
{
"mcpServers": {
"workingagents": {
"type": "http",
"url": "https://workingagents.ai/mcp",
"headers": {
"Authorization": "Bearer <token>"
}
}
}
}
Python agent:
import httpx
headers = {"Authorization": "Bearer <token>"}
response = httpx.post(
"https://workingagents.ai/mcp",
headers=headers,
json={"jsonrpc": "2.0", "method": "tools/list", "id": 1}
)
This token has no expiry mechanism beyond the server’s secret_key_base rotation. Use it for development, automation scripts, and single-user setups. Use OAuth for multi-user deployments.
Which Method to Use
| Scenario | Method |
|---|---|
| Claude Code, single user | Direct bearer token |
| Claude Code, multiple users | Automatic OAuth |
| Custom agent, developer-controlled | Direct bearer token |
| Custom agent, end-user login | Manual OAuth flow |
| Third-party tool integration | Manual OAuth or bearer token |
Permission Model
Regardless of how the client authenticates, the token resolves to a user ID and that user’s permission map. The tools available to an MCP client are exactly the tools the authenticated user is allowed to call. Revoking a user’s permission takes effect on the next tool call – no token reissue required.
An agent calling tools/list sees only the tools its user can access. A user without the workflow permission does not see workflow_create. A user without the Stripe connection permission does not see Stripe tools. The MCP client cannot escalate beyond the authenticated user’s permissions.