可组合mcp节点
一个公开的Node.js HTTP服务器 组合工具 作为MCP(模型上下文协议)端点。工具是通过将操作链接在一起构建的——每个操作都将其结果传递给下一个操作——因此可以组装复杂的工作流,而无需为每个工具编写新的服务器。
______________________________________________________________________
我们正在建造什么
MCP服务器(HTTP传输,无SSE),其中每个“工具”都是一个命名的 流动:接受输入、按顺序运行输入并返回结构化结果的有序操作链。工具存储在外部(当前为Directus),并使用调用者的Bearer令牌按请求获取,因此可以添加或修改新工具,而无需重新部署服务器。
核心理念
POST /mcp/:tool_collation { jsonrpc, method, params }
│
▼
fetch tools for collation (Directus)
│
▼
run operations in chain
[op-1] ──resolve──▶ [op-2] ──resolve──▶ [op-3] ──▶ done
└─reject──▶ [error-handler]
│
▼
return MCP content blocks每个操作都会收到一个共享 上下文 对象,可以读取 $last (先前的结果), $env (冻结环境配置), $trigger (调用者提供的输入),以及由其slug键控的每个较早的结果。成功之后,它 resolve 链接;错误随之而来 reject.
______________________________________________________________________
当前状态
对存在
| 文件 | 状态 | 描述 |
|---|---|---|
src/App.mjs | ✅ 工作 | Express服务器、Directus工具加载器、MCP端点、登录页 |
src/functions/run_operations.mjs | ✅ 工作 | 带循环保护和上下文跟踪的迭代流运行器 |
src/operations/ScriptOperation.mjs | ✅ working | 在沙盒中运行用户JS node:vm 上下文 |
src/operations/FetchRequest.mjs | ✅ 工作 | 具有可配置方法、标头、正文的出站HTTP调用 |
src/operations/CallTool.mjs | ✅ working | 从流中调用另一个Directus支持的工具 |
src/directus/schema.mjs | ✅ 工作 | 确保 tools 和 operations Directus中存在集合 |
src/directus/default_tools.mjs | ✅ working | 文件系统提供的内置工具定义 POST /mcp |
src/directus/permissions.mjs | ✅ working | 创建每个用户对工具/操作的CRUD权限 |
main.mjs | ✅ 工作 | 入口点 |
package.json | ✅ 工作 | 声明的依赖关系(express、ajv) |
工件
- 快速HTTP服务器 —
GET /,GET /health,GET /initialize,POST /initialize,POST /mcp,POST /mcp/:tool_collation - 默认工具端点 —
POST /mcp直接从文件系统为11个内置管理工具提供服务;工具列表没有Directus往返 - Directus加载器 --从Directus集合中获取每个请求的用户定义工具定义
POST /mcp/:tool_collation - 迭代流道 --通过slug执行一系列操作,遵循解析/拒绝链接,防止无限循环(每次操作最多50次访问)
- 脚本操作 --让工具步骤运行任意JS;用户代码导出
async function(data) { ... } - 获取请求 --出站HTTP调用
{{key}}URL、标题和正文中的模板插值;可配置的方法/标头/正文 - 呼叫工具 --从流中调用另一个Directus支持的工具,传播问责制和环境上下文
- MCP工具列表 —
tools/list返回所请求端点的MCP工具描述符 - 输入验证 --根据流验证请求体
inputSchema使用AJV - MCP响应格式 --MCP端点返回
{ content: [{ type: "text", text: "..." }] } - 着陆页 —
GET /提供一个深色主题的HTML页面,其中包含指向Directus管理员的链接、初始化状态徽章和用于运行的令牌表单POST /initialize无需离开浏览器 - 初始化状态检查 —
GET /initialize探测Directus并返回四种状态之一(见下文);Bearer令牌是可选的,省略它将返回{ state: "needed" }立即 - Bootstrap端点 —
POST /initialize在一次调用中创建Directus模式并设置权限;被封锁了409当状态为migration_needed(不支持迁移) - 每个用户权限 --CRUD打开
tools和operations仅限于项目所有者(user_created = $CURRENT_USER) - 单元测试 --153次测试
run_operations,ScriptOperation,FetchRequest,CallTool,default_tools,以及App
______________________________________________________________________
建筑
main.mjs
└─ App.mjs (Express + lifecycle)
├─ fetchToolsForCollation() → fetches user-defined tool definitions per request
├─ GET / → HTML landing page
├─ GET /health
├─ GET /initialize → checkInitializationState() → src/directus/schema.mjs
├─ POST /initialize → checkInitializationState() (blocks 409 if migration_needed)
│ ├─ initializeSchema() → src/directus/schema.mjs
│ └─ setupPermissions() → src/directus/permissions.mjs
├─ POST /mcp → DEFAULT_TOOLS (filesystem) → src/directus/default_tools.mjs
├─ POST /mcp/:tool_collation操作模式(Directus存储的内容)
{
"slug": "get-weather",
"type": "fetch_request",
"config": { "url": "https://api.example.com/weather", "method": "GET" },
"resolve": "format-output",
"reject": "handle-error"
}传递给每个操作的上下文对象
| 关键字 | 类型 | 注释 |
|---|---|---|
$trigger | object | 调用方为工具提供的输入参数 |
$env | 对象(冻结) | 服务器环境配置--包括 DIRECTUS_BASE_URL |
$last | any | 返回前一个操作的值 |
$vars | object | 可变累加器,可通过操作写入 |
[slug] | 任何 | 每个已完成操作的结果,由其滑块键控 |
FetchRequest插值
{{key}} 占位符在操作配置中的所有位置都根据当前上下文进行解析,而不仅仅是URL:
| 配置字段 | 是否插入? | 备注 |
|---|---|---|
url | ✅ | 总是穿线 |
headers.* | ✅ | 每个标头值都是插值的 |
body (字符串) | ✅ | 嵌入式占位符字符串化;精确的 "{{key}}" 返回原始值 |
body (对象) | ✅ | 所有字符串叶值都是递归插值的 |
当整个值是单个占位符时(例如。, "{{$last}}"),原始上下文值按原样返回,保留对象和数组。这使得直接将先前操作的结果作为请求体转发变得容易。
______________________________________________________________________
默认工具(POST /mcp)
11个内置工具直接从服务器的文件系统提供 POST /mcp --他们是 不 存储在Directus中,不需要播种步骤。像其他MCP端点一样调用它们:
# List all default tools
curl -X POST http://localhost:8787/mcp \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'| 工具名称 | 它的作用 |
|---|---|
list_operation_types | 返回此服务器支持的操作类型(run_script, fetch_request, call_tool) |
create_tool | 在Directus中创建新的工具定义 |
add_run_script_operation | 添加a run_script 对现有工具的操作步骤;接受 tool_id, slug, code,可选 resolve/reject |
add_fetch_request_operation | 添加a fetch_request 对现有工具的操作步骤;接受 tool_id, slug, url,可选 method, headers, body, resolve/reject |
edit_tool | 更新现有工具上的字段(稀疏PATCH) |
edit_run_script_operation | 更新a run_script 操作步骤(稀疏PATCH);接受 operation_id 以及任何 slug, code, resolve, reject |
edit_fetch_request_operation | 更新a fetch_request 操作步骤(稀疏PATCH);接受 operation_id 以及任何 slug, url, method, headers, body, resolve, reject |
list_collations | 返回Directus中存储有工具的不同排序规则名称(命名空间) |
list_composed_tools | 返回存储在Directus中用于指定排序规则的工具 |
test_composed_tool | 从Directus获取工具,并使用提供的参数执行它 |
delete_composed_tool | 从Directus中永久删除工具(需要显式 confirm: true) |
添加/编辑操作工具按类型划分,因此每个工具都有专门构建的输入字段,而不是通用的输入字段 type+config 一对。这意味着MCP客户端可以更具体地提示-- code 对于脚本, url/method/headers/body 用于HTTP调用。
获取请求详细信息
当运行fetch_request操作时,服务器使用当前上下文对URL、标头和正文执行插值。 这允许工具根据之前的操作结果或调用者输入动态构造请求。一旦url被解析, 检查是否满足以下条件
- url以Directus基本url开头
- 报头不包括授权报头。
如果满足这两个条件,服务器会自动注入 Authorization Bearer 带有调用者令牌的标头。 这使得对后端的工具调用保持身份验证。
______________________________________________________________________
初始化状态(GET /initialize)
Bearer令牌是可选的。如果省略,端点将返回 { "state": "needed" } 立即联系Directus。提供一个令牌来探测实际状态:
curl http://localhost:8787/initialize \
-H 'Authorization: Bearer '
# → { "state": "needed" | "in_progress" | "migration_needed" | "complete" }
# When state is migration_needed, a details object is also included:
# → { "state": "migration_needed", "details": { "missingToolFields": [...], "missingOpsFields": [...] } }| 状态 | 含义 |
|---|---|
needed | 也不是 tools 也不是 operations 集合已存在--新安装 |
in_progress | 集合存在,但初始化不完整(缺少字段或关系) |
migration_needed | 集合存在,但缺少一个或多个预期字段(应用程序已更新)-- 不支持迁移;使用中的字段名手动解决架构差异 details |
complete | 所有集合、字段和关系都已就绪 |
______________________________________________________________________
登录页面(GET /)
在浏览器中导航到服务器根目录会显示一个设置仪表板:
- 打开Directus管理员↗ --直接链接到
DIRECTUS_BASE_URL/admin/ - 检查状态 --电话
GET /initialize使用输入的令牌并更新徽章;当migration_needed,显示缺少哪些字段的详细信息 - 初始化 --电话
POST /initialize;一旦状态被隐藏complete或migration_needed
______________________________________________________________________
演出
- \[\]工具列表通过授权和排序索引可见且唯一。对(内存中的?文件系统?)缓存列表进行哈希运算,并将复查限制在配置的秒数内
- \[\]早期授权噪音拒绝。将滥用ip暴露给主机进行监视/阻止
- \[\]脚本操作的每个工具超时和资源限制。
开发简易性
- \[x\] 创建一个POST/initialize处理程序,检查directus中所需的集合,并在需要时创建/更新它们
- \[x\] 在POST/mcp上从文件系统提供默认工具(不需要Directus种子)
- \[x\] 工具/操作上CRUD的Directus权限基本上应该“归该用户所有”
- \[x\] GET/initial--返回初始化状态(
complete,in_progress,needed,migration_needed) - \[x\] GET/--带有Directus链接和初始化表单的HTML登录页
______________________________________________________________________
快速开始
npm install
DIRECTUS_BASE_URL=https://your-directus.example.com \
NODE_ENV=development \
PORT=8787 \
npm start# open the landing page in your browser
open http://localhost:8787/
# check initialization state (JSON) — token is optional; omit to get a quick "needed" check
curl http://localhost:8787/initialize \
-H 'Authorization: Bearer '
# health check
curl http://localhost:8787/health
# bootstrap Directus schema + permissions (run once per Directus instance)
# note: default tools are filesystem-side and need no seeding
curl -X POST http://localhost:8787/initialize \
-H 'Authorization: Bearer '
# list default tools (filesystem-side, no Directus lookup)
curl -X POST http://localhost:8787/mcp \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# list tools in a user-defined collation (fetched from Directus)
curl -X POST http://localhost:8787/mcp/my-collation \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# invoke a tool (MCP)
curl -X POST http://localhost:8787/mcp/my-collation \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"my-tool","arguments":{"input":"hello"}}}'
# create a new tool using the default tools at POST /mcp
curl -X POST http://localhost:8787/mcp \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ' \
-d '{ \
"jsonrpc": "2.0", "id": 1, "method": "tools/call", \
"params": { \
"name": "create_tool", \
"arguments": { \
"name": "my-new-tool", \
"title": "My New Tool", \
"tool_collation": "my-collation", \
"start_slug": "step-1" \
} \
} \
}'
# add a run_script operation step to a tool (tool_id from create_tool response)
curl -X POST http://localhost:8787/mcp \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ' \
-d '{ \
"jsonrpc": "2.0", "id": 2, "method": "tools/call", \
"params": { \
"name": "add_run_script_operation", \
"arguments": { \
"tool_id": 1, \
"slug": "step-1", \
"code": "module.exports = async function(data) { return { hello: data.$trigger.name }; };" \
} \
} \
}'
# add a fetch_request operation step to a tool
curl -X POST http://localhost:8787/mcp \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ' \
-d '{ \
"jsonrpc": "2.0", "id": 3, "method": "tools/call", \
"params": { \
"name": "add_fetch_request_operation", \
"arguments": { \
"tool_id": 1, \
"slug": "step-1", \
"url": "https://api.example.com/items/{{$trigger.id}}", \
"method": "GET", \
"headers": { "Authorization": "Bearer {{$env.DIRECTUS_TOKEN}}" } \
} \
} \
}'______________________________________________________________________
测试
npm test贡献
该项目正处于早期开发阶段。现在最好的帮助方式是从路线图中选择一个项目,打开一个PR,并为您添加的代码至少包含一个测试。
