simple-job
mcp-simple-job
将子任务交给另一台机器上的本地模型,并在返回结果之前验证结果。
它是为解决一种特定形态的问题而构建的:能力强大、按量计费的助手运行在你面前的机器上,而一台完全够用的 GPU 机器却在角落里闲置。读取四十个文件、总结一个长页面、拉取一个数据集——这些都不需要昂贵的模型,而在助手的上下文中完成它们会消耗真正稀缺的那一种资源。
在作者的配置上实测,与在上下文中完成同样的四项任务相比:~2.7 倍更快,上下文消耗减少 6.1 倍,因为原始页面从未进入上下文。
唯一规则
任务必须附带检查。 无法说明如何知道任务是否成功的任务会被拒绝——不是警告,而是拒绝。
这不是对本地模型的不信任,而是调用方无法判断。当助手委派一个摘要任务并收到四百个自信满满的词时,它没有独立的方法知道这些词描述的是文档还是别的东西。没有检查就委派,等于又增加了一处绿灯毫无意义的地方。
检查刻意保持朴素:nonempty、contains、regex、json_keys、line_count、shell(你的 argv,退出码 0 即通过,输出通过管道送入 stdin),以及 summary_of(比源文本短,且不是源文本的副本)。
Related MCP server: any-model-plugin
四个子任务
工具 | 作用 | 工作发生的位置 |
| 一个普通提示词,带你自己定义的检查 | 模型主机 |
| 文本,或先抓取的 url/urls | 工作机(抓取 + 模型) |
| 搜索,阅读排名靠前的页面,总结 | 搜索在主机间交替,其余在工作机 |
| 抓取文件,报告字节数和 sha256 | 默认在工作机,也可在任何配置的主机 |
simple_job_stats 报告所有这些任务实际进行得如何,数据来自为每个任务写入的一行账本记录。
运行它
要求
运行 MCP 服务器的机器上需要 Node 18+。
一个兼容 OpenAI 的聊天端点。 llama.cpp 的
llama-server、Ollama、vLLM、LM Studio,或托管的 API——任何能响应POST /v1/chat/completions的服务。Python 3.9+,在负责抓取的机器上。仅用标准库;无需 pip 安装,无需 node,无需 beautifulsoup。
SSH 密钥认证,如果要使用第二台机器的话。这是可选的(见下文)。
在作者的机器上是如何连接的
两台计算机:
一台 Mac mini——日常工作站。16 GB 内存,通常有数 GB 的交换空间。它运行 Claude Desktop,因此也运行这个 MCP 服务器。
一台装有 RTX 5070 Ti 的 Pop!_OS 机器——实验室。它在 8080 端口上通过
llama-server运行一个名为 ornith 的 35B 模型,一天中大部分时间都处于空闲状态。
一条 SSH 隧道让远程模型在 Mac 上看起来像本地模型:
ssh -N -o ServerAliveInterval=30 -L 127.0.0.1:8081:127.0.0.1:8080 pop-ospop-os 是 ~/.ssh/config 中的一个别名,带有密钥和 IdentitiesOnly yes,因此服务器可以通过 BatchMode=yes 以非交互方式访问它。
MCP 客户端条目就是:
{ "mcpServers": { "simple-job": { "command": "node", "args": ["/path/to/mcp-simple-job/index.js"] } } }其余由默认值完成:模型在 127.0.0.1:8081,工作机在 pop-os,账本在已有的 ~/Code/harness/ 目录中(如果存在的话)。
没有任何东西需要手动调用。助手选择工具——这比听起来更难,下文会介绍。
在你自己的环境上运行
两台机器,一台运行客户端,一台运行模型:
{
"mcpServers": {
"simple-job": {
"command": "node",
"args": ["/path/to/mcp-simple-job/index.js"],
"env": {
"ORNITH_URL": "http://127.0.0.1:8081/v1/chat/completions",
"ORNITH_MODEL": "your-model-name",
"POP_HOST": "your-ssh-alias"
}
}
}
}一台机器——全部本地,没有任何 SSH:
{
"env": {
"ORNITH_URL": "http://127.0.0.1:11434/v1/chat/completions",
"ORNITH_MODEL": "qwen3:8b",
"SEARCH_HOSTS": "mac"
}
}SEARCH_HOSTS=mac 这个开关表示“没有第二台机器”。抓取、下载和搜索都在本地进行,搜索只有一个速率预算而不是两个。其他一切行为相同。
环境变量
变量 | 默认值 | 作用 |
|
| 聊天端点 |
|
| 请求中发送的模型名称 |
|
| 工作机的 SSH 别名 |
|
| 要在哪些机器之间交替搜索;单机安装时设为 |
|
| 同一台机器两次搜索之间的最小间隔 |
|
| 一台机器被限流后暂停多长时间 |
| 位于 | 搜索计时状态的存放位置 |
|
| SQLite 账本 |
|
| 可选,要盖在行上的跟踪 id |
账本是可选的。 它会在首次使用时创建,如果无法写入,任务仍然会运行——日志记录是尽力而为的,绝不会阻塞工作。HARNESS_TRACE 是作者自己的跟踪设置的一个钩子;忽略它,行就只是有一个空的 trace id。
让它真正被用起来
这是大多数人跳过的一部分,也是决定上述一切是否重要的部分。
一个没有任何东西会路由到它的工具,无论它工作得多好都是不可见的。作者有一个独立的 MCP 服务器,它工作得完美,但几个月里零调用,纯粹是因为从来没有任何东西告诉助手去使用它。构建一个能力与将请求路由到它是两件不同的事,而完成第一件会让人感觉已经完成了。
有三种方法可以缩小这个差距,从最便宜的开始。大多数人想要第二种。
1. 什么都不做,先看看。 有些客户端对工具描述的理解足够好,一个足够明显的请求——比如“总结这四十页”——会自动找到这个工具。在添加机制之前,值得先试一天。观察它是否真的被调用。
2. 把一条规则放在你的客户端保存常驻指令的地方。 Claude Desktop 项目指令、Claude Code 的 CLAUDE.md、.cursorrules、自定义 GPT 的指令——无论你的客户端在每一轮都会读取什么。类似这样:
有一个本地模型可以通过
simple-job使用。当材料尚未在上下文中且任务是机械性的时使用它:总结页面或文件、网络搜索加阅读、抓取下载、提取和重新格式化。它是免费的,不会在源材料上消耗上下文。当文本已经在上下文中、当任务需要解释而不是转写、或者当“正确”比“可检查”更重要时,自己做。绝不要委派需要判断的决策、必须正确的代码或文件编辑。
每个任务都必须携带检查——服务器会拒绝它无法验证的工作。
在何时不委派上花与“何时委派”同样多的篇幅。路由委派工具的失败模式是过度委派,而一个把所有事情都往下游送的助手,会在你想要判断的地方给你忠实的转写。
3. 如果你有路由器,就把它接入路由器。 如果你的设置已经将情境匹配到工具,就为“批量阅读或抓取尚未进入上下文的材料”添加一个条目。相对于常驻指令的优势在于它是可衡量的——你可以统计它在应该触发时是否触发了。常驻指令要么有效要么无效,而且没有任何东西记录是哪一种。
不要给它发送什么
任何“看起来对”是唯一检验标准的东西。需要判断的决策。必须正确的代码。编辑文件。
还有一条通过测量而非品味发现的边界:小型本地模型能忠实转写,但不能解释。在测试中,它逐字复述了源文本中含糊的措辞,而不是解决其含义,并且把仓库的星标数总结得像是一个 bug 报告的一部分。把转写交给它。把解释留给自己。
构建过程中的笔记
下面的一切都是测量结果,而不是观点。这些数字也写在代码注释里。
思考默认关闭
推理模型从同一个 token 预算中产生它们的思考和答案。在一个总结任务上完全相同地运行三次,三次中有两次花费了 5,500–6,000 个字符思考,撞到上限,并以 HTTP 200 返回了空答案。
提高 max_tokens 没有修复它。reasoning_effort: "low" 没有修复它。一个 `` system 标签也没有修复它。只有 chat_template_kwargs: {enable_thinking: false} 修复了它,同一个任务随后用 258 个 token 就回答了。对于真正需要深思的任务,传入 think: true,并同时提高 max_tokens。
搜索是配给的,而 DuckDuckGo 在原因上撒谎
DuckDuckGo 不会礼貌地限流:
被服务的查询是 HTTP 200,约 28 KB,十个结果链接
被拒绝的查询是 HTTP 202,约 14.2 KB,其文本写着 “请完成以下挑战... 选择所有包含鸭子的方块”
这是 IP 上的一个验证码标记,而不是时间限制,而且拉开间隔并不能清除它。在静默四分钟后,从一台机器以 30 秒间隔发六个查询、从另一台以 15 秒间隔发六个查询,结果是 0 对 12。在被封锁期间每 5 秒轮询一次,162 秒内从未恢复——重试反而会喂养它。这个标记大约二十分钟后自行消退。
因此,搜索被拉开间隔,在机器之间交替(两个 IP 就是两个预算),并且限流被报告为 throttled,绝不会报告为“无结果”。这两者意思相反。
阅读跟随问题
一个长页面会被截断以适配模型的窗口,而从顶部截断会静默地回答错误的问题。在一个 40,063 字符的页面、8,000 字符的窗口下被问到 “稀疏门控和负载均衡” 时,第一个版本返回了文章开头的流畅总结——其中“负载均衡”从未出现(它从第 16,181 个字符开始),“稀疏”也从未出现(14,671)。
因此 focus 引导窗口:先取开头作为上下文,然后取每个命名术语周围的段落,在每个术语获得第二个窗口之前,保证每个术语有一个窗口。两个早期版本都不够好——子串匹配在“download”中找到了“load”,报告了 42 次噪声命中;而按文档顺序取段落则在到达第 16,181 个字符之前就用完了预算。
如果页面从未使用这些词,调用会返回 ok:false 和 focus_not_found。零命中是比一段关于其他材料的貌似合理的总结更好的答案。
一个大部分是脚本的页面会被拒绝
一个网站提供了 68,896 字节,其中只包含 40 个字符的文本(“Loading...”),这通过了原来的 if not text 守卫——于是这个页面外壳作为源材料进入总结,模型写出了一个自信的基准数字并引用它。
现在有两个测试,因为单独任何一个都会被欺骗:一个绝对下限,以及一个文本与字节的比率,它只会谴责一个同样短的页面。一个 GitHub issue 是 290,000 字节的标记,围绕 3,896 个字符的真实讨论,仅凭比率就把它丢弃了。
引用被编号,以便可以核查
search_and_summarize 对页面进行编号,并要求使用 [1]、[2] 而不是 URL。当被要求提供 URL 时,模型把它在博客中读到的一个数字归因于一个文档页面——事实是真实的并且存在于材料中,但归因不是,而 summary_of 无法看到这一点,因为一个贴错标签的项目符号长度正确,并且不是复制品。
URL 是一长串不透明的字符串,难以正确复制。整数则不是,并且可以对照实际读取的页面进行范围检查——代码正是这样做的,对超出范围的数字使调用失败,并统计完全没有来源的项目符号。
哪台机器更快?
通过两端的相同 sha256 验证:
客户端机器 | 工作机机器 | |
ssh 往返 | — | ~185 ms 每次调用 |
获取 4 个页面 | ~1.7 s | ~2.0 s |
下载 20 MB | 17.5 MB/s | 12.6 MB/s |
客户端机器在两项上都更快。 速度不是把工作发送给工作机的原因。原因是它持有模型,它在另一台机器被使用时处于空闲状态,而且第二台机器是第二个搜索预算。根据文件需要的位置来选择下载的主机,而不是根据吞吐量。
测试
node test_e2e.mjs # 24 assertions, spawns the real server over JSON-RPC
node --test test/simple-job.test.mjs # 19 unit assertions on the checks端到端套件以客户端的方式启动实际的服务器,针对一个一次性的账本。进程内测试无法捕获 PATH 或环境变量错误,而这些正是只有在重启后才会出现的错误。
对 index.js 的更改在客户端下次启动服务器时生效。pop_agent.py 在每次调用时都会被重新读取,因此对获取、搜索和下载的更改会立即生效。
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- FlicenseAqualityCmaintenanceLocal MCP server that enables delegating low-risk tasks like summarization or code patches to a low-cost model, with the main agent reviewing results.2
- AlicenseNot gradedqualityAmaintenanceEnables AI agents to delegate tasks, run adversarial reviews, and manage background jobs across multiple models and providers via anymodel_* tools.Apache 2.0
- AlicenseNot gradedqualityCmaintenanceEnables Claude Code to delegate mechanical tasks (summaries, boilerplate, reformatting) to local models running in LM Studio.1MIT
- AlicenseNot gradedqualityAmaintenanceDelegates replaceable grunt work (boilerplate, formatting, translation, long-document summarizing) from a premium agent to cheap models behind a local LiteLLM proxy, auto-routing each task by type. Delegated calls run in a separate process, so the subscription session and the API credentials never share an environment.MIT
Related MCP Connectors
Verifies AI agent work end to end: real artifacts and outcomes checked, not self-reported success.
LLM chat, text summarization and AI image generation
Free OpenAI-compatible inference with signed provenance receipts and 3 focused MCP tools.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/MikeyBeez/mcp-simple-job'
If you have feedback or need assistance with the MCP directory API, please join our Discord server