
Keryx:面向MCP和API的全栈TypeScript框架
概览
全栈TypeScript框架,用于MCP和API。 Keryx 的核心理念是:单个动作类处理 HTTP 请求、WebSocket 消息、CLI 命令、后台任务和 MCP 工具调用——相同的输入、相同的 Zod 验证、相同的中间件、相同的响应。你写一次逻辑;唯一变化的是请求如何到达以及响应如何传递。 最后那个传输——MCP——就是我现在构建这个平台的原因。每个操作都会自动成为MCP工具,所以AI代理通过内置的OAuth 2.1进行认证,获得类型错误,并调用与你的HTTP客户端相同的验证端点。没有第二个MCP服务器需要维护,也没有需要保持同步的模式......你通过编写应用来暴露给代理。 而且它是真正的后台,不是玩具。
概述(AI 翻译)
全栈TypeScript框架,用于MCP和API。
这个项目是什么?
Keryx背后的核心理念是:只写一次控制器,它在任何地方都能用。 单个动作类处理HTTP请求、WebSocket消息、CLI命令、后台任务和MCP工具调用——相同的输入、相同的Zod验证、相同的中间件、相同的响应。你写一次逻辑;唯一变化的是请求如何到达以及响应如何被传递。
最后那个传输——MCP——就是我现在构建这个平台的原因。每个操作都会自动成为MCP工具,所以AI代理通过内置的OAuth 2.1进行认证,获得类型错误,并调用与你的HTTP客户端相同的验证端点。没有第二个MCP服务器需要维护,也没有需要保持同步的模式…你通过编写应用来暴露给代理。
而且它是真正的后台,不是玩具。
本页由 AI 翻译,技术细节请以原文为准。
README
以下为原文 README 节选,完整内容请查看原文。
Keryx
The fullstack TypeScript framework for MCP and APIs.
What is this Project?
The big idea behind Keryx: write your controller once, and it works everywhere. A single action class handles HTTP requests, WebSocket messages, CLI commands, background tasks, and MCP tool calls — same inputs, same Zod validation, same middleware, same response. You write the logic once; the only thing that changes is how the request arrived and how the response gets delivered.
That last transport — MCP — is the reason I built this now. Every action is automatically an MCP tool, so AI agents authenticate with built-in OAuth 2.1, get typed errors, and call the exact same validated endpoints your HTTP clients do. There’s no second MCP server to maintain and no schemas to keep in sync… you expose your app to agents by writing the app.
And it’s a real backend, not a toy. You get Drizzle ORM with auto-migrations, background tasks with fan-out on Resque, streaming responses (SSE, chunked binary, and the same stream over WebSocket and MCP), OpenAPI generation, pagination and caching helpers, database transactions, and a companion Vite + React frontend that reads your actions with end-to-end type safety — no code generation. It’s all on Bun, so TypeScript runs natively and startup is fast.
One Action, Every Transport
Here’s what that looks like in practice. This is one action:
export class UserCreate implements Action {
name = "user:create";
description = "Create a new user";
inputs = z.object({
name: z.string().min(3),
email: z.string().email(),
password: secret(z.string().min(8)),
});
web = { route: "/user", method: HTTP_METHOD.PUT };
task = { queue: "default" };
async run(params: ActionParams) {
const user = await createUser(params);
return { user: serializeUser(user) };
}
}
That one class gives you:
HTTP — PUT /api/user with JSON body, query params, or form data:
curl -X PUT http://localhost:8080/api/user \
-H "Content-Type: application/json" \
-d '{"name":"Evan","email":"[email protected]","password":"secret123"}'
WebSocket — send a JSON message over an open connection:
{ "messageType": "action", "action": "user:create",
"params": { "name": "Evan", "email": "[email protected]", "password": "secret123" } }
CLI — flags are generated from the Zod schema automatically:
./keryx.ts "user:create" --name Evan --email [email protected] --password secret123 -q | jq
Background Task — enqueued to a Resque worker via Redis:
安装
This server does not publish a one-line install command.
Open the repository installation guide 新手?先看 mcpskill.org 教程 →配置
{
"mcpServers": {
"my-app": {
"url": "http://localhost:8080/mcp"
}
}
}