MCP中间件层

Civic MCP Hooks是一个为模型上下文协议(MCP)设计的中间件层,用于监控、验证和转换AI工具交互,提供安全控制、审计日志和定制化功能。

  • by civicteam
  • TypeScript
  • 本地部署
  • 9 星标
  • 更新于
在 GitHub 查看 civicteam/mcp-tools

使用说明

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

直通服务器:

  1. 截距 来自AI的所有请求
  2. 过程 他们穿过一串钩子
  3. 前进 对实际工具的批准请求
  4. 退货 响应(也通过钩子处理)

链条上的每个钩子都可以:

  • 检查 请求(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

快速开始

  1. 克隆并安装:
git clone https://github.com/civicteam/mcp-hooks.git
cd mcp-hooks
pnpm install
pnpm build
  1. 试试这个简单的例子:
# 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

创建自己的钩子

构建自定义钩子很简单:

  1. 创建新包:
mkdir packages/my-hook
cd packages/my-hook
pnpm init
  1. 安装依赖项:
pnpm add @civic/hook-common @trpc/server
  1. 执行你的钩子:
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);
  1. 使用你的钩子:
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

许可证

麻省理工学院-参见 许可证 了解详情。