jsmcp
jsmcp 适用于代理需要执行多个MCP工具调用的情况。
大多数MCP客户端在一次工具调用中表现出色,但在工作需要时却很尴尬:
- 几个相关的工具调用
- 基于早期结果的分支逻辑
- 循环、重试或结果聚合
- 在下次调用之前转换工具输出
jsmcp 通过将批准的MCP工具作为JavaScript名称空间公开来解决这个问题。它可以发现可用的工具,然后编写少量的JavaScript以编程方式使用这些工具,而不是强迫模型处理许多单独的工具调用。
在实践中,这意味着:
- 代理首先了解哪些服务器和工具可用,同时
jsmcp限制对预设中允许的任何服务器和工具的访问 - 然后,代理可以为多步工作编写JavaScript
- 日志与返回值分开,因此代码更容易推理
从读取配置 $XDG_CONFIG_HOME/jsmcp/ 或者,如果 XDG_CONFIG_HOME 未设置, ~/.config/jsmcp/.正是其中之一 config.json, config.yaml,或 config.yml 必须存在于那里。
为什么
使用 jsmcp 当您希望代理将MCP工具处理得更像一个小型可编程API表面,而不是一系列孤立的按钮按下时。
当代理需要:
- 结合多个MCP工具的结果
- 跨一个或多个MCP服务器的脚本工作流
- 在代码中做出决策,而不是在工具调用之间反复重新规划
- 将工具访问限制在已审核的预设范围内
安装
npm install -g @alesya_h/jsmcp或者在不全局安装的情况下运行它:
npx @alesya_h/jsmcp run跑
jsmcp run
jsmcp run work
jsmcp server work --port 3000 --bind 0.0.0.0
jsmcp client --profile work --host 127.0.0.1 --port 3000
jsmcp client --profile work --port 3000 --session-id my-agent-session
jsmcp status --profile work --host 127.0.0.1 --port 3000
jsmcp status kagi --tools --profile work --port 3000
jsmcp auth
jsmcp auth firefox_devtools如果您从源代码签出而不是已安装的软件包运行,请替换 jsmcp 随着 node src/index.js例如 node src/index.js run.
run 直接通过stdio启动元MCP服务器。
server 在上启动一个长期守护进程 ws://: /mcp,启动每个全局启用的MCP服务器一次,并保持这些底层连接的温暖。所选预设将成为不请求连接的默认配置文件。它绑定到 0.0.0.0 默认情况下,接受 --bind 选择另一个绑定地址。
client 公开一个stdio MCP服务器,该服务器将原始MCP/JSON-RPC消息代理到 server 通过WebSocket。它接受 --host 和 --port 要选择要连接的守护进程,可以选择传递 --profile 选择该守护进程侧配置文件,并接受 --session-id 在客户端重新连接时重用相同的守护进程端日志会话。
status 通过HTTP连接到正在运行的守护进程,并在所选配置文件中打印配置的服务器及其启动状态或启动错误。传递服务器名称以仅显示该服务器,然后传递 --tools 包括每个健康服务器允许的工具和描述。它接受 --host , --port ,以及 --profile .
run, server,以及 client 所有选项都接受可选的预设作为位置参数或 --profile 。默认守护程序端口为 41528.如果 client --session-id 如果省略,客户端将生成一个随机会话id,并在该客户端进程中重用它进行重新连接。
首先 server 开始, jsmcp 在处创建API密钥 $XDG_CONFIG_HOME/jsmcp/api-key.txt,或 ~/.config/jsmcp/api-key.txt 如果 XDG_CONFIG_HOME 未设置。守护进程WebSocket和HTTP API请求必须将其包含在 X-JSMCP-API-Key 头球收到未经身份验证的请求 401.
该守护进程还通过一个JSON HTTP端点公开了五个元工具:
POST /api/call?tool=list_servers&profile=
POST /api/call?tool=list_tools&profile=
POST /api/call?tool=execute_code&sessionId=&profile=
POST /api/call?tool=fetch_logs&sessionId=
POST /api/call?tool=clear_logs&sessionId=请求体是一个与所选MCP工具参数匹配的JSON对象。HTTP调用者可能包括 sessionId 在查询字符串中使用稳定的守护进程端日志会话。它们可能包括 profile 选择哪个配置文件过滤该请求的服务器和工具视图。
使用 jsmcp auth 管理远程服务器的OAuth。没有参数,它列出了启用OAuth的远程服务器。使用服务器名称,它将启动该服务器的OAuth流。
如果未检测到图形环境,或者您通过了 --no-browser, jsmcp auth 打印授权URL并等待localhost回调或粘贴的回调URL/代码。
systemd用户服务
此回购包括 systemd/jsmcp.service,启动的用户单元 jsmcp server 从全局安装的CLI。
安装时使用:
npm install -g .
mkdir -p ~/.config/systemd/user
ln -sfn "$PWD/systemd/jsmcp.service" ~/.config/systemd/user/jsmcp.service
systemctl --user daemon-reload
systemctl --user enable --now jsmcp.service有用的命令:
systemctl --user status jsmcp.service
journalctl --user -u jsmcp.service -f
systemctl --user restart jsmcp.service签入单元启动默认守护程序端口上的默认预设,并解析 jsmcp 通过用户的实际登录shell getent passwd.
配置
配置文件可以是JSON或YAML,并使用以下顶级键:
servers:服务器定义jsmcp:可选的jsmcp特定设置presets:服务器和工具向代理公开的可选覆盖
服务器名称必须是有效的JavaScript标识符,因为 execute_code() 将它们直接暴露为全局变量。
jsmcp 对于公共字段,接受OpenCode MCP配置样式和重叠的Claude Code MCP样式:
- 本地服务器:
type: "local"或type: "stdio" - 远程服务器:
type: "remote",type: "http",或type: "sse" - 命令:要么
command: ["cmd", "arg1"]或command: "cmd"随着args: ["arg1"] - 环境变量:要么
environment或env
支持 servers. 领域:
type:必填;之一local,stdio,remote,http,ssedescription:中显示的可选字符串list_servers()enabled:可选布尔值;默认为truetimeout:用于初始工具发现的可选数字(毫秒)strip_tool_prefix:可选字符串,true,或false;从公开的工具名称中删除字符串,true推断共享前缀,以及false禁用该服务器的前缀剥离normalize_tool_names:可选布尔值;将公开的工具名称转换为snake_case去掉前缀后blocked_tools:可选的服务器级拒绝列表;工具名称字符串或精确工具名称数组,{ glob: "..." },以及{ regex: "..." }选择器。选择器在前缀剥离和规范化后与最终暴露的工具名称匹配,被阻止的工具不能通过预设重新启用。
支持 jsmcp 领域:
auto_strip_tool_prefixes:可选布尔值;默认false;如果true,服务器推断并删除共享工具名称前缀,除非被覆盖servers..strip_tool_prefixnormalize_tool_names:可选布尔值;默认false;如果true,服务器将工具名称公开为snake_case除非被覆盖servers..normalize_tool_names
对于本地/stdio服务器:
command:必填;非空字符串或非空数组args:可选数组;附加到command当command是字符串,并且在以下情况下也被接受command是一个数组env:环境变量的可选对象environment:环境变量的可选对象;合并env,并在重复密钥上获胜cwd:可选工作目录
对于远程/HTTP/SSE服务器:
url:必填字符串headers:请求标头的可选对象oauth:可选OAuth配置
支持 oauth 形式:
- 省略,
null,或true:使用默认行为启用OAuth false:禁用该服务器的OAuth- 对象具有以下任何一项:
- clientId - clientSecret - scope
字符串字段中支持的值替换:
{env:NAME}:从当前环境扩展${NAME}:Claude代码风格环境扩展${NAME:-default}:带回退的Claude代码风格扩展{file:path}:替换为文件内容
对于 {file:path}:
- 相对路径是相对于配置文件目录解析的
~/...从用户主目录解析- 绝对路径按原样使用
如果 presets 省略,默认预设包括每个服务器 enabled !== false 并允许使用该服务器的所有工具。
如果 presets 如果存在,它是一个预设名称的对象。每个预设都是分层在服务器定义之上的每服务器覆盖的对象:
presets.default:默认预设的可选覆盖- 任何其他预设名称,例如
presets.work:其他命名预设替代
如果服务器删除前缀或规范化名称,则预设的工具选择器将与代理看到的最终公开的工具名称相匹配。
服务器级别 blocked_tools 选择器在预设的分配列表之前应用。将它们用于全局不安全的工具,并将预设用于特定于配置文件的白名单。
在预设中,服务器规则的工作方式如下:
- 省略服务器规则:按原样使用服务器定义
true:包括该服务器并允许其所有工具false:从该预设中排除该服务器"tool_name":只包括那个确切的工具- 数组条目可以是:
- 精确的工具名称字符串 - { "regex": "..." } 选择器 - { "glob": "..." } 选择器
如果服务器具有 enabled: false 在 servers,它是全局禁用的,不会由任何预设启动或暴露。
例子:
{
"servers": {
"math": {
"type": "stdio",
"description": "Basic arithmetic tools",
"command": "node",
"args": ["/absolute/path/to/math-server.js"],
"env": {
"LOG_LEVEL": "debug"
},
"cwd": "${PWD}"
},
"docs": {
"type": "http",
"description": "Documentation search and retrieval",
"url": "https://example.com/mcp",
"headers": {
"Authorization": "Bearer ${DOCS_TOKEN}"
},
"oauth": {
"scope": "docs.read"
}
}
},
"presets": {
"default": {
"math": ["add", { "glob": "mul_*" }],
"docs": [{ "regex": "(search|fetch)" }]
},
"work": {
"docs": true
}
}
}兼容性说明:
- Claude代码风格
env,type: "stdio",type: "http",type: "sse",以及command加args得到支持 - OpenCode样式
type: "local",type: "remote"、命令数组,以及environment也支持 - Claude Code的特定功能,如
headersHelper以及高级OAuth字段,如callbackPort或authServerMetadataUrl尚不支持
OAuth令牌和注册状态存储在 $XDG_DATA_HOME/jsmcp/oauth.json 或 ~/.local/share/jsmcp/oauth.json.
外露工具
list_serverslist_toolsexecute_codefetch_logsclear_logs
行为
- 每台服务器
enabled !== false在以下情况下启动一次jsmcp开始 list_servers()是代理了解可用功能所需的第一步- 你必须打电话
list_tools(server)在使用服务器之前execute_code()因此,您知道确切的工具名称、别名和模式 list_servers()和list_tools(server)仅返回连接所选配置文件中允许的服务器和工具execute_code({ code, data?, timeoutMs? })不管理服务器生命周期;它只能使用已启动的服务器- 更喜欢
execute_code({ code, ... })每当工作需要不止一次工具调用时 console.log,console.info,console.warn,以及console.error里面execute_code()存储用于fetch_logs()fetch_logs()读取时耗尽日志缓冲区
execute_code
execute_code 将JavaScript作为异步函数的主体运行。
启动的服务器作为全局变量注入。每个允许的MCP工具都成为该服务器对象上的一个函数。如果可用,最好使用下划线别名。
如果你通过 data,它作为全局变量暴露给脚本 data这对于需要在代码字符串中转义的字符串或结构化值非常有用。
你应该打个电话 list_tools(server) 在使用服务器之前 execute_code()对于多步骤的工作,更喜欢编写JavaScript,而不是试图在精神上链接几个工具调用。
例子:
return await math.add({ a: 2, b: 5 });数据:
return data.message;如果MCP工具返回 structuredContent,这就是JavaScript调用的解析结果。因此,上面的示例可以返回:
{
"sum": 7
}如果工具名称不是有效的JavaScript标识符,请使用其下划线别名:
return await math.tool_name({ value: 1 });原始工具名称仍然适用于括号访问:
return await math["tool-name"]({ value: 1 });