INTEGRITY Dokumentace

McpAgent

McpAgent vytvoří stavový starší server MCP podložený Durable Object.

import { McpAgent } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";

export class MyMCP extends McpAgent {
	server = new McpServer({ name: "Demo", version: "1.0.0" });

	async init() {
		this.server.tool(
			"add",
			{ a: z.number(), b: z.number() },
			async ({ a, b }) => ({
				content: [{ type: "text", text: String(a + b) }],
			}),
		);
	}
}
src/index.ts
import { McpAgent } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";

export class MyMCP extends McpAgent {
	server = new McpServer({ name: "Demo", version: "1.0.0" });

	async init() {
		this.server.tool(
			"add",
			{ a: z.number(), b: z.number() },
			async ({ a, b }) => ({
				content: [{ type: "text", text: String(a + b) }],
			}),
		);
	}
}

To znamená, že každá instance vašeho serveru MCP má vlastní trvalý stav, který je podpořen Durable Object, s vlastním Databáze SQL.

Bezstavový server může definovat nástroje hodnotou @modelcontextprotocol/server a poskytovat je prostřednictvím createMcpHandler.

Pokud ale chcete, aby váš server MCP:

K tomu můžete použít níže uvedená API.

Přehled API

Vlastnost/metoda Popis
state Aktuální objekt stavu (uložený)
initialState Výchozí stav při spuštění instance
setState(state) Aktualizace a zachování stavu
onStateChanged(state) Volá se při změně stavu
sql Spouští SQL dotazy ve vestavěné databázi
server McpServer instanci pro registraci nástrojů
props Identita uživatele a tokeny z autentizace OAuth
elicitInput(options, context) Vyžádat od uživatele strukturovaný vstup
McpAgent.serve(path, options) Statická metoda pro vytvoření handleru Workeru

Nasazování pomocí McpAgent.serve()

McpAgent.serve() statická metoda vytvoří handler Workeru, který směruje požadavky na váš server MCP:

import { McpAgent } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";

export class MyMCP extends McpAgent {
	server = new McpServer({ name: "my-server", version: "1.0.0" });

	async init() {
		this.server.tool("square", { n: z.number() }, async ({ n }) => ({
			content: [{ type: "text", text: String(n * n) }],
		}));
	}
}

// Export the Worker handler
export default MyMCP.serve("/mcp");
import { McpAgent } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";

export class MyMCP extends McpAgent {
	server = new McpServer({ name: "my-server", version: "1.0.0" });

	async init() {
		this.server.tool("square", { n: z.number() }, async ({ n }) => ({
			content: [{ type: "text", text: String(n * n) }],
		}));
	}
}

// Export the Worker handler
export default MyMCP.serve("/mcp");

Toto je nejjednodušší způsob nasazení serveru MCP, zabere zhruba 15 řádků kódu. Tento serve() metoda automaticky zpracovává transport Streamable HTTP.

S ověřováním OAuth

Při použití OAuth Provider Library ↗, předejte svůj server MCP do apiHandlers:

import { OAuthProvider } from "@cloudflare/workers-oauth-provider";

export default new OAuthProvider({
	apiHandlers: { "/mcp": MyMCP.serve("/mcp") },
	authorizeEndpoint: "/authorize",
	tokenEndpoint: "/token",
	clientRegistrationEndpoint: "/register",
	defaultHandler: AuthHandler,
});
import { OAuthProvider } from "@cloudflare/workers-oauth-provider";

export default new OAuthProvider({
	apiHandlers: { "/mcp": MyMCP.serve("/mcp") },
	authorizeEndpoint: "/authorize",
	tokenEndpoint: "/token",
	clientRegistrationEndpoint: "/register",
	defaultHandler: AuthHandler,
});

Jurisdikce dat

Kvůli souladu s GDPR a požadavky na umístění dat určete jurisdikci, aby instance vašeho serveru MCP běžely ve specifických regionech:

// EU jurisdiction for GDPR compliance
export default MyMCP.serve("/mcp", { jurisdiction: "eu" });
// EU jurisdiction for GDPR compliance
export default MyMCP.serve("/mcp", { jurisdiction: "eu" });

S OAuth:

