INTEGRITY Dokumentace

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:

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:

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í:

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}/callback

Napří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:

  1. Zobrazte, který server MCP odeslal požadavek.
  2. Zobrazte zprávu požadavku, cílového hostitele a úplnou adresu URL.
  3. Před otevřením URL si vyžádejte souhlas.
  4. Otevřete stránku v kontextu prohlížeče, který Agent ani model nemohou zkoumat.
  5. Vraťte action: "accept" bez content po udělení souhlasu.
  6. 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)

Parametry (RPC transport)

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í:

removeMcpServer()

Odpojte se od serveru MCP a uvolněte jeho prostředky.

async removeMcpServer(id: string): Promise<void>

Parametry

getMcpServers()

Získat aktuální stav všech připojení k MCP serverům.

getMcpServers(): MCPServersState

Vrací

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í:

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>;
}): void

Parametry

Výchozí chování

Když není zadána žádná konfigurace:

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>;
}): void

Parametry

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): ToolSet

Ná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;
		}
	}
}

Další kroky

Vytváření serverů MCP

Sestavte si vlastní server MCP.

Client SDK

Připojení z prohlížečů pomocí onMcpUpdate.

Ukládání a synchronizace stavu

Seznamte se s perzistencí agentů.