@arcadeai/arcade-mcp
TypeScript MCP框架,带有秘密注入、OAuth身份验证提供者、多用户支持、工作路由和中间件。包裹官方 @modelcontextprotocol/sdk --切勿对其进行分叉或修补。
快速开始
bun add @arcadeai/arcade-mcpimport { MCPApp } from "@arcadeai/arcade-mcp";
import { z } from "zod";
const app = new MCPApp({
name: "MyServer",
version: "1.0.0",
instructions: "A helpful tool server",
});
app.tool(
"greet",
{
description: "Greet someone by name",
parameters: z.object({
name: z.string().describe("Name to greet"),
}),
},
async (args) => `Hello, ${args.name}!`,
);
app.run(); // stdio by default运行它:
bun run server.ts或者通过HTTP:
app.run({ transport: "http", port: 8000 });CLI自动发现
在不写入服务器文件的情况下运行MCP服务器。CLI会自动发现当前目录中的工具模块:
npx @arcadeai/arcade-mcp # auto-discover tools, run stdio
npx @arcadeai/arcade-mcp --http # auto-discover tools, run HTTP工具模块可以从以下位置找到:
*.tools.ts/*.tools.js文件(例如。,math.tools.ts)- a中的任何文件
tools/目录(例如。,tools/greet.ts)
每个文件都应导出工具定义:
// tools/greet.ts
import { z } from "zod";
export const greetTools = {
greet: {
options: {
description: "Greet someone",
parameters: z.object({ name: z.string() }),
},
handler: async (args) => `Hello, ${args.name}!`,
},
};CLI选项:
| 标志 | 默认值 | 描述 |
|---|---|---|
--http | -- | 使用HTTP传输(默认:stdio) |
--host | 127.0.0.1 | HTTP主机 |
--port | 8000 | HTTP端口 |
--name | 目录名称 | 应用程序名称 |
| `--dir | ||
| ` | cwd | 要扫描的目录 |
--dev | -- | 文件更改时自动重新加载(仅限HTTP) |
Node.js+TypeScript:使用npx tsx arcade-mcp或Bun进口.ts工具文件直接。
开发模式(自动重新加载)
监视源文件并在发生更改时自动重新启动服务器:
npx @arcadeai/arcade-mcp --http --dev或者以编程方式:
app.run({ transport: "http", dev: true });当a .ts, .js, .mts,或 .mjs 文件更改后,服务器停止,用新副本重新导入工具模块,然后重新启动。文件在 node_modules/, dist/,并且忽略隐藏目录。
备注:开发模式仅适用于HTTP传输。Stdio会话无法重新启动。
您还可以通过以下方式启用开发模式 ARCADE_SERVER_RELOAD=1 环境变量。
特性
- 生成器API —
app.tool(name, options, handler)使用方法链 - 秘密注射 --env-vars自动捕获并注入到工具上下文中
- OAuth身份验证提供者 --21家提供商:GitHub、谷歌、Slack、微软、Linear、Notion等。
- 上下文对象 --用于日志记录、进度、采样、资源、工具、UI的命名空间外观
- 中间件 --具有方法特定钩子的可组合洋葱模型中间件
- 多用户HTTP身份验证 --通过JWKS验证JWT承载令牌
- 工人路线 —
/worker/tools,/worker/tools/invoke,/worker/health - 错误层次结构 --支持重试的结构化错误,上游错误映射
- 提示 —
app.prompt(name, options, handler)具有参数验证和运行时管理 - 资源 —
app.resource(uri, options, handler)具有MIME类型和运行时管理 - 开发模式 --文件更改时自动重新加载
--dev标志(仅限HTTP) - 可恢复流 --HTTP流可恢复性的可选事件存储
Last-Event-ID - 评估 --使用评论家、量规和匈牙利语最佳匹配来评估LLM工具调用的准确性
- 双重运输 --stdio和HTTP(Elysia+流式HTTP)
- 运行时兼容 --Bun和Node.js(否
Bun.*库代码中的API)
工具选项
基本工具
app.tool(
"echo",
{
description: "Echo a message",
parameters: z.object({
message: z.string(),
}),
},
async (args) => args.message,
);OAuth工具
import { auth } from "@arcadeai/arcade-mcp";
app.tool(
"star_repo",
{
description: "Star a GitHub repository",
parameters: z.object({
owner: z.string(),
repo: z.string(),
}),
auth: auth.GitHub({ scopes: ["repo"] }),
},
async (args, context) => {
const token = context.getAuthToken();
// ... use token to call GitHub API
return { starred: true };
},
);秘密工具
app.tool(
"get_repo",
{
description: "Get repo info",
parameters: z.object({ repo: z.string() }),
secrets: ["GITHUB_TOKEN"],
},
async (args, context) => {
const token = context.getSecret("GITHUB_TOKEN");
// ... use token
},
);任何未加前缀的env变量 MCP_ 或 _ 可作为工具秘密使用。
带有行为提示的工具
用映射到MCP的行为提示注释工具 ToolAnnotations:
app.tool(
"delete_file",
{
description: "Delete a file from the workspace",
parameters: z.object({ path: z.string() }),
behavior: {
readOnly: false,
destructive: true,
idempotent: true,
openWorld: false,
},
},
async (args) => {
// ...
},
);这些提示如下 readOnlyHint, destructiveHint, idempotentHint,以及 openWorldHint 在MCP工具列表中。
弃用的工具
将工具标记为已弃用——该消息将附加在描述之前:
app.tool(
"old_search",
{
description: "Search for items",
parameters: z.object({ query: z.string() }),
deprecationMessage: "Use search_v2 instead",
},
async (args) => {
// ...
},
);
// Description seen by clients: "[DEPRECATED: Use search_v2 instead] Search for items"工具标题
提供人类可读的显示名称:
app.tool(
"gh_star",
{
description: "Star a GitHub repository",
parameters: z.object({ repo: z.string() }),
title: "Star Repository",
},
async (args) => {
// ...
},
);工具包版本控制
应用程序的 name, version,以及 title 作为工具包元数据自动附加到每个工具。您还可以覆盖每个工具的工具包信息:
app.tool(
"myTool",
{
description: "A tool with custom toolkit info",
parameters: z.object({}),
toolkit: { name: "my-toolkit", version: "1.2.0" },
},
async () => {},
);版本已标准化为semver-- "1" 成为 "1.0.0", "v1.2" 成为 "1.2.0".
提示
注册提示 app.prompt(name, options, handler?):
app.prompt(
"greeting",
{
description: "Generate a greeting",
arguments: [{ name: "name", description: "Name to greet", required: true }],
},
(args) => ({
messages: [
{
role: "user",
content: { type: "text", text: `Please greet ${args.name} warmly.` },
},
],
}),
);选项: description? 和 arguments? (数组 { name, description?, required? }).如果没有提供处理程序,则默认处理程序将描述作为用户消息返回。
运行时管理(之后 app.run()):
app.prompts.add("new-prompt", { description: "Added at runtime" }, handler);
app.prompts.remove("new-prompt");
app.prompts.list(); // returns registered prompt names资源
注册资源 app.resource(uri, options, handler?):
app.resource(
"config://app",
{ description: "Application configuration", mimeType: "application/json" },
(uri) => ({
contents: [
{
uri: uri.href,
mimeType: "application/json",
text: JSON.stringify({ name: "EchoServer", version: "1.0.0" }),
},
],
}),
);选项: description? 和 mimeType?。如果没有提供处理程序,默认处理程序将返回空文本内容。
运行时管理(之后 app.run()):
app.resources.add("data://users", { mimeType: "application/json" }, handler);
app.resources.remove("data://users");
app.resources.list(); // returns registered resource URIs认证提供商
21个OAuth2提供程序的工厂功能:
import { auth } from "@arcadeai/arcade-mcp";
auth.GitHub({ scopes: ["repo"] })
auth.Google({ scopes: ["https://www.googleapis.com/auth/calendar"] })
auth.Slack({ scopes: ["chat:write"], id: "my-slack" })
auth.Microsoft()
auth.Linear()
auth.Notion()
// ... Asana, Atlassian, Attio, ClickUp, Discord, Dropbox,
// Figma, Hubspot, LinkedIn, PagerDuty, Reddit, Spotify,
// Twitch, X, ZoomArcade Cloud Auth(本地开发)
工具与 auth 需求通过以下方式自动解析OAuth令牌 街机云。设置凭据有两种方法:
选项1:Arcade CLI(推荐)
安装Arcade CLI并登录。这将凭据存储在 ~/.arcade/credentials.yaml 框架自动读取:
pip install arcade-ai
arcade login就是这样——不需要环境变量。运行您的服务器,工具将通过您的Arcade帐户进行身份验证:
bun run examples/github-tools/server.ts选项2:环境变量
集 ARCADE_API_KEY 和 ARCADE_USER_ID 直接:
export ARCADE_API_KEY="your-arcade-api-key"
export ARCADE_USER_ID="your-user-id"环境变量优先于凭据文件。
运作原理
当一个工具 auth 被调用,该框架调用Arcade Cloud的授权API:
- 第一个电话 --返回一个授权URL。在浏览器中访问该URL以完成OAuth流程。
- 重试该工具 --令牌现在可用并注入
context.getAuthToken().
这是自动的,不需要更改代码。相同的工具在本地(通过Arcade Cloud身份验证)和部署(通过ArcadeCloud直接注入令牌的工作路由)都能工作。
集 ARCADE_AUTH_DISABLED=true 跳过身份验证解析(对于使用模拟令牌进行测试很有用)。
上下文
工具处理程序接收 (args, context)。上下文提供了命名空间的立面:
app.tool("example", opts, async (args, context) => {
// Secrets & auth
context.getSecret("API_KEY");
context.getAuthToken();
context.getAuthTokenOrEmpty();
// Logging
context.log.info("Processing request");
context.log.debug("Details", { extra: "data" });
context.log.warning("Watch out");
context.log.error("Something failed");
// Progress
await context.progress.report(50, 100, "Halfway done");
// Notifications (deduplicated, flushed at end of request)
await context.notifications.tools.listChanged();
await context.notifications.resources.listChanged();
await context.notifications.prompts.listChanged();
// Metadata
context.signal; // AbortSignal
context.sessionId; // string | undefined
context.requestId; // string
context.userId; // string | undefined
});中间件
具有洋葱模型的可组合中间件。覆盖任何钩子:
import { Middleware, composeMiddleware } from "@arcadeai/arcade-mcp";
class RateLimitMiddleware extends Middleware {
async onCallTool(context, next) {
// before
const result = await next(context);
// after
return result;
}
}
const app = new MCPApp({
name: "MyServer",
version: "1.0.0",
middleware: composeMiddleware(
new RateLimitMiddleware(),
),
});可用挂钩: onMessage, onRequest, onCallTool, onListTools, onReadResource, onListResources, onListResourceTemplates, onGetPrompt, onListPrompts.
内置中间件(默认启用):
- 错误处理中间件 --捕获错误,返回结构化MCP错误响应
- 日志中间件 --记录请求/响应时间(自动检测TTY以获得漂亮的输出;用覆盖
MCP_LOG_FORMAT=json|pretty)
多用户HTTP身份验证
根据JWKS端点验证JWT承载令牌:
import { MCPApp, JWTResourceServerValidator } from "@arcadeai/arcade-mcp";
const app = new MCPApp({
name: "MyServer",
version: "1.0.0",
auth: new JWTResourceServerValidator({
canonicalUrl: "https://mcp.example.com/mcp",
authorizationServers: [{
authorizationServerUrl: "https://auth.example.com",
issuer: "https://auth.example.com",
jwksUri: "https://auth.example.com/.well-known/jwks.json",
algorithm: "RS256",
expectedAudiences: ["my-client-id"],
}],
}),
});
app.run({ transport: "http", port: 8000 });支持RFC 9728 OAuth保护资源元数据发现。当 canonicalUrl 具有非根路径(例如。 https://example.com/mcp),两者 /.well-known/oauth-protected-resource 和 /.well-known/oauth-protected-resource/mcp 已注册以实现向后兼容性。响应包括CORS标头。
可恢复流
启用HTTP流可恢复性,以便断开连接的客户端可以使用 Last-Event-ID 头球
import { MCPApp, InMemoryEventStore } from "@arcadeai/arcade-mcp";
const app = new MCPApp({ name: "MyServer", version: "1.0.0" });
app.run({
transport: "http",
eventStore: new InMemoryEventStore(),
});这个 InMemoryEventStore 适用于单进程部署。对于分布式系统,实现 EventStore 与持久后端的接口:
import type { EventStore, EventId, StreamId } from "@arcadeai/arcade-mcp";
import type { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js";
class RedisEventStore implements EventStore {
async storeEvent(streamId: StreamId, message: JSONRPCMessage): Promise {
// Store in Redis...
}
async replayEventsAfter(
lastEventId: EventId,
{ send }: { send: (eventId: EventId, message: JSONRPCMessage) => Promise },
): Promise {
// Replay from Redis...
}
}会话管理
HTTP传输使用 HTTPSessionManager 支持有状态(默认)和无状态模式、基于TTL的会话驱逐和最大会话上限:
app.run({
transport: "http",
stateless: false, // true = fresh transport per request, no session reuse
sessionTtlMs: 300_000, // evict idle sessions after 5 minutes
maxSessions: 100, // reject new sessions with 503 when at capacity
});在 有状态模式 (默认),会话通过以下方式重用 mcp-session-id 头球无效的会话ID将收到400响应。
在 无状态模式,每个请求都会得到一个新的传输和服务器——不会跟踪任何会话。
您还可以使用 HTTPSessionManager 直接进行更多控制:
import { HTTPSessionManager } from "@arcadeai/arcade-mcp";
const manager = new HTTPSessionManager({
server: arcadeMcpServer,
sessionTtlMs: 60_000,
maxSessions: 50,
});
// In your HTTP handler:
const response = await manager.handleRequest(request, { authInfo });
// Graceful shutdown:
await manager.close();每个有状态的HTTP会话都由一个 ServerSession 它补充道:
- 初始化状态跟踪 —
NOT_INITIALIZED → INITIALIZING → INITIALIZED - 服务器发起的请求 —
createMessage(),elicitInput(),listRoots()具有超时和错误处理功能 - 会话范围的数据 --通过以下方式为每个会话存储密钥/值
getData()/setData() - 通知广播 —
NotificationManager向所有或选定会话发送工具/资源/提示列表更改通知
这个 Context 立面 context.sampling.createMessage() 和 context.ui.elicit() 自动委派给 ServerSession 如果可用。
工人路线
当 ARCADE_WORKER_SECRET 已设置,公开Arcade Cloud集成的工具执行端点:
import { createWorkerRoutes } from "@arcadeai/arcade-mcp";
const workerApp = createWorkerRoutes({
catalog: app.catalog,
secret: process.env.ARCADE_WORKER_SECRET,
});| 端点 | 方法 | 身份验证 | 描述 |
|---|---|---|---|
/worker/tools | GET | Bearer | 列出可用工具(裸阵列) |
/worker/tools/invoke | POST | 承载 | 执行工具 |
/worker/health | GET | 无 | 健康检查 |
worker wire格式与Python匹配 arcade-mcp 框架准确:
GET /worker/tools返回一个包含工具定义的JSON数组(未包装在{ tools: [...] }),使用input.parameters随着value_schema(不是JSON模式inputSchema),fully_qualified_name,requirements,以及output领域。POST /worker/tools/invoke接受{ tool: { name, toolkit, version }, inputs, context: { user_id, authorization, secrets, metadata }, run_id, execution_id, created_at }.- 回复 使用
snake_case字段名称(execution_id,finished_at)结构化output: { value, error: { message, kind, can_retry, ... }, requires_authorization }. - 工具名称分隔符 默认为
.(例如。,MyToolkit.echo),可通过以下方式配置ARCADE_TOOL_NAME_SEPARATOR.
错误处理
工具执行的结构化错误层次结构:
import {
RetryableToolError,
FatalToolError,
UpstreamError,
ContextRequiredToolError,
} from "@arcadeai/arcade-mcp";
// Retryable error (LLM will retry)
throw new RetryableToolError("Rate limited, try again", {
retryAfterMs: 5000,
additionalPromptContent: "Wait a moment before retrying",
});
// Fatal error (no retry)
throw new FatalToolError("API key is invalid");
// Upstream service error (auto-maps status codes)
throw new UpstreamError("GitHub API failed", { statusCode: 503 });
// Needs more context from the user
throw new ContextRequiredToolError("Missing info", {
additionalPromptContent: "Please specify the repository owner",
});遥测(开放遥测)
内置OpenTetry支持跟踪和指标,通过OTLP HTTP导出。
使用环境变量启用:
ARCADE_MCP_OTEL_ENABLE=true \
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 \
bun run server.ts启用后,框架会自动:
- 创建
RunTool围绕每个MCP工具执行(tool_name,toolkit_name,environment属性) - 创建
CallTool和Catalog工人路线中的跨度 - 增量a
tool_call每次工具调用的计数器度量 - 通过OTLP HTTP将跟踪和指标导出到配置的端点
OTLP端点、标头和协议通过标准配置 OTEL_EXPORTER_OTLP_* env变量。
| 变量 | 默认值 | 描述 |
|---|---|---|
ARCADE_MCP_OTEL_ENABLE | false | 启用开放遥测 |
OTEL_SERVICE_NAME | arcade-mcp-worker | 跟踪中的服务名称 |
OTEL_EXPORTER_OTLP_ENDPOINT | -- | OTLP收集器端点 |
ARCADE_ENVIRONMENT | dev | 部署环境名称 |
您还可以使用 OTELHandler 直接用于自定义集成:
import { OTELHandler } from "@arcadeai/arcade-mcp";
const telemetry = new OTELHandler({
enable: true,
serviceName: "my-service",
environment: "production",
});
telemetry.initialize();
// ... use telemetry.getTracer(), telemetry.getMeter()
await telemetry.shutdown();评估
评估LLM如何使用您的工具。定义预期的工具调用,并与批评者一起对结果进行评分。
import { EvalSuite, BinaryCritic, NumericCritic, SimilarityCritic } from "@arcadeai/arcade-mcp";基本评估
const suite = new EvalSuite({
name: "My Tool Eval",
systemMessage: "You are a helpful assistant.",
rubric: { failThreshold: 0.85, warnThreshold: 0.95 },
});
// Register tools (MCP-style definitions)
suite.addToolDefinitions([
{
name: "greet",
description: "Greet someone by name",
inputSchema: {
type: "object",
properties: { name: { type: "string" } },
required: ["name"],
},
},
]);
// Add test cases
suite.addCase({
name: "Greet Alice",
userMessage: "Say hello to Alice",
expectedToolCalls: [{ toolName: "greet", args: { name: "Alice" } }],
critics: [new BinaryCritic({ field: "name" })],
});
// Run against an LLM
import Anthropic from "@anthropic-ai/sdk";
const results = await suite.run({
client: new Anthropic(),
model: "claude-sonnet-4-20250514",
provider: "anthropic",
});
for (const c of results.cases) {
console.log(`${c.evaluation.passed ? "PASS" : "FAIL"} ${c.name} (${c.evaluation.score})`);
}OpenAI也能工作——通过 OpenAI 自动检测客户端和提供者。
使用工具目录
如果您已经在 MCPApp 或 ToolCatalog,直接添加它们:
suite.addFromCatalog(app.catalog);批评者
评论家们对工具调用的各个论点进行了评分:
| 评论家 | 用例 | 关键选项 |
|---|---|---|
BinaryCritic | 完全相等(带类型强制) | field, weight? |
NumericCritic | 模糊数值范围匹配 | field, valueRange, matchThreshold?, weight? |
SimilarityCritic | 词频余弦相似度 | field, similarityThreshold?, weight? |
// Exact match
new BinaryCritic({ field: "city" })
// Numeric within range [1, 7], match if similarity >= 0.9
new NumericCritic({ field: "days", valueRange: [1, 7], matchThreshold: 0.9 })
// String similarity >= 0.75
new SimilarityCritic({ field: "description", similarityThreshold: 0.75 })评分标准
这个 EvalRubric 控制通过/失败/警告阈值:
| 选项 | 默认值 | 描述 |
|---|---|---|
failThreshold | 0.8 | 通过的最低分数 |
warnThreshold | 0.9 | 低于此分数会触发警告 |
failOnToolSelection | true | 如果调用了错误的工具,则立即失败 |
failOnToolCallQuantity | true | 如果呼叫次数错误,立即失败 |
toolSelectionWeight | 1.0 | 工具名称匹配的权重 |
跑步逃生
# With Anthropic
ANTHROPIC_API_KEY=sk-ant-... bun run examples/evals/echo-eval.ts
# With OpenAI
OPENAI_API_KEY=sk-... bun run examples/evals/echo-eval.ts看 examples/evals/ 查看完整示例。
示例
这个 examples/ 目录包含演示不同功能的可运行服务器。使用以下命令运行任何示例:
bun run examples/echo/server.ts配置
所有设置都从环境变量加载:
| 变量 | 默认值 | 描述 |
|---|---|---|
MCP_SERVER_NAME | ArcadeMCP | 服务器名称 |
MCP_SERVER_VERSION | 0.1.0 | 服务器版本 |
MCP_SERVER_INSTRUCTIONS | -- | 服务器说明 |
MCP_MIDDLEWARE_ENABLE_LOGGING | true | 启用日志中间件 |
MCP_MIDDLEWARE_LOG_LEVEL | INFO | 日志级别 |
MCP_LOG_FORMAT | auto | 日志格式: json (结构化), pretty (彩色,人类可读),或自动(TTY格式,JSON格式) |
MCP_MIDDLEWARE_MASK_ERROR_DETAILS | false | 向客户端隐藏错误详细信息 |
ARCADE_MCP_OTEL_ENABLE | false | 启用OpenTetry遥测 |
OTEL_SERVICE_NAME | arcade-mcp-worker | OTEL服务名称 |
ARCADE_WORKER_SECRET | -- | 工人路线的承载令牌 |
ARCADE_API_KEY | - | Arcade API密钥 |
ARCADE_API_URL | https://api.arcade.dev | Arcade API URL |
ARCADE_USER_ID | -- | 默认用户ID |
ARCADE_TOOL_NAME_SEPARATOR | . | FQN中工具包和工具名称之间的分隔符 |
看 .env.example 查看完整列表。
许可证
麻省理工学院