export default new OAuthProvider({
	apiHandlers: {
		"/mcp": MyMCP.serve("/mcp", { jurisdiction: "eu" }),
	},
	// ... other OAuth config
});
export default new OAuthProvider({
	apiHandlers: {
		"/mcp": MyMCP.serve("/mcp", { jurisdiction: "eu" }),
	},
	// ... other OAuth config
});

Když zadáte jurisdiction: "eu":

Dostupné jurisdikce zahrnují "eu" (Evropská unie) a "fedramp" (lokality vyhovující FedRAMP). Viz Umístění dat Durable Objects pro další možnosti.

Podpora hibernace

McpAgent instance automaticky podporují Hibernace WebSockets, což stavovým MCP serverům umožňuje během neaktivních period usnout, aniž by přišly o svůj stav. Vaši agenti tak spotřebovávají výpočetní prostředky jen tehdy, když aktivně zpracovávají požadavky, což snižuje náklady a zároveň zachovává celý kontext a historii konverzace.

Hibernace je ve výchozím nastavení povolená a nevyžaduje žádnou další konfiguraci.

Obnovitelnost streamu

McpAgent: transport Streamable HTTP přežije přibližně pětiminutový watchdog nečinného streamu na Cloudflare edge, takže probíhající volání nástrojů se při nestabilním připojení neztratí:

DurableObjectEventStore se exportuje z agents/mcp pro stavový WorkerTransport volajících, kteří vkládají transport dovnitř Agentu nebo Durable Object:

import { DurableObjectEventStore } from "agents/mcp";

const eventStore = new DurableObjectEventStore(this.ctx.storage);
import { DurableObjectEventStore } from "agents/mcp";

const eventStore = new DurableObjectEventStore(this.ctx.storage);

Viz Transport MCP pro konfiguraci transportu.

Ověřování a autorizace

Třída McpAgent poskytuje bezproblémovou integraci s OAuth Provider Library ↗ pro autentizace a autorizace.

Když se uživatel ověří u vašeho serveru MCP, informace o jeho identitě a tokeny jsou zpřístupněny přes props parametr, který umožňuje:

API pro synchronizaci stavu

McpAgent třída poskytuje plný přístup k API pro stav agenta:

Následující kód například implementuje server MCP, který si pamatuje hodnotu čítače a aktualizuje ho, když se add nástroj je volán:

import { McpAgent } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";

export class MyMCP extends McpAgent {
	server = new McpServer({
		name: "Demo",
		version: "1.0.0",
	});

	initialState = {
		counter: 1,
	};

	async init() {
		this.server.resource(`counter`, `mcp://resource/counter`, (uri) => {
			return {
				contents: [{ uri: uri.href, text: String(this.state.counter) }],
			};
		});

		this.server.tool(
			"add",
			"Add to the counter, stored in the MCP",
			{ a: z.number() },
			async ({ a }) => {
				this.setState({ ...this.state, counter: this.state.counter + a });

				return {
					content: [
						{
							type: "text",
							text: String(`Added ${a}, total is now ${this.state.counter}`),
						},
					],
				};
			},
		);
	}

	onStateChanged(state) {
		console.log({ stateUpdate: state });
	}
}
src/index.ts
import { McpAgent } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";

type State = { counter: number };

export class MyMCP extends McpAgent<Env, State, {}> {
	server = new McpServer({
		name: "Demo",
		version: "1.0.0",
	});

	initialState: State = {
		counter: 1,
	};

	async init() {
		this.server.resource(`counter`, `mcp://resource/counter`, (uri) => {
			return {
				contents: [{ uri: uri.href, text: String(this.state.counter) }],
			};
		});

		this.server.tool(
			"add",
			"Add to the counter, stored in the MCP",
			{ a: z.number() },
			async ({ a }) => {
				this.setState({ ...this.state, counter: this.state.counter + a });

				return {
					content: [
						{
							type: "text",
							text: String(`Added ${a}, total is now ${this.state.counter}`),
						},
					],
				};
			},
		);
	}

	onStateChanged(state: State) {
		console.log({ stateUpdate: state });
	}
}

Elicitace na starších serverech

Elicitace MCP ↗ umožňuje serveru vyžádat si vstup od uživatele během zpracování jiného požadavku, například volání nástroje. Starší postup definuje dva režimy:

Klient musí podporu daného režimu oznámit dřív, než mu ho server odešle.

Režim formuláře

