使用说明
Civic MCP挂钩
模型上下文协议(MCP)的中间件层,支持AI工具交互的监控、验证和转换。
了解MCP及其为什么需要钩子
什么是MCP?
模型上下文协议(MCP)是一种标准,允许人工智能助手(如克劳德)与外部工具和服务进行交互。将其视为一种通用语言,让人工智能模型:
- 读取和写入文件
- 查询数据库
- 调用API
- 执行代码
- 访问各种服务
当您使用带有MCP的AI助手时,它可以代表您执行实际操作,使其在自动化和生产力方面非常强大。
为什么要添加中间件层?
虽然MCP的力量令人兴奋,但它也提出了重要的问题:
- 安全:我们如何确保AI只访问它应该访问的内容?
- 审计:我们如何跟踪采取了哪些行动?
- 控制:我们如何防止意外或危险的操作?
- 定制:我们如何修改特定用例的行为?
这就是钩子发挥作用的地方。就像web应用程序使用中间件来处理身份验证、日志记录和请求处理一样,MCP也可以从类似的模式中受益。
此解决方案的工作原理
我们的方法引入了一个位于AI和实际MCP工具之间的“直通服务器”:
AI Assistant ←→ Passthrough Server ←→ Target MCP Server
↓↑
[Hooks]
以下是当AI想要使用工具时会发生的情况:
1. Request: AI → Passthrough → Hook 1 → Hook 2 → ... → Target MCP Server
2. Response: AI ← Passthrough ← Hook 2 ← Hook 1 ← ... ← Target MCP Server
直通服务器:
- 截距 来自AI的所有请求
- 过程 他们穿过一串钩子
- 前进 对实际工具的批准请求
- 退货 响应(也通过钩子处理)
链条上的每个钩子都可以:
- 检查 请求(AI试图做什么?)
- 修改 请求(更改参数,添加上下文)
- 批准或拒绝 请求(安全和验证)
- 执行副作用 (审计日志、通知、分析)
- 变换 响应(格式化、过滤或增强结果)
真实世界的例子
为什么我们需要MCP专用挂钩?这关系到 关注点分离 以及LLM工具使用的独特挑战。
核心问题:MCP服务器旨在做好一件事——提供工具。它们不应该充斥着身份验证逻辑、审计跟踪或特定于上下文的修改。此外,OAuth(MCP使用)缺乏LLM交互所需的粒度-无法表示“允许文件读取,但仅在/docs文件夹中”或“允许API调用,但基于内容进行速率限制”
示例1:LLM专用护栏
您的MCP文件服务器提供简单的文件操作。但是LLM需要与人类用户不同的规则:
Human user: Can precisely click on files they need
LLM: Might try to read entire directory trees to "be helpful"
Guardrail Hook: Limits directory traversal depth, prevents reading
binary files, and caps file sizes - rules that only make sense for LLMs
MCP服务器保持简单,而钩子添加了LLM特定的安全轨道。
示例2:上下文相关工具描述和提示
对于不同的用例,同一个工具可能需要不同的描述:
Standard fetch tool: "Retrieves web content"
In a research environment:
"Retrieves web content (academic sources preferred, checks Sci-Hub)"
In a corporate environment:
"Retrieves web content (internal wiki only, external sites blocked)"
自定义描述钩子根据您的上下文修改工具描述——这是原始MCP服务器不能也不应该处理的。
示例3:使用Explain Hook强制透明
LLM可以在不解释原因的情况下使用工具。Explain Hook添加了一个必需的“原因”参数:
Without hook:
AI: execute_sql("DROP TABLE users")
With Explain Hook:
AI: execute_sql("DROP TABLE users", reason="User requested database cleanup")
这种修改发生在中间件层——MCP服务器不需要更改。
示例4:语义审计跟踪
MCP服务器返回原始响应。审计钩子可以添加语义含义:
MCP Server returns: {status: "success", rowsAffected: 1523}
Audit Hook logs:
- Tool: database_query
- Action: "Bulk update customer emails"
- Impact: 1523 records modified
- Context: "Part of migration from old email domain"
- Risk level: High (bulk data modification)
MCP服务器仍然专注于数据库操作,而钩子处理合规性日志记录。
可用包
这个monorepo包含了向MCP添加中间件层所需的一切:
核心基础设施
@civil/passthrough mcp服务器 拦截MCP流量并通过钩子路由它的主代理服务器。这是使其他一切成为可能的基础。
@市政/钩公用
用于构建钩子的共享实用程序和TypeScript类型。提供 AbstractHook 基类,使创建新钩子变得简单。
钩子示例
@公民/审计挂钩 记录每个请求和响应,以进行调试和合规性。非常适合了解您的AI正在做什么并维护审计跟踪。
@市政/护栏挂钩 实施安全规则以过滤和验证请求。包括一个阻止访问某些域的示例,但可以扩展到任何验证逻辑。
@民用/简易木钩 最简单的钩子——只登录到控制台。了解钩子是如何工作的,并构建自己的钩子,这是一个很好的起点。
@公民/解释钩子 为所有工具添加一个“原因”参数,鼓励人工智能解释它为什么使用每个工具。有助于提高透明度和调试。
@公民/剧作家代币保护代理
Playwright的单个stdio MCP代理,添加了 run_code_cheap 包装和压缩大 browser_run_code 输出到紧凑型 success/failure 结果。
@公民/习俗描述挂钩 替换基于配置的工具描述。有助于提供有关工具在您的环境中所做工作的特定于上下文的信息。
@市政/费率限制挂钩 对每个用户的工具调用执行速率限制。每分钟和每小时的可配置限制,响应后明确重试。
@市政/地方工具挂钩 一种编程挂钩,允许直接添加本地工具以通过MCP服务器,而不需要单独的MCP服务器。非常适合在应用程序代码中定义自定义工具。
测试工具
@civic/fetch文档 一个简单的MCP服务器,它获取网页并将其转换为markdown。作为直通服务器和挂钩的测试目标。
@civil/whoami服务器 与Civic Auth集成以识别经过身份验证的用户的MCP服务器。提供一个“whoami”工具,该工具返回有关当前用户的信息,可用于测试身份验证流。
入门指南
先决条件
- Node.js 18+
- pnpm(推荐)或npm
快速开始
- 克隆并安装:
git clone https://github.com/civicteam/mcp-hooks.git
cd mcp-hooks
pnpm install
pnpm build
- 试试这个简单的例子:
# Terminal 1: Start the fetch-docs MCP server
cd packages/fetch-docs
pnpm start
# Terminal 2: Start the audit hook
cd packages/audit-hook
pnpm start
# Terminal 3: Start the passthrough server
cd packages/passthrough-mcp-server
export TARGET_SERVER_URL="http://localhost:33005"
export HOOKS="http://localhost:33004"
pnpm start
现在,任何连接到端口34000的MCP客户端在转发到获取文档服务器之前,都会被审计挂钩记录所有请求。
包含示例的测试
这 test/ 目录包含准备运行的配置:
cd test
./test.sh simple-log-passthrough.json # See basic logging in action
./test.sh audit-passthrough.json # Test audit trail functionality
./test.sh guardrail-passthrough.json # Test security filtering
./test.sh playwright-tokensaver-proxy.json # Test token-efficient Playwright wrapper
创建自己的钩子
构建自定义钩子很简单:
- 创建新包:
mkdir packages/my-hook
cd packages/my-hook
pnpm init
- 安装依赖项:
pnpm add @civic/hook-common @trpc/server
- 执行你的钩子:
import { AbstractHook, createHookRouter, ToolCallRequestHookResult } from "@civic/hook-common";
import { createHTTPServer } from "@trpc/server/adapters/standalone";
import type { CallToolRequest } from "@modelcontextprotocol/sdk/types.js";
class MyHook extends AbstractHook {
get name(): string {
return "my-hook";
}
async processCallToolRequest(request: CallToolRequest): Promise<ToolCallRequestHookResult> {
// Your logic here
console.log(`Processing: ${toolCall.params.name}`);
// Continue with the request
return {
resultType: "continue",
request: toolCall
};
}
}
// Start the server
const hook = new MyHook();
const router = createHookRouter(hook);
const server = createHTTPServer({ router, createContext: () => ({}) });
server.listen(33007);
- 使用你的钩子:
export HOOKS="http://localhost:33007"
pnpm start
高级配置
多钩
钩子可以链接在一起:
export HOOKS="http://localhost:33004,http://localhost:33005,http://localhost:33006"
它们按请求的顺序执行,按响应的相反顺序执行。
吊钩响应类型
Hooks可以返回三种类型的响应:
- 继续:继续处理(可能已修改)请求
- 中止:停止处理并返回错误
- 回应:返回响应而不调用目标服务器
环境变量
PORT:直通服务器的HTTP端口(默认值:34000)TARGET_SERVER_URL:MCP服务器将请求转发到TARGET_SERVER_TRANSPORT:传输类型(httpStream、sse、stdio)HOOKS:以逗号分隔的挂钩URL列表
建筑细部
技术栈
- TypeScript:所有包装的全类型安全
- 瞬时受体电位通道:钩子和服务器之间的类型安全通信
- fastMCP:高性能MCP服务器实施
- 涡轮仓库:Monorepo构建编排
- 生物群系:快速、现代的剪裁和格式化
- Vitest:单元测试框架
项目结构
mcp-hooks/
├── packages/
│ ├── passthrough-mcp-server/ # Main proxy server
│ ├── hook-common/ # Shared types and utilities
│ ├── audit-hook/ # Example: Logging hook
│ ├── guardrail-hook/ # Example: Security hook
│ ├── simple-log-hook/ # Example: Minimal hook
│ ├── explain-hook/ # Example: Transparency hook
│ ├── playwright-tokensaver-proxy/ # Example: Token-efficient Playwright wrapper
│ ├── custom-description-hook/ # Example: Transform hook
│ ├── rate-limit-hook/ # Example: Rate limiting hook
│ ├── local-tools-hook/ # Example: Programmatic tools hook
│ ├── fetch-docs/ # Test MCP server
│ └── whoami-server/ # Test auth server
├── test/ # Test configurations
└── docs/ # Additional documentation
挂钩接口
所有钩子都实现了这个简单的接口:
interface Hook {
name: string;
processCallToolRequest?(request: CallToolRequest): Promise<ToolCallRequestHookResult>;
processCallToolResult?(response: CallToolResult, originalCallToolRequest: CallToolRequest): Promise<ToolCallResponseHookResult>;
processToolsList?(request: ListToolsRequest): Promise<ListToolsRequestHookResult>;
processListToolsResult?(response: ListToolsResult, originalRequest: ListToolsRequest): Promise<ListToolsResponseHookResult>;
processToolCallTransportError?(error: unknown, originalCallToolRequest: CallToolRequest): Promise<ToolCallTransportErrorHookResult>;
processToolsListTransportError?(error: unknown, originalRequest: ListToolsRequest): Promise<ListToolsTransportErrorHookResult>;
}
贡献
我们欢迎捐款!请查看我们的 贡献指南 了解详情。
开发命令
pnpm install # Install dependencies
pnpm build # Build all packages
pnpm test # Run tests
pnpm lint # Check code style
pnpm dev # Start in watch mode
许可证
麻省理工学院-参见 许可证 了解详情。