← Vytvářejte agenty na Cloudflare / agents / model-context-protocol / apis
McpClient
Připojte svého agenta k externím Model Context Protocol (MCP) servery, aby využil jejich nástroje, zdroje a prompty. Agents SDK v0.20.0 používá @modelcontextprotocol/client a automaticky vyjednává bezstavové nebo starší chování.
Viz Migrovat na MCP SDK v2 pro změny balíčku, typu, poskytovatele OAuth a postupného nasazení.
Přehled
Možnost klienta MCP umožňuje vašemu agentovi:
- Připojení k externím serverům MCP - GitHub, Slack, databáze, AI služby
- Použití jejich nástrojů - Volejte funkce zpřístupněné servery MCP
- Prostředky Access - Čtení dat ze serverů MCP
- Použití promptů - Využití předpřipravených šablon promptů
Rychlý start
import { Agent } from "agents";
export class MyAgent extends Agent {
async onRequest(request) {
// Add an MCP server
const result = await this.addMcpServer(
"github",
"https://mcp.github.com/mcp",
);
if (result.state === "authenticating") {
// Server requires OAuth - redirect user to authorize
return Response.redirect(result.authUrl);
}
// Server is ready - tools are now available
const state = this.getMcpServers();
console.log(`Connected! ${state.tools.length} tools available`);
return new Response("MCP server connected");
}
}import { Agent } from "agents";
export class MyAgent extends Agent {
async onRequest(request: Request) {
// Add an MCP server
const result = await this.addMcpServer(
"github",
"https://mcp.github.com/mcp",
);
if (result.state === "authenticating") {
// Server requires OAuth - redirect user to authorize
return Response.redirect(result.authUrl);
}
// Server is ready - tools are now available
const state = this.getMcpServers();
console.log(`Connected! ${state.tools.length} tools available`);
return new Response("MCP server connected");
}
}Připojení přetrvávají v agentově Úložiště SQL, a když se agent připojí k MCP serveru, automaticky se zpřístupní všechny nástroje z tohoto serveru.
Přidání serverů MCP
Použijte addMcpServer() pro připojení k MCP serveru. U serverů bez OAuth nejsou potřeba žádné možnosti:
// Non-OAuth server — no options required
await this.addMcpServer("notion", "https://mcp.notion.so/mcp");
// OAuth server — callbackHost is auto-derived from the incoming request,
// but you can set it explicitly if needed (e.g. custom domains)
await this.addMcpServer("github", "https://mcp.github.com/mcp", {
callbackHost: "https://my-worker.workers.dev",
});// Non-OAuth server — no options required
await this.addMcpServer("notion", "https://mcp.notion.so/mcp");
// OAuth server — callbackHost is auto-derived from the incoming request,
// but you can set it explicitly if needed (e.g. custom domains)
await this.addMcpServer("github", "https://mcp.github.com/mcp", {
callbackHost: "https://my-worker.workers.dev",
});Stabilní ID serverů
Ve výchozím nastavení je každému připojení přiřazen vygenerovaný nanoid(8) ID. Předejte id pro integrace ve stylu konektorů, díky čemuž se nástroje zobrazují jako čitelné klíče místo neprůhledných ID připojení.
await this.addMcpServer("GitHub", env.MCP_SESSION, {
id: "github",
props: { token: "..." },
});
// tools surface as `tool_github_<name>`await this.addMcpServer("GitHub", env.MCP_SESSION, {
id: "github",
props: { token: "..." },
});
// tools surface as `tool_github_<name>`Pokud je uvedeno, toto id nahradí vygenerovanou hodnotu jako ID serveru v úložišti, obnovení, listServers(), listTools(), getAITools(), a stav OAuth. Zadané ID se normalizuje pomocí exportované normalizeServerId pomocná funkce, takže hodnoty jako "GitHub MCP!" se stanou "github-mcp" : zaručuje, že je ID bezpečné vložit do názvů nástrojů AI SDK a klíčů úložiště.
Stabilní ID jsou plně aditivní: žádný stávající kód se tím nerozbije. Pokud přidáte { id: "github" } na addMcpServer volání pro server, který je už zaregistrovaný pod automaticky vygenerovaným ID, SDK transparentně přemigruje existující řádek úložiště, spojení v paměti a klíče úložiště související s OAuth na nové stabilní ID. Žádné removeMcpServer krok je vyžadován. addMcpServer vyvolá výjimku pouze při skutečně nejednoznačné kolizi: stejné stabilní ID již patří jiný (name, url) server.
Možnosti transportu
MCP podporuje více typů transportu:
await this.addMcpServer("server", "https://mcp.example.com/mcp", {
transport: {
type: "streamable-http",
},
});await this.addMcpServer("server", "https://mcp.example.com/mcp", {
transport: {
type: "streamable-http",
},
});| Transport | Popis |
|---|---|
auto |
Automatická detekce podle odpovědi serveru (výchozí) |
streamable-http |
HTTP se streamováním |
sse |
Server-Sent Events: starší přenos pro zpětnou kompatibilitu |
Vlastní hlavičky
Pro servery za autentizací (jako je Cloudflare Access) nebo používající bearer tokeny:
await this.addMcpServer("internal", "https://internal-mcp.example.com/mcp", {
transport: {
headers: {
Authorization: "Bearer my-token",
"CF-Access-Client-Id": "...",
"CF-Access-Client-Secret": "...",
},
},
});await this.addMcpServer("internal", "https://internal-mcp.example.com/mcp", {
transport: {
headers: {
Authorization: "Bearer my-token",
"CF-Access-Client-Id": "...",
"CF-Access-Client-Secret": "...",
},
},
});Zabezpečení URL
Adresy URL serveru MCP se před připojením ověřují, aby se zabránilo útoku Server-Side Request Forgery (SSRF). Následující cílové adresy URL jsou blokovány:
- Soukromé/interní rozsahy IP adres (RFC 1918:
10.x,172.16-31.x,192.168.x) - Nespecifikované adresy (
0.0.0.0,[::]) - Adresy link-local (
169.254.x,fe80::) - Unikátní lokální adresy IPv6 (
fc00::/7) - Adresy IPv6 typu IPv4-mapped, které se překládají na privátní rozsahy (například
[::ffff:10.0.0.1]) - Koncové body cloudových metadat (
metadata.google.internal)
Loopback adresy (localhost, 127.x.x.x, [::1]) jsou povoleno pro lokální vývoj.
Pro produkční připojení k interním službám použijte Transport RPC s vazbou Durable Object místo HTTP.
Návratová hodnota
addMcpServer() vrátí stav připojení:
ready- Server připojen a nástroje nalezenyauthenticating- Server vyžaduje OAuth; přesměrujte uživatele naauthUrl
OAuth autentizace
Mnoho serverů MCP vyžaduje ověření OAuth. Agent zpracovává tok OAuth automaticky.
Jak to funguje
sequenceDiagram
participant Client
participant Agent
participant MCPServer
Client->>Agent: addMcpServer(name, url)
Agent->>MCPServer: Connect
MCPServer-->>Agent: Requires OAuth
Agent-->>Client: state: authenticating, authUrl
Client->>MCPServer: User authorizes
MCPServer->>Agent: Callback with code
Agent->>MCPServer: Exchange for token
Agent-->>Client: onMcpUpdate (ready)
Zpracování OAuth ve vašem agentovi
class MyAgent extends Agent {
async onRequest(request) {
const result = await this.addMcpServer(
"github",
"https://mcp.github.com/mcp",
);
if (result.state === "authenticating") {
// Redirect the user to the OAuth authorization page
return Response.redirect(result.authUrl);
}
return Response.json({ status: "connected", id: result.id });
}
}class MyAgent extends Agent {
async onRequest(request: Request) {
const result = await this.addMcpServer(
"github",
"https://mcp.github.com/mcp",
);
if (result.state === "authenticating") {
// Redirect the user to the OAuth authorization page
return Response.redirect(result.authUrl);
}
return Response.json({ status: "connected", id: result.id });
}
}OAuth callback
URL callbacku se sestaví automaticky:
https://{host}/{agentsPrefix}/{agent-name}/{instance-name}/callbackNapříklad: https://my-worker.workers.dev/agents/my-agent/default/callback
OAuth tokeny jsou bezpečně uloženy v SQLite a zůstávají zachovány i po restartu agenta.
Ochrana názvů instancí v callbacích OAuth
Při použití sendIdentityOnConnect: false ke skrytí citlivých názvů instancí (jako jsou ID relací nebo ID uživatelů), výchozí OAuth callback URL by název instance odhalila. Abyste tomuto bezpečnostnímu problému předešli, musíte poskytnout vlastní callbackPath.
import { Agent, routeAgentRequest, getAgentByName } from "agents";
export class SecureAgent extends Agent {
static options = { sendIdentityOnConnect: false };
async onRequest(request) {
// callbackPath is required when sendIdentityOnConnect is false
const result = await this.addMcpServer(
"github",
"https://mcp.github.com/mcp",
{
callbackPath: "mcp-oauth-callback", // Custom path without instance name
},
);
if (result.state === "authenticating") {
return Response.redirect(result.authUrl);
}
return new Response("Connected!");
}
}
// Route the custom callback path to the agent
export default {
async fetch(request, env) {
const url = new URL(request.url);
// Route custom MCP OAuth callback to agent instance
if (url.pathname.startsWith("/mcp-oauth-callback")) {
// Implement this to extract the instance name from your session/auth mechanism
const instanceName = await getInstanceNameFromSession(request);
const agent = await getAgentByName(env.SecureAgent, instanceName);
return agent.fetch(request);
}
// Standard agent routing
return (
(await routeAgentRequest(request, env)) ??
new Response("Not found", { status: 404 })
);
},
};import { Agent, routeAgentRequest, getAgentByName } from "agents";
export class SecureAgent extends Agent {
static options = { sendIdentityOnConnect: false };
async onRequest(request: Request) {
// callbackPath is required when sendIdentityOnConnect is false
const result = await this.addMcpServer(
"github",
"https://mcp.github.com/mcp",
{
callbackPath: "mcp-oauth-callback", // Custom path without instance name
},
);
if (result.state === "authenticating") {
return Response.redirect(result.authUrl);
}
return new Response("Connected!");
}
}
// Route the custom callback path to the agent
export default {
async fetch(request: Request, env: Env) {
const url = new URL(request.url);
// Route custom MCP OAuth callback to agent instance
if (url.pathname.startsWith("/mcp-oauth-callback")) {
// Implement this to extract the instance name from your session/auth mechanism
const instanceName = await getInstanceNameFromSession(request);
const agent = await getAgentByName(env.SecureAgent, instanceName);
return agent.fetch(request);
}
// Standard agent routing
return (
(await routeAgentRequest(request, env)) ??
new Response("Not found", { status: 404 })
);
},
} satisfies ExportedHandler<Env>;Vlastní zpracování OAuth callbacku
Nakonfigurujte, jak se zpracovává dokončení OAuth. Ve výchozím nastavení úspěšná autentizace přesměruje na origin vaší aplikace, zatímco neúspěšná autentizace zobrazí chybovou stránku HTML.
export class MyAgent extends Agent {
onStart() {
this.mcp.configureOAuthCallback({
// Redirect after successful auth
successRedirect: "https://myapp.com/success",
// Redirect on error with error message in query string
errorRedirect: "https://myapp.com/error",
// Or use a custom handler
customHandler: () => {
// Close popup window after auth completes
return new Response("<script>window.close();</script>", {
headers: { "content-type": "text/html" },
});
},
});
}
}export class MyAgent extends Agent {
onStart() {
this.mcp.configureOAuthCallback({
// Redirect after successful auth
successRedirect: "https://myapp.com/success",
// Redirect on error with error message in query string
errorRedirect: "https://myapp.com/error",
// Or use a custom handler
customHandler: () => {
// Close popup window after auth completes
return new Response("<script>window.close();</script>", {
headers: { "content-type": "text/html" },
});
},
});
}
}Používání možností MCP
Jakmile jste připojeni, přistupte k možnostem serveru:
Získat dostupné nástroje
Použijte listTools() ke kontrole surového katalogu MCP bez přípravy nástrojů pro volání modelu AI SDK:
const tools = this.mcp.listTools();
for (const tool of tools) {
console.log(`Tool: ${tool.name}`);
console.log(` From server: ${tool.serverId}`);
console.log(` Title: ${tool.title ?? tool.annotations?.title ?? tool.name}`);
console.log(` Description: ${tool.description}`);
}const tools = this.mcp.listTools();
for (const tool of tools) {
console.log(`Tool: ${tool.name}`);
console.log(` From server: ${tool.serverId}`);
console.log(` Title: ${tool.title ?? tool.annotations?.title ?? tool.name}`);
console.log(` Description: ${tool.description}`);
}getMcpServers().tools vrátí stejné syrové záznamy nástrojů jako součást úplného stavu klienta MCP. Ani jedno z těchto API neprovádí konverzi schémat nástrojů.
Integrace s AI SDK
Chcete-li používat nástroje MCP s AI SDK, použijte this.mcp.getAITools() který převádí nástroje MCP do formátu AI SDK:
import { generateText } from "ai";
import { createWorkersAI } from "workers-ai-provider";
export class MyAgent extends Agent {
async onRequest(request) {
const workersai = createWorkersAI({ binding: this.env.AI });
const response = await generateText({
model: workersai("@cf/zai-org/glm-4.7-flash"),
prompt: "What's the weather in San Francisco?",
tools: this.mcp.getAITools(),
});
return new Response(response.text);
}
}import { generateText } from "ai";
import { createWorkersAI } from "workers-ai-provider";
export class MyAgent extends Agent<Env> {
async onRequest(request: Request) {
const workersai = createWorkersAI({ binding: this.env.AI });
const response = await generateText({
model: workersai("@cf/zai-org/glm-4.7-flash"),
prompt: "What's the weather in San Francisco?",
tools: this.mcp.getAITools(),
});
return new Response(response.text);
}
}Resources a prompty
const state = this.getMcpServers();
// Available resources
for (const resource of state.resources) {
console.log(`Resource: ${resource.name} (${resource.uri})`);
}
// Available prompts
for (const prompt of state.prompts) {
console.log(`Prompt: ${prompt.name}`);
}const state = this.getMcpServers();
// Available resources
for (const resource of state.resources) {
console.log(`Resource: ${resource.name} (${resource.uri})`);
}
// Available prompts
for (const prompt of state.prompts) {
console.log(`Prompt: ${prompt.name}`);
}Elicitace
Servery MCP mohou při zpracování jiné operace požádat o vstup od uživatele. Na bezstavové cestě elicitace vrací input_required a dokončuje se prostřednictvím vícekolových požadavků (MRTR). Ve starším postupu server aktivně odesílá elicitation/create požadavků. Oba používají režimy form a URL.
Zaregistrujte handler pro každý režim, který váš Agent podporuje, v onStart(). Obě dráhy obsluhují stejné handlery:
import { Agent } from "agents";
class MyAgent extends Agent {
onStart() {
this.mcp.configureElicitationHandlers({
form: (request, serverId) =>
this.forwardElicitationToBrowser(request, serverId),
url: (request, serverId) =>
this.forwardElicitationToBrowser(request, serverId),
});
}
forwardElicitationToBrowser(request, serverId) {
// Forward the request to your UI and resolve after the user responds.
// A complete implementation appears in Forward elicitation to a UI.
throw new Error(
`Implement elicitation for ${serverId}: ${request.params.message}`,
);
}
}import { Agent } from "agents";
import type { ElicitRequest, ElicitResult } from "agents/mcp/client";
class MyAgent extends Agent<Env> {
onStart() {
this.mcp.configureElicitationHandlers({
form: (request, serverId) =>
this.forwardElicitationToBrowser(request, serverId),
url: (request, serverId) =>
this.forwardElicitationToBrowser(request, serverId),
});
}
private forwardElicitationToBrowser(
request: ElicitRequest,
serverId: string,
): Promise<ElicitResult> {
// Forward the request to your UI and resolve after the user responds.
// A complete implementation appears in Forward elicitation to a UI.
throw new Error(
`Implement elicitation for ${serverId}: ${request.params.message}`,
);
}
} serverId identifikuje připojení, které požadavek odeslalo. Použijte ji k tomu, abyste uživateli sdělili, který server žádá o vstup, a uplatnili zásady specifické pro daný server.
Vyjednávání možností a hibernace
SDK oznamuje pouze režimy s nakonfigurovanými handlery. Připojení přes starší cestu je oznamuje během initialize. Požadavky na bezstavové cestě je nesou spolu s možnostmi požadavku. Handler pouze pro formuláře deklaruje režim formuláře. Handler pouze pro URL deklaruje režim URL. Připojení bez handlerů nedeklaruje žádnou možnost elicitace, což serveru umožní použít jeho záložní řešení.
SDK ukládá oznámené režimy při každé registraci serveru. Připojení obnovené po hibernaci Durable Object proto může při opětovném navázání oznámit stejné režimy. Funkce callback zůstávají v paměti a znovu se připojí, když onStart() se spustí.
Při přidávání serveru můžete explicitně zúžit nabízené režimy:
await this.addMcpServer("portal", "https://portal.example.com/mcp", {
client: {
capabilities: {
elicitation: { form: {} },
},
},
});await this.addMcpServer("portal", "https://portal.example.com/mcp", {
client: {
capabilities: {
elicitation: { form: {} },
},
},
});Explicitní client.capabilities.elicitation hodnota má přednost před režimy odvozenými z handleru a zůstává zachována při registraci serveru. Neinzerujte režim bez odpovídajícího handleru. Pokud server tento režim odešle, spojení vrátí chybu, protože požadavek nedokáže zpracovat.
Režim formuláře
Režim formuláře shromažďuje strukturovaná, necitlivá data na straně klienta. Požadavek obsahuje omezené JSON Schema v requestedSchema. Pokud uživatel formulář odešle, vraťte action: "accept" s odpovídajícím content:
this.mcp.configureElicitationHandlers({
form: async (request) => {
const content = await showFormToUser(request.params.requestedSchema);
return content ? { action: "accept", content } : { action: "cancel" };
},
});this.mcp.configureElicitationHandlers({
form: async (request) => {
const content = await showFormToUser(request.params.requestedSchema);
return content ? { action: "accept", content } : { action: "cancel" };
},
});Umožněte uživateli zkontrolovat a upravit hodnoty před odesláním. Přijatý obsah ověřte podle requestedSchema. Nepoužívejte režim formuláře k vyžádání hesel, API klíčů, přístupových tokenů, platebních údajů nebo jiných tajných údajů.
Režim URL
Režim URL vyzve uživatele k otevření externí stránky. Použijte ho pro mimopásmové interakce, při kterých se mohou sbírat tajné údaje, například autorizaci třetí strany nebo platbu. Adresu URL udržujte ve vyhrazené cestě elicitace a mimo zprávy viditelné modelu a text výsledků nástrojů.
Handler URL by měl:
- Zobrazte, který server MCP odeslal požadavek.
- Zobrazte zprávu požadavku, cílového hostitele a úplnou adresu URL.
- Před otevřením URL si vyžádejte souhlas.
- Otevřete stránku v kontextu prohlížeče, který Agent ani model nemohou zkoumat.
- Vraťte
action: "accept"bezcontentpo udělení souhlasu. - Nabídněte oddělené ovládací prvky pro odmítnutí a zrušení.
Adresu URL ani její metadata nenačítejte předem. Zacházejte s adresou URL jako s nedůvěryhodným vstupem. Produkční servery by měly odesílat adresy URL HTTPS.
Pro režim URL, accept znamená, že uživatel souhlasil s otevřením URL. Neznamená to, že externí interakce skončila. Server může později odeslat notifications/elicitation/complete s požadavkem elicitationId.
Reakční akce
Oba režimy podporují tři akce:
| Akce | Význam |
|---|---|
accept |
Uživatel odeslal formulář nebo souhlasil s otevřením URL. |
decline |
Uživatel žádost výslovně odmítl. |
cancel |
Uživatel žádost zavřel, aniž by provedl explicitní volbu. |
Vložte content pouze pro přijatou odpověď formuláře. Vynechejte jej u odpovědí typu URL, decline a cancel.
Předání elicitace do uživatelského rozhraní
Handler vrátí promise, ale odpověď často přichází z prohlížeče. Požadavek odešlete broadcastem připojeným klientům a poté promise vyřešte prostřednictvím @callable metoda:
import { Agent, callable } from "agents";
class MyAgent extends Agent {
pendingElicitations = new Map();
onStart() {
this.mcp.configureElicitationHandlers({
form: (request, serverId) => this.forward(request, serverId),
url: (request, serverId) => this.forward(request, serverId),
});
}
forward(request, serverId) {
const id = crypto.randomUUID();
const result = new Promise((resolve) => {
const timeout = setTimeout(() => {
if (this.pendingElicitations.delete(id)) {
resolve({ action: "cancel" });
}
}, 55_000);
this.pendingElicitations.set(id, { resolve, timeout });
});
this.broadcast(
JSON.stringify({
type: "mcp-elicitation",
id,
serverId,
params: request.params,
}),
);
return result;
}
@callable()
respondToElicitation(id, result) {
const pending = this.pendingElicitations.get(id);
if (!pending) return;
this.pendingElicitations.delete(id);
clearTimeout(pending.timeout);
pending.resolve(result);
}
}import { Agent, callable } from "agents";
import type { ElicitRequest, ElicitResult } from "agents/mcp/client";
type PendingResolver = {
resolve: (result: ElicitResult) => void;
timeout: ReturnType<typeof setTimeout>;
};
class MyAgent extends Agent<Env> {
private pendingElicitations = new Map<string, PendingResolver>();
onStart() {
this.mcp.configureElicitationHandlers({
form: (request, serverId) => this.forward(request, serverId),
url: (request, serverId) => this.forward(request, serverId),
});
}
private forward(
request: ElicitRequest,
serverId: string,
): Promise<ElicitResult> {
const id = crypto.randomUUID();
const result = new Promise<ElicitResult>((resolve) => {
const timeout = setTimeout(() => {
if (this.pendingElicitations.delete(id)) {
resolve({ action: "cancel" });
}
}, 55_000);
this.pendingElicitations.set(id, { resolve, timeout });
});
this.broadcast(
JSON.stringify({
type: "mcp-elicitation",
id,
serverId,
params: request.params,
}),
);
return result;
}
@callable()
respondToElicitation(id: string, result: ElicitResult) {
const pending = this.pendingElicitations.get(id);
if (!pending) return;
this.pendingElicitations.delete(id);
clearTimeout(pending.timeout);
pending.resolve(result);
}
}Příklad používá 55sekundový timeout, protože požadavky MCP SDK mají ve výchozím nastavení timeout 60 sekund. Pokud vaše klientské volání nastavuje delší timeout požadavku, upravte tento timeout tak, aby skončil dřív.
Viz mcp-client příklad ↗ pro implementaci v prohlížeči. mcp-elicitation-mrtr ↗ demonstruje bezstavovou elicitaci. mcp-elicitation ↗ demonstruje starší elicitaci.
Vzory na straně serveru najdete v Elicitace s bezstavovým handlerem a Elicitace na starších serverech.
Správa serverů
Registrace serverů MCP přetrvávají i po restartování Agenta. SDK ukládá konfiguraci serveru do SQLite, bezpečně ukládá OAuth tokeny a při probuzení Agenta obnovuje připojení.
Vypsat všechny servery
const state = this.getMcpServers();
for (const [id, server] of Object.entries(state.servers)) {
console.log(`${id}: ${server.name} (${server.server_url})`);
}const state = this.getMcpServers();
for (const [id, server] of Object.entries(state.servers)) {
console.log(`${id}: ${server.name} (${server.server_url})`);
}Získat stav serveru
Použijte ID serveru k prozkoumání jednotlivého připojení:
const state = this.getMcpServers();
const server = state.servers[serverId];
if (server) {
console.log(`${server.name}: ${server.state}`);
// state: "ready" | "authenticating" | "connecting" | "connected" | "discovering" | "failed"
}const state = this.getMcpServers();
const server = state.servers[serverId];
if (server) {
console.log(`${server.name}: ${server.state}`);
// state: "ready" | "authenticating" | "connecting" | "connected" | "discovering" | "failed"
}Odebrat server
await this.removeMcpServer(serverId);await this.removeMcpServer(serverId);Tím se odpojí od serveru a odstraní ho z úložiště.
Integrace na straně klienta
Připojení klienti dostávají aktualizace MCP v reálném čase prostřednictvím WebSocketu:
import { useAgent } from "agents/react";
import { useState } from "react";
function Dashboard() {
const [tools, setTools] = useState([]);
const [servers, setServers] = useState({});
const agent = useAgent({
agent: "MyAgent",
onMcpUpdate: (mcpState) => {
setTools(mcpState.tools);
setServers(mcpState.servers);
},
});
return (
<div>
<h2>Connected Servers</h2>
{Object.entries(servers).map(([id, server]) => (
<div key={id}>
{server.name}: {server.state}
</div>
))}
<h2>Available Tools ({tools.length})</h2>
{tools.map((tool) => (
<div key={`${tool.serverId}-${tool.name}`}>{tool.name}</div>
))}
</div>
);
}import { useAgent } from "agents/react";
import { useState } from "react";
function Dashboard() {
const [tools, setTools] = useState([]);
const [servers, setServers] = useState({});
const agent = useAgent({
agent: "MyAgent",
onMcpUpdate: (mcpState) => {
setTools(mcpState.tools);
setServers(mcpState.servers);
},
});
return (
<div>
<h2>Connected Servers</h2>
{Object.entries(servers).map(([id, server]) => (
<div key={id}>
{server.name}: {server.state}
</div>
))}
<h2>Available Tools ({tools.length})</h2>
{tools.map((tool) => (
<div key={`${tool.serverId}-${tool.name}`}>{tool.name}</div>
))}
</div>
);
}Referenční dokumentace API
addMcpServer()
Přidejte připojení k serveru MCP a zpřístupněte jeho nástroje svému agentovi.
Volání addMcpServer je idempotentní, pokud se shodují název serveru a URL odpovídá existujícímu aktivnímu připojení, vrátí se toto existující připojení bez vytvoření duplicity. Díky tomu je bezpečné volat onStart() aniž byste se museli starat o duplicitní připojení po restartu.
Pokud zavoláte addMcpServer se stejným názvem, ale s jiný URL, vytvoří se nové připojení. Obě připojení zůstávají aktivní a jejich nástroje se sloučí v getAITools(). Chcete-li server nahradit, zavolejte removeMcpServer(oldId) nejprve.
Adresy URL se před porovnáním normalizují (řeší se koncová lomítka, výchozí porty a velikost písmen v názvu hostitele), takže https://MCP.Example.com a https://mcp.example.com/ jsou považovány za stejnou URL adresu.
// HTTP transport (Streamable HTTP, SSE)
async addMcpServer(
serverName: string,
url: string,
options?: {
id?: string;
callbackHost?: string;
callbackPath?: string;
agentsPrefix?: string;
client?: McpClientOptions;
transport?: {
headers?: HeadersInit;
type?: "sse" | "streamable-http" | "auto";
};
retry?: RetryOptions;
}
): Promise<
| { id: string; state: "authenticating"; authUrl: string }
| { id: string; state: "ready" }
>
// RPC transport (Durable Object binding — no HTTP overhead)
async addMcpServer(
serverName: string,
binding: DurableObjectNamespace,
options?: {
id?: string;
props?: Record<string, unknown>;
client?: McpClientOptions;
retry?: RetryOptions;
}
): Promise<{ id: string; state: "ready" }>Parametry (HTTP transport)
serverName(řetězec, povinné): zobrazovaný název serveru MCPurl(řetězec, povinné): URL koncového bodu serveru MCPoptions(objekt, volitelné): konfigurace připojení:id: Volitelné stabilní ID serveru dodané volajícím pro integrace ve stylu konektoru. Pokud je zadáno, nahrazuje vygenerovanénanoid(8)napříč úložištěm,listServers(),listTools(),getAITools()(takže klíče nástrojů se stanou čitelnými, napříkladtool_github_create_pull_request), a stav OAuth. Podívejte se na Stabilní ID serverůcallbackHost: Hostitel pro URL adresu zpětného volání OAuth. Potřebný pouze pro servery ověřené pomocí OAuth. Pokud není zadán, odvodí se automaticky z příchozího požadavku nebo z URI připojení WebSocket. Toto obvykle nemusíte nastavovat, pokud nepoužíváte vlastní doménu odlišnou od hostitelského názvu WorkerucallbackPath: Vlastní cesta URL callbacku, která obchází výchozí/agents/{class}/{name}/callbackkonstrukce. Vyžadováno, pokudsendIdentityOnConnectjefalsek zamezení úniku názvu instance. Je-li nastaveno, callback URL se změní na{callbackHost}/{callbackPath}. Tuto cestu musíte na instanci agenta nasměrovat přesgetAgentByNameagentsPrefix: Prefix URL adresy pro cestu zpětného volání OAuth. Výchozí:"agents". Ignoruje se, kdyžcallbackPathje zadánclient: Podporovaný AgentsMcpClientOptionspodmnožinu z@modelcontextprotocol/client. Výchozí validátor podporuje schémata JSON Schema 2020-12 a starší schémata draft-07 ve Workerstransport: Konfigurace transportní vrstvy:headers: Vlastní HTTP hlavičky pro ověřovánítype: Typ transportu:"auto"(výchozí),"streamable-http", nebo"sse"
retry: Možnosti opakování pro pokusy o připojení a opětovné připojení. Ukládají se a používají při obnově připojení po hibernaci nebo po dokončení OAuth. Výchozí hodnoty: 3 pokusy, základní prodleva 500 ms, maximální prodleva 5 s. Viz Opakování pro podrobnosti oRetryOptions.
Parametry (RPC transport)
serverName(řetězec, povinné): zobrazovaný název serveru MCPbinding(DurableObjectNamespace, povinné): vazba Durable Object proMcpAgenttřídaoptions(objekt, volitelné): konfigurace připojení:id: Volitelné stabilní ID serveru dodané volajícím. Viz Stabilní ID serverůprops: Inicializační data předaná doMcpAgent:onStart(props). Použijte to k předání uživatelského kontextu, konfigurace nebo jiných dat instanci serveru MCPclient: Možnosti konfigurace klienta MCPretry: Možnosti opakování pro připojení
Transport RPC propojuje vašeho Agenta přímo s McpAgent prostřednictvím vazeb Durable Object bez režie HTTP. Viz Transport MCP pro podrobnosti o konfiguraci transportu RPC.
Vrací
Promise, který se vyřeší na diskriminovanou unii založenou na stavu připojení:
-
Když
stateje"authenticating":id(řetězec): jedinečný identifikátor tohoto připojení serverustate("authenticating"): Server čeká na autorizaci OAuthauthUrl(řetězec): autorizační URL OAuth pro ověření uživatele
-
Když
stateje"ready":id(řetězec): jedinečný identifikátor tohoto připojení serverustate("ready"): Server je plně připojen a funkční
removeMcpServer()
Odpojte se od serveru MCP a uvolněte jeho prostředky.
async removeMcpServer(id: string): Promise<void>Parametry
id(řetězec, povinné): ID připojení serveru vrácené zaddMcpServer()
getMcpServers()
Získat aktuální stav všech připojení k MCP serverům.
getMcpServers(): MCPServersStateVrací
type MCPServersState = {
servers: Record<
string,
{
name: string;
server_url: string;
auth_url: string | null;
state:
| "authenticating"
| "connecting"
| "connected"
| "discovering"
| "ready"
| "failed";
capabilities: ServerCapabilities | null;
instructions: string | null;
error: string | null;
}
>;
tools: Array<Tool & { serverId: string }>;
prompts: Array<Prompt & { serverId: string }>;
resources: Array<Resource & { serverId: string }>;
resourceTemplates: Array<ResourceTemplate & { serverId: string }>;
}; state pole udává životní cyklus připojení:
authenticating: Čekání na dokončení autorizace OAuthconnecting: Navazování transportního připojeníconnected: Transportní připojení navázánodiscovering: Zjišťování možností serveru (nástroje, zdroje, prompty)ready: Plně připojeno a funkčnífailed: Připojení selhalo (vizerrorpole, kde najdete podrobnosti)
error pole obsahuje chybovou zprávu, pokud state je "failed". Chybové zprávy od externích poskytovatelů OAuth se automaticky escapují, aby se předešlo útokům XSS, takže je lze bezpečně zobrazit přímo ve vašem UI.
configureOAuthCallback()
Nakonfigurujte chování OAuth callbacku pro servery MCP vyžadující autentizaci. Tato metoda umožňuje upravit, co se stane po dokončení autorizace OAuth uživatelem.
this.mcp.configureOAuthCallback(options: {
successRedirect?: string;
errorRedirect?: string;
customHandler?: () => Response | Promise<Response>;
}): voidParametry
options(objekt, povinné): konfigurace OAuth callbacku:successRedirect(řetězec, volitelné): URL, na kterou se přesměruje po úspěšném ověřeníerrorRedirect(řetězec, volitelné): URL, na kterou se přesměruje po neúspěšném ověření. Chybová zpráva se připojí jako?error=<message>parametr dotazucustomHandler(funkce, volitelné): vlastní handler pro úplnou kontrolu nad odpovědí callbacku. Musí vracet Response
Výchozí chování
Když není zadána žádná konfigurace:
- Úspěch: Přesměruje na origin vaší aplikace
- Selhání: Zobrazí chybovou stránku HTML se zprávou o chybě
Pokud selže OAuth, stav připojení se změní na "failed" a chybová zpráva se uloží do server.error pole pro zobrazení ve vašem uživatelském rozhraní.
Použití
Konfigurace v onStart() před zahájením jakéhokoli OAuth flow:
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: () => {
return new Response("<script>window.close();</script>", {
headers: { "content-type": "text/html" },
});
},
});
}
}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: () => {
return new Response("<script>window.close();</script>", {
headers: { "content-type": "text/html" },
});
},
});
}
}configureElicitationHandlers()
Konfigurace handlerů pro bezstavovou elicitaci a starší elicitation/create požadavků. Přidejte handler pro každý režim elicitace, který váš Agent podporuje.
this.mcp.configureElicitationHandlers(handlers?: {
form?: (
request: ElicitRequest,
serverId: string,
signal?: AbortSignal,
) => Promise<ElicitResult>;
url?: (
request: ElicitRequest,
serverId: string,
signal?: AbortSignal,
) => Promise<ElicitResult>;
}): voidParametry
handlers(objekt, volitelné): handlery pro vyžádání údajů (elicitation) klíčované podle režimu:form(funkce, volitelné): zpracovává požadavky v režimu formuláře pro strukturovaný, necitlivý vstup.url(funkce, volitelné): zpracovává požadavky v režimu URL pro mimopásmové interakce.
request(ElicitRequest): Požadavek na MCP elicitaci. Prozkoumejterequest.params.modepro pole specifická pro daný režim.serverId(řetězec): ID připojení serveru MCP, které požadavek odeslalo.signal(AbortSignal, volitelné): přeruší se, když je zrušena původní operace MCP.
Každý handler vrátí promise obsahující ElicitResult. Vraťte accept, decline, nebo cancel. Mezi akceptované odpovědi formuláře patří content která odpovídá requestedSchema. Odpovědi URL vynechávají content.
Předávání undefined vymaže všechny nakonfigurované handlery.
Chování možností
Klient během vyjednávání přes starší rozhraní a u bezstavových požadavků oznamuje pouze režimy s nakonfigurovanými handlery. Změny handlerů se u aktivních připojení projeví okamžitě, ale servery dostanou aktualizované oznámené režimy až po opětovném navázání těchto připojení.
SDK ukládá režimy odvozené z handlerů při každé registraci serveru MCP. Obnovená připojení tyto režimy oznámí i po hibernaci Durable Object a callbacky se znovu připojí, když onStart() se spustí.
Použití
Konfigurace handlerů v onStart():
import { Agent } from "agents";
export class MyAgent extends Agent {
onStart() {
this.mcp.configureElicitationHandlers({
form: (request, serverId) =>
this.forwardElicitationToBrowser(request, serverId),
url: (request, serverId) =>
this.forwardElicitationToBrowser(request, serverId),
});
}
forwardElicitationToBrowser(request, serverId) {
// Forward the request to your UI and resolve after the user responds.
throw new Error(
`Implement elicitation for ${serverId}: ${request.params.message}`,
);
}
}import { Agent } from "agents";
import type { ElicitRequest, ElicitResult } from "agents/mcp/client";
export class MyAgent extends Agent<Env> {
onStart() {
this.mcp.configureElicitationHandlers({
form: (request, serverId) =>
this.forwardElicitationToBrowser(request, serverId),
url: (request, serverId) =>
this.forwardElicitationToBrowser(request, serverId),
});
}
private forwardElicitationToBrowser(
request: ElicitRequest,
serverId: string,
): Promise<ElicitResult> {
// Forward the request to your UI and resolve after the user responds.
throw new Error(
`Implement elicitation for ${serverId}: ${request.params.message}`,
);
}
}Úplný vzor předávání prohlížeče a požadavky specifické pro jednotlivé režimy najdete v Elicitace.
Vlastní poskytovatel OAuth
Přepište výchozího poskytovatele OAuth použitého při připojování k MCP serverům implementací createMcpOAuthProvider() na vaší třídě Agent. To umožňuje vlastní strategie ověřování, jako jsou předregistrované přihlašovací údaje klienta nebo mTLS, nad rámec vestavěné dynamické registrace klienta.
Přepis se používá jak pro nová připojení (addMcpServer) a obnovila připojení po restartu Durable Object.
import { Agent } from "agents";
export class MyAgent extends Agent {
createMcpOAuthProvider(callbackUrl) {
const env = this.env;
return {
get redirectUrl() {
return callbackUrl;
},
get clientMetadata() {
return {
client_id: env.MCP_CLIENT_ID,
client_secret: env.MCP_CLIENT_SECRET,
redirect_uris: [callbackUrl],
};
},
clientInformation() {
return {
client_id: env.MCP_CLIENT_ID,
client_secret: env.MCP_CLIENT_SECRET,
};
},
};
}
}import { Agent } from "agents";
import type { AgentMcpOAuthProvider } from "agents";
export class MyAgent extends Agent<Env> {
createMcpOAuthProvider(callbackUrl: string): AgentMcpOAuthProvider {
const env = this.env;
return {
get redirectUrl() {
return callbackUrl;
},
get clientMetadata() {
return {
client_id: env.MCP_CLIENT_ID,
client_secret: env.MCP_CLIENT_SECRET,
redirect_uris: [callbackUrl],
};
},
clientInformation() {
return {
client_id: env.MCP_CLIENT_ID,
client_secret: env.MCP_CLIENT_SECRET,
};
},
};
}
}Pokud tuto metodu nepřepíšete, agent použije výchozího poskytovatele, který provádí OAuth 2.0 Dynamic Client Registration ↗ se serverem MCP.
Vlastní úložný backend
Chcete-li zachovat vestavěnou logiku OAuth (stav CSRF, PKCE, generování nonce, správu tokenů), ale ukládání tokenů směrovat do jiného backendu, importujte DurableObjectOAuthClientProvider a předat vlastní adaptér úložiště:
import { Agent, DurableObjectOAuthClientProvider } from "agents";
export class MyAgent extends Agent {
createMcpOAuthProvider(callbackUrl) {
return new DurableObjectOAuthClientProvider(
myCustomStorage, // any DurableObjectStorage-compatible adapter
this.name,
callbackUrl,
);
}
}import { Agent, DurableObjectOAuthClientProvider } from "agents";
import type { AgentMcpOAuthProvider } from "agents";
export class MyAgent extends Agent {
createMcpOAuthProvider(callbackUrl: string): AgentMcpOAuthProvider {
return new DurableObjectOAuthClientProvider(
myCustomStorage, // any DurableObjectStorage-compatible adapter
this.name,
callbackUrl,
);
}
}Pokročilé: MCPClientManager
Pro jemnější kontrolu použijte this.mcp přímo:
Připojení krok za krokem
// 1. Register the server (saves to storage and creates in-memory connection)
const id = "my-server";
await this.mcp.registerServer(id, {
url: "https://mcp.example.com/mcp",
name: "My Server",
callbackUrl: "https://my-worker.workers.dev/agents/my-agent/default/callback",
transport: { type: "auto" },
});
// 2. Connect (initializes transport, handles OAuth if needed)
const connectResult = await this.mcp.connectToServer(id);
if (connectResult.state === "failed") {
console.error("Connection failed:", connectResult.error);
return;
}
if (connectResult.state === "authenticating") {
console.log("OAuth required:", connectResult.authUrl);
return;
}
// 3. Discover capabilities (transitions from "connected" to "ready")
if (connectResult.state === "connected") {
const discoverResult = await this.mcp.discoverIfConnected(id);
if (!discoverResult?.success) {
console.error("Discovery failed:", discoverResult?.error);
}
}// 1. Register the server (saves to storage and creates in-memory connection)
const id = "my-server";
await this.mcp.registerServer(id, {
url: "https://mcp.example.com/mcp",
name: "My Server",
callbackUrl: "https://my-worker.workers.dev/agents/my-agent/default/callback",
transport: { type: "auto" },
});
// 2. Connect (initializes transport, handles OAuth if needed)
const connectResult = await this.mcp.connectToServer(id);
if (connectResult.state === "failed") {
console.error("Connection failed:", connectResult.error);
return;
}
if (connectResult.state === "authenticating") {
console.log("OAuth required:", connectResult.authUrl);
return;
}
// 3. Discover capabilities (transitions from "connected" to "ready")
if (connectResult.state === "connected") {
const discoverResult = await this.mcp.discoverIfConnected(id);
if (!discoverResult?.success) {
console.error("Discovery failed:", discoverResult?.error);
}
}Odběr událostí
// Listen for state changes (onServerStateChanged is an Event<void>)
const disposable = this.mcp.onServerStateChanged(() => {
console.log("MCP server state changed");
this.broadcastMcpServers(); // Notify connected clients
});
// Clean up the subscription when no longer needed
// disposable.dispose();// Listen for state changes (onServerStateChanged is an Event<void>)
const disposable = this.mcp.onServerStateChanged(() => {
console.log("MCP server state changed");
this.broadcastMcpServers(); // Notify connected clients
});
// Clean up the subscription when no longer needed
// disposable.dispose();Metody životního cyklu
this.mcp.registerServer()
Zaregistrujte server bez okamžitého připojení.
async registerServer(
id: string,
options: {
url: string;
name: string;
callbackUrl: string;
clientOptions?: ClientOptions;
transportOptions?: TransportOptions;
}
): Promise<string>this.mcp.connectToServer()
Naváže připojení k dříve registrovanému serveru.
async connectToServer(id: string): Promise<MCPConnectionResult>
type MCPConnectionResult =
| { state: "failed"; error: string }
| { state: "authenticating"; authUrl: string }
| { state: "connected" }this.mcp.discoverIfConnected()
Zkontroluje možnosti serveru, pokud je připojení aktivní.
async discoverIfConnected(
serverId: string,
options?: { timeoutMs?: number }
): Promise<MCPDiscoverResult | undefined>
type MCPDiscoverResult = {
success: boolean;
state: MCPConnectionState;
error?: string;
}this.mcp.waitForConnections()
Počkejte, až se ustálí všechny probíhající operace připojení a zjišťování MCP. To se hodí, když potřebujete this.mcp.getAITools() k vrácení úplné sady nástrojů ihned po probuzení agenta z hibernace.
// Wait indefinitely
await this.mcp.waitForConnections();
// Wait with a timeout (milliseconds)
await this.mcp.waitForConnections({ timeout: 10_000 });this.mcp.closeConnection()
Uzavře připojení k určitému serveru a přitom ho ponechá zaregistrované.
async closeConnection(id: string): Promise<void>this.mcp.closeAllConnections()
Uzavře všechna aktivní připojení k serveru a přitom zachová registrace.
async closeAllConnections(): Promise<void>this.mcp.listTools()
Získat nezpracované záznamy nástrojů MCP bez převodu jejich schémat na Zod.
listTools(filter?: MCPServerFilter): Array<Tool & { serverId: string }>Použijte tuto metodu pro zjišťování a prohlížení katalogu. Předejte MCPServerFilter k omezení vrácených nástrojů na konkrétní připojení.
this.mcp.getAITools()
Získat všechny objevené nástroje MCP ve formátu kompatibilním s AI SDK.
getAITools(filter?: MCPServerFilter): ToolSetNástroje se automaticky zařazují do jmenného prostoru podle ID serveru, aby nedocházelo ke konfliktům, když více MCP serverů nabízí nástroje se stejným názvem.
getAITools() při každém aktivním připojení znovu použije převedená schémata pro aktuální katalog. Schémata převede znovu poté, co objevování nahradí katalog nebo se aktivní připojení změní. Každé volání vrátí nové záznamy nástrojů a spouštěcí funkce. Použijte this.mcp.listTools() když potřebujete jen surový katalog.
Předejte MCPServerFilter k omezení vrácených nástrojů na podmnožinu připojených serverů:
// Tools from a specific server only
const githubTools = this.mcp.getAITools({ serverId: "github" });
// Tools from multiple servers
const tools = this.mcp.getAITools({ serverId: ["github", "notion"] });
// Tools from servers matching a name
const tools = this.mcp.getAITools({ serverName: "GitHub" });
// Only tools from servers that are ready
const tools = this.mcp.getAITools({ state: "ready" });// Tools from a specific server only
const githubTools = this.mcp.getAITools({ serverId: "github" });
// Tools from multiple servers
const tools = this.mcp.getAITools({ serverId: ["github", "notion"] });
// Tools from servers matching a name
const tools = this.mcp.getAITools({ serverName: "GitHub" });
// Only tools from servers that are ready
const tools = this.mcp.getAITools({ state: "ready" });Typ filtru je dostupný z agents/mcp/client:
import type { MCPServerFilter } from "agents/mcp/client";
type MCPServerFilter = {
serverId?: string | string[];
serverName?: string | string[];
state?: MCPConnectionState | MCPConnectionState[];
};Všechna zadaná filtrovací kritéria se kombinují operátorem AND. Stejný parametr filtru přijímá listTools(), listPrompts(), listResources(), a listResourceTemplates().
Zpracování chyb
Použijte nástroje pro detekci chyb ke zpracování chyb připojení:
import { isUnauthorized, isTransportNotImplemented } from "agents";
export class MyAgent extends Agent {
async onRequest(request) {
try {
await this.addMcpServer("Server", "https://mcp.example.com/mcp");
} catch (error) {
if (isUnauthorized(error)) {
return new Response("Authentication required", { status: 401 });
} else if (isTransportNotImplemented(error)) {
return new Response("Transport not supported", { status: 400 });
}
throw error;
}
}
}import { isUnauthorized, isTransportNotImplemented } from "agents";
export class MyAgent extends Agent {
async onRequest(request: Request) {
try {
await this.addMcpServer("Server", "https://mcp.example.com/mcp");
} catch (error) {
if (isUnauthorized(error)) {
return new Response("Authentication required", { status: 401 });
} else if (isTransportNotImplemented(error)) {
return new Response("Transport not supported", { status: 400 });
}
throw error;
}
}
}