Zavolejte this.server.server.elicitInput() v handleru nástroje. Předejte extra.requestId jako relatedRequestId aby se odpověď vrátila na streamu původního volání nástroje:

const result = await this.server.server.elicitInput(
	{
		mode: "form",
		message: "By how much do you want to increase the counter?",
		requestedSchema: {
			type: "object",
			properties: {
				amount: {
					type: "number",
					title: "Amount",
					minimum: 1,
					maximum: 100,
				},
			},
			required: ["amount"],
		},
	},
	{ relatedRequestId: extra.requestId },
);

if (result.action !== "accept" || !result.content) {
	return { content: [{ type: "text", text: "Counter unchanged." }] };
}

const amount = Number(result.content.amount);
const result = await this.server.server.elicitInput(
	{
		mode: "form",
		message: "By how much do you want to increase the counter?",
		requestedSchema: {
			type: "object",
			properties: {
				amount: {
					type: "number",
					title: "Amount",
					minimum: 1,
					maximum: 100,
				},
			},
			required: ["amount"],
		},
	},
	{ relatedRequestId: extra.requestId },
);

if (result.action !== "accept" || !result.content) {
	return { content: [{ type: "text", text: "Counter unchanged." }] };
}

const amount = Number(result.content.amount);

Kvůli zpětné kompatibilitě mohou formulářové požadavky vynechat mode: "form". Schéma podporuje ploché objekty s primitivními poli. Režim formuláře nepoužívejte k vyžádání hesel, klíčů API, přístupových tokenů, platebních údajů ani jiných tajných údajů.

Režim URL

Použijte režim URL pro interakce, které musí probíhat mimo klienta MCP. Požadavek obsahuje zprávu, URL a jedinečné elicitationId:

const elicitationId = crypto.randomUUID();
const result = await this.server.server.elicitInput(
	{
		mode: "url",
		message: "Connect your account to continue.",
		url: `https://example.com/connect?elicitationId=${elicitationId}`,
		elicitationId,
	},
	{ relatedRequestId: extra.requestId },
);

if (result.action !== "accept") {
	return { content: [{ type: "text", text: "Connection cancelled." }] };
}

return {
	content: [
		{
			type: "text",
			text: "Connection page opened. Complete it in your browser.",
		},
	],
};
const elicitationId = crypto.randomUUID();
const result = await this.server.server.elicitInput(
	{
		mode: "url",
		message: "Connect your account to continue.",
		url: `https://example.com/connect?elicitationId=${elicitationId}`,
		elicitationId,
	},
	{ relatedRequestId: extra.requestId },
);

if (result.action !== "accept") {
	return { content: [{ type: "text", text: "Connection cancelled." }] };
}

return {
	content: [
		{
			type: "text",
			text: "Connection page opened. Complete it in your browser.",
		},
	],
};

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 se stejným elicitationId.

Nevkládejte tajné údaje, osobní údaje ani předautorizovanou adresu URL chráněného prostředku do url. Produkční servery by měly používat HTTPS. Každý požadavek svažte s ověřeným uživatelem a ověřte, že externí postup dokončí tentýž uživatel.

Zpracování odpovědí

Oba režimy vracejí jednu ze tří akcí:

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.

Přijaté odpovědi formuláře zahrnují content která odpovídá requestedSchema. Odpovědi URL vynechávají content. Odpovědi typu decline a cancel ho obvykle vynechávají.

switch (result.action) {
	case "accept":
		// For form mode, validate and process result.content.
		break;
	case "decline":
		return { content: [{ type: "text", text: "Request declined." }] };
	case "cancel":
		return { content: [{ type: "text", text: "Request dismissed." }] };
}
switch (result.action) {
	case "accept":
		// For form mode, validate and process result.content.
		break;
	case "decline":
		return { content: [{ type: "text", text: "Request declined." }] };
	case "cancel":
		return { content: [{ type: "text", text: "Request dismissed." }] };
}

Další vzory s lidským zásahem najdete v Vzory s lidským zásahem.

Další kroky

Sestavte vzdálený server MCP

Začínáme s MCP servery na Cloudflare.

Nástroje MCP

Navrhněte a přidejte nástroje do svého serveru MCP.

Autorizace

Nastavte ověřování OAuth.

Zabezpečení serverů MCP

Osvědčené postupy zabezpečení pro produkční prostředí.

createMcpHandler

Vytvářejte bezstavové servery MCP.