Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 57 additions & 16 deletions src/content/docs/agents/guides/oauth-mcp-client.mdx

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/bonk consider more reliable ways to handle HTML escaping. don't leave users at risk when they copy our examples.

Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ Instead of an automatic redirect, you can present the `authUrl` to your user as

## Configure callback behavior

After OAuth completes, the provider redirects back to your Agent's callback URL. Configure what happens next.
After OAuth completes, the provider redirects back to your Agent's callback URL. By default, successful authentication redirects to your application origin, while failed authentication displays an HTML error page with the error message.

### Redirect to your application

Expand All @@ -93,9 +93,9 @@ export class MyAgent extends Agent<Env, never> {

Users return to `/dashboard` on success or `/auth-error?error=<message>` on failure.

### Close popup window
### Custom handler for popup windows

If you opened OAuth in a popup, close it automatically when complete:
If you opened OAuth in a popup, use a custom handler to close it automatically. The handler receives an `MCPClientOAuthResult` object and must check `authSuccess` to handle both success and error cases:

<TypeScriptExample>

Expand All @@ -112,13 +112,30 @@ export class MyAgent extends Agent<Env, never> {
return new Response("<script>window.close();</script>", {
headers: { "content-type": "text/html" },
});
} else {
// Error - show message, then close
return new Response(
`<script>alert('Authorization failed: ${result.authError}'); window.close();</script>`,
{ headers: { "content-type": "text/html" } },
);
}
// Error - show message briefly, then close
const errorJson = JSON.stringify(
result.authError ?? "Unknown error",
);
return new Response(
`<!DOCTYPE html>
<html>
<head><title>Authentication Failed</title></head>
<body style="font-family:system-ui,sans-serif;max-width:400px;margin:80px auto;padding:0 20px;text-align:center;">
<h2 style="color:#c00;">Authentication Failed</h2>
<p id="err" style="color:#666;"></p>
<p style="color:#999;font-size:0.85em;">This window will close automatically...</p>
<script>
document.getElementById("err").textContent = ${errorJson};
setTimeout(() => window.close(), 3000);
</script>
</body>
</html>`,
{
headers: { "content-type": "text/html" },
status: 400,
},
);
},
});
}
Expand All @@ -127,6 +144,12 @@ export class MyAgent extends Agent<Env, never> {

</TypeScriptExample>

:::note[Important]

Always check `result.authSuccess` in your custom handler. The handler is called for both successful and failed authentication attempts. Without this check, authentication errors will be ignored and users will not know why the connection failed.

:::

Your main application can detect the popup closing and refresh the connection status.

## Monitor connection status
Expand Down Expand Up @@ -221,7 +244,7 @@ Connection states flow: `authenticating` (needs OAuth) → `connecting` (complet

## Handle failures

When OAuth fails, the connection state becomes `"failed"`. Detect this in your UI and allow users to retry:
When OAuth fails, the connection state becomes `"failed"` and the `error` field contains the error message. Display the error to your users and allow them to retry:

<TypeScriptExample>

Expand Down Expand Up @@ -270,7 +293,7 @@ function App() {
<strong>{server.name}</strong>: {server.state}
{server.state === "failed" && (
<div>
<p>Connection failed. Please try again.</p>
{server.error && <p style={{ color: "red" }}>Error: {server.error}</p>}
<button
onClick={() => handleRetry(id, server.server_url, server.name)}
>
Expand Down Expand Up @@ -318,12 +341,30 @@ export class MyAgent extends Agent<Env, never> {
return new Response("<script>window.close();</script>", {
headers: { "content-type": "text/html" },
});
} else {
return new Response(
`<script>alert('Authorization failed: ${result.authError}'); window.close();</script>`,
{ headers: { "content-type": "text/html" } },
);
}
// Error - show message briefly, then close
const errorJson = JSON.stringify(
result.authError ?? "Unknown error",
);
return new Response(
`<!DOCTYPE html>
<html>
<head><title>Authentication Failed</title></head>
<body style="font-family:system-ui,sans-serif;max-width:400px;margin:80px auto;padding:0 20px;text-align:center;">
<h2 style="color:#c00;">Authentication Failed</h2>
<p id="err" style="color:#666;"></p>
<p style="color:#999;font-size:0.85em;">This window will close automatically...</p>
<script>
document.getElementById("err").textContent = ${errorJson};
setTimeout(() => window.close(), 3000);
</script>
</body>
</html>`,
{
headers: { "content-type": "text/html" },
status: 400,
},
);
},
});
}
Expand Down
117 changes: 110 additions & 7 deletions src/content/docs/agents/model-context-protocol/mcp-client-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -192,29 +192,42 @@ OAuth tokens are securely stored in SQLite, and persist across agent restarts.

### Custom OAuth callback handling

For custom OAuth completion behavior:
Configure how OAuth completion is handled. By default, successful authentication redirects to your application origin, while failed authentication displays an HTML error page.

<TypeScriptExample>

```ts
import type { MCPClientOAuthResult } from "agents/mcp";

export class MyAgent extends Agent {
onStart() {
this.mcp.configureOAuthCallback({
// Redirect after successful auth
successRedirect: "https://myapp.com/success",

// Redirect on error
// Redirect on error with error message in query string
errorRedirect: "https://myapp.com/error",

// Or use a custom handler
customHandler: (result) => {
// Or use a custom handler for complete control
customHandler: (result: MCPClientOAuthResult) => {
// result.authSuccess - boolean indicating if auth succeeded
// result.authError - string with error message (if failed)
// result.serverId - ID of the MCP server

if (result.authSuccess) {
// Close popup window after auth
// Close popup window after successful auth
return new Response("<script>window.close();</script>", {
headers: { "content-type": "text/html" },
});
}
return new Response("Auth failed", { status: 400 });
// Handle error - must check authSuccess to avoid ignoring failures
return new Response(
`Auth failed: ${result.authError ?? "Unknown error"}`,
{
status: 400,
headers: { "content-type": "text/plain" },
},
);
},
});
}
Expand All @@ -223,6 +236,12 @@ export class MyAgent extends Agent {

</TypeScriptExample>

:::note[Important]

When using `customHandler`, always check `result.authSuccess` to handle both success and failure cases. The handler is called for all OAuth callback requests, including failures.

:::

## Using MCP capabilities

Once connected, access the server's capabilities:
Expand Down Expand Up @@ -477,6 +496,7 @@ type MCPServersState = {
| "failed";
capabilities: ServerCapabilities | null;
instructions: string | null;
error?: string; // Present when state is "failed"
}
>;
tools: Array<Tool & { serverId: string }>;
Expand All @@ -493,7 +513,90 @@ The `state` field indicates the connection lifecycle:
- `connected` — Transport connection established
- `discovering` — Discovering server capabilities (tools, resources, prompts)
- `ready` — Fully connected and operational
- `failed` — Connection failed
- `failed` — Connection failed (check the `error` field for details)

### `configureOAuthCallback()`

Configure OAuth callback behavior for MCP servers requiring authentication. This method allows you to customize what happens after a user completes OAuth authorization.

```ts
this.mcp.configureOAuthCallback(options: {
successRedirect?: string;
errorRedirect?: string;
customHandler?: (result: MCPClientOAuthResult) => Response | Promise<Response>;
}): void
```

#### Parameters

- `options` (object, required) — OAuth callback configuration:
- `successRedirect` (string, optional) — URL to redirect to after successful authentication
- `errorRedirect` (string, optional) — URL to redirect to after failed authentication. Error message is appended as `?error=<message>` query parameter
- `customHandler` (function, optional) — Custom handler for complete control over the callback response. Receives an `MCPClientOAuthResult` object and must return a Response

#### MCPClientOAuthResult type

The `customHandler` receives an object with the following properties:

```ts
type MCPClientOAuthResult = {
serverId: string; // ID of the MCP server
authSuccess: boolean; // true if authentication succeeded
authError?: string; // Error message if authentication failed
};
```

#### Default behavior

When no configuration is provided:
- **Success**: Redirects to your application origin
- **Failure**: Displays an HTML error page with the error message

#### Usage

Configure in `onStart()` before any OAuth flows begin:

<TypeScriptExample>

```ts
import type { MCPClientOAuthResult } from "agents/mcp";

export class MyAgent extends Agent {
onStart() {
// Option 1: Simple redirects
this.mcp.configureOAuthCallback({
successRedirect: "/dashboard",
errorRedirect: "/auth-error",
});

// Option 2: Custom handler (e.g., for popup windows)
this.mcp.configureOAuthCallback({
customHandler: (result: MCPClientOAuthResult) => {
if (result.authSuccess) {
return new Response("<script>window.close();</script>", {
headers: { "content-type": "text/html" },
});
}
return new Response(
`Auth failed: ${result.authError ?? "Unknown error"}`,
{
status: 400,
headers: { "content-type": "text/plain" },
},
);
},
});
}
}
```

</TypeScriptExample>

:::caution

Always check `result.authSuccess` in your `customHandler`. The handler is called for both successful and failed authentication. Ignoring this check will cause authentication errors to be silently swallowed.

:::

## Advanced: MCPClientManager

Expand Down
Loading