INTEGRITY Документация

Инструменты

MCP-инструменты: функции, которые MCP-сервер предоставляет клиентам для вызова. LLM может вызвать инструмент, чтобы найти данные, выполнить вычисление или обратиться к API. MCP-сервер выполняет инструмент и возвращает его результат.

Используйте @modelcontextprotocol/server для не сохраняющего состояние createMcpHandler сервер. McpAgent устарел и не получает новых функций. Существующие McpAgent routes должны продолжать использовать @modelcontextprotocol/sdk только пока они мигрируют.

Пример WebMCP

Соедините мостом инструменты MCP из Cloudflare McpAgent с экспериментальным WebMCP API браузера Chrome.

Определение инструментов

Используйте server.registerTool() чтобы зарегистрировать инструмент на не сохраняющем состояние McpServer экземпляр. У каждого инструмента есть имя, описание и схема входных данных, заданная с помощью библиотеки схем, например Zod ↗ или Valibot ↗, и функцию-обработчик.

import { McpServer } from "@modelcontextprotocol/server";
import { z } from "zod";

function createServer() {
	const server = new McpServer({ name: "Math", version: "1.0.0" });

	server.registerTool(
		"add",
		{
			description: "Add two numbers together",
			inputSchema: { a: z.number(), b: z.number() },
		},
		async ({ a, b }) => ({
			content: [{ type: "text", text: String(a + b) }],
		}),
	);

	return server;
}
import { McpServer } from "@modelcontextprotocol/server";
import { z } from "zod";

function createServer() {
	const server = new McpServer({ name: "Math", version: "1.0.0" });

	server.registerTool(
		"add",
		{
			description: "Add two numbers together",
			inputSchema: { a: z.number(), b: z.number() },
		},
		async ({ a, b }) => ({
			content: [{ type: "text", text: String(a + b) }],
		}),
	);

	return server;
}

Обработчик инструмента получает провалидированный ввод и должен вернуть объект с content массив. Каждый элемент содержимого имеет type (обычно "text") и соответствующие данные.

Результаты инструмента

Результаты инструмента возвращаются в виде массива частей содержимого. Самый распространённый тип: text, но вы также можете возвращать изображения и встроенные ресурсы.

server.registerTool(
	"lookup",
	{
		description: "Look up a user by ID",
		inputSchema: { userId: z.string() },
	},
	async ({ userId }) => {
		const user = await db.getUser(userId);

		if (!user) {
			return {
				isError: true,
				content: [{ type: "text", text: `User ${userId} not found` }],
			};
		}

		return {
			content: [{ type: "text", text: JSON.stringify(user, null, 2) }],
		};
	},
);
server.registerTool(
	"lookup",
	{
		description: "Look up a user by ID",
		inputSchema: { userId: z.string() },
	},
	async ({ userId }) => {
		const user = await db.getUser(userId);

		if (!user) {
			return {
				isError: true,
				content: [{ type: "text", text: `User ${userId} not found` }],
			};
		}

		return {
			content: [{ type: "text", text: JSON.stringify(user, null, 2) }],
		};
	},
);

Задайте isError: true чтобы сообщить, что вызов инструмента завершился ошибкой. LLM получает сообщение об ошибке и может решить, как действовать дальше.

Описания инструментов

description параметр критически важен: именно его читает LLM, чтобы решить, стоит ли и когда вызывать ваш инструмент. Пишите описания, которые:

Валидация ввода с помощью Zod

Входные данные инструмента задаются схемами Zod и автоматически проверяются перед запуском обработчика. Используйте .describe() метод, чтобы дать LLM контекст о каждом параметре.

server.registerTool(
	"search",
	{
		description: "Search for documents by query",
		inputSchema: {
			query: z.string().describe("The search query"),
			limit: z
				.number()
				.min(1)
				.max(100)
				.default(10)
				.describe("Maximum number of results to return"),
			category: z
				.enum(["docs", "blog", "api"])
				.optional()
				.describe("Filter by content category"),
		},
	},
	async ({ query, limit, category }) => {
		const results = await searchIndex(query, { limit, category });
		return {
			content: [{ type: "text", text: JSON.stringify(results) }],
		};
	},
);
server.registerTool(
	"search",
	{
		description: "Search for documents by query",
		inputSchema: {
			query: z.string().describe("The search query"),
			limit: z
				.number()
				.min(1)
				.max(100)
				.default(10)
				.describe("Maximum number of results to return"),
			category: z
				.enum(["docs", "blog", "api"])
				.optional()
				.describe("Filter by content category"),
		},
	},
	async ({ query, limit, category }) => {
		const results = await searchIndex(query, { limit, category });
		return {
			content: [{ type: "text", text: JSON.stringify(results) }],
		};
	},
);

Использование инструментов с createMcpHandler

Для серверов MCP без сохранения состояния определяйте инструменты внутри фабричной функции и передавайте сервер в createMcpHandler:

import { createMcpHandler } from "agents/mcp/server";
import { McpServer } from "@modelcontextprotocol/server";
import { z } from "zod";

function createServer() {
	const server = new McpServer({ name: "My Tools", version: "1.0.0" });

	server.registerTool(
		"ping",
		{ description: "Check if the server is alive", inputSchema: {} },
		async () => ({
			content: [{ type: "text", text: "pong" }],
		}),
	);

	return server;
}

export default {
	fetch(request, env, ctx) {
		return createMcpHandler(createServer)(request, env, ctx);
	},
};
import { createMcpHandler } from "agents/mcp/server";
import { McpServer } from "@modelcontextprotocol/server";
import { z } from "zod";

function createServer() {
	const server = new McpServer({ name: "My Tools", version: "1.0.0" });

	server.registerTool(
		"ping",
		{ description: "Check if the server is alive", inputSchema: {} },
		async () => ({
			content: [{ type: "text", text: "pong" }],
		}),
	);

	return server;
}

export default {
	fetch(request, env, ctx) {
		return createMcpHandler(createServer)(request, env, ctx);
	},
} satisfies ExportedHandler;

Использование инструментов с McpAgent

Этот раздел применяется только к существующим устаревшим маршрутам во время миграции. Определите их инструменты в init() метод объекта McpAgent. Инструменты получают доступ к экземпляру агента через this, чтобы они могли читать и записывать состояние.

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: "Stateful Tools", version: "1.0.0" });

	async init() {
		this.server.tool(
			"incrementCounter",
			"Increment and return a counter",
			{},
			async () => {
				const count = (this.state?.count ?? 0) + 1;
				this.setState({ count });
				return {
					content: [{ type: "text", text: `Counter: ${count}` }],
				};
			},
		);
	}
}
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: "Stateful Tools", version: "1.0.0" });

	async init() {
		this.server.tool(
			"incrementCounter",
			"Increment and return a counter",
			{},
			async () => {
				const count = (this.state?.count ?? 0) + 1;
				this.setState({ count });
				return {
					content: [{ type: "text", text: `Counter: ${count}` }],
				};
			},
		);
	}
}

Следующие шаги

Создайте удалённый MCP-сервер

Пошаговое руководство по развёртыванию сервера MCP на Cloudflare.

API createMcpHandler

Справочник по MCP-серверам без сохранения состояния.

McpAgent API

Справочник по MCP-серверам с сохранением состояния.

MCP-авторизация

Добавьте аутентификацию OAuth в ваш MCP-сервер.