mcp-longjobs
mcp-longjobs
Долговечные, возобновляемые операции для MCP — длительные задачи и большие файлы, которые переживают тайм-ауты, разрывы соединений и перезапуски клиентов. На любом клиенте — уже сегодня.
Проблема
Есть три вещи, из-за которых ломается любой MCP-сервер, выполняющий реальную работу:
Длительные вызовы инструментов упираются в тайм-аут. Клиенты устанавливают тайм-ауты на каждый вызов (часто 10–60 с). Падает обход сайта, сборка или пакетное задание — и «повторная попытка» модели запускает всю операцию заново.
Сбои не поддаются исправлению. Неудачный вызов возвращает произвольную ошибку, поэтому модель гадает: слепо повторить или сдаться. Она не может исправить один параметр и продолжить.
Для больших файлов нет механизма передачи. Бинарное содержимое — это base64 в JSON (33% накладных расходов, жёсткие ограничения размера сообщения) или голая ссылка без каких-либо соглашений — ни разбиения на части, ни возобновления, ни проверки целостности.
Спецификация MCP от 2026-07-28 добавила Tasks — асинхронное выполнение с вводом на лету и долговечными дескрипторами. Но ни один клиент его пока не поддерживает, а спецификация требует от серверов отклонять задачи для клиентов, которые не дали согласие. Поэтому каждому долго работающему серверу нужен запасной путь, который работает на сегодняшних клиентах. Это и есть этот пакет.
Related MCP server: Simple Streamable HTTP MCP Server
Что вы получаете
import { z } from "zod";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { JsonFileSessionStore, withTasks, withFileTransfer, asToolRegistrar } from "mcp-longjobs";
const mcp = new McpServer({ name: "my-server", version: "1.0.0" });
const registrar = asToolRegistrar(mcp);
const store = new JsonFileSessionStore("./state/sessions.json");
const tasks = withTasks(registrar, { store });
tasks.taskTool("crawl-site", {
description: "Crawl a site and produce a report (takes minutes)",
inputSchema: { url: z.string(), maxPages: z.number().default(50) },
}, async (args, ctx) => {
for (const page of pages) {
if (ctx.signal.aborted) throw new Error("cancelled");
await ctx.progress(`Crawled ${page.url}`, done / total);
if (needsConfirmation(page)) {
const answer = await ctx.needInput({ prompt: `Include ${page.url}?`, choices: ["yes", "no"] });
if (answer === "no") continue;
}
}
return { summary, reportPath }; // small result for the model; big artifacts go through file transfer
});
withFileTransfer(registrar, { store, storageDir: "./state/blobs" });Что модель получает на сегодняшних клиентах (поддержка Tasks не требуется):
crawl-siteвозвращает результат мгновенно вместе сtaskIdи инструкциями опрашиватьdurable_task_get— никаких тайм-аутов больше.Опрос показывает прогресс в реальном времени:
{ "status": "working", "progress": { "message": "Crawled /pricing", "fraction": 0.4 } }.Вопросы на лету приостанавливают задачу с состоянием
input_required; модель отвечает черезdurable_task_respond, и задача продолжается с того места, где остановилась.Клиент упал? Новая сессия?
durable_task_getс тем жеtaskIdпо-прежнему работает — состояние хранится в хранилище, а не в соединении.durable_task_cancelкооперативно прерывает работу на следующей контрольной точке.
Сбои — это данные, а не ошибки протокола — структурированная обёртка, которую модель может исправить за один цикл запроса-ответа:
{
"status": "failed",
"error": {
"code": "offset_mismatch",
"message": "Expected offset 131072, got 0.",
"retryable": true,
"recoveryHint": "Do NOT resend the whole file. Re-send this chunk starting at offset 131072.",
"partial": { "cursor": 131072 }
}
}Пакеты (экспорт через подпути)
Import | Purpose |
|
|
|
|
| Модель сессии, подключаемые хранилища (память, JSON-файл), структурированная обёртка ошибок |
Заметки по дизайну
Байты никогда не проходят через модель. Модель видит только метаданные: дескриптор, размер, sha256, прогресс. Части через вызовы инструментов предназначены для полезной нагрузки малого и среднего размера; большие файлы должны передаваться в обход основного канала (планируется конечная точка TUS) с проверкой целостности моделью.
Модель — режиссёр, а не курьер. Результаты фасадных инструментов содержат собственные инструкции («вызовите
durable_task_getс этим id», «продолжите со смещения N»), поэтому любая способная модель может вести протокол без какой-либо поддержки на стороне хоста.Сбои — это исправимые данные. Каждый сбой содержит
code,retryable,recoveryHintиpartial.cursor— что пошло не так, может ли сработать повтор, что сделать вместо этого и что уже получилось.Словарь жизненного цикла соответствует спецификации.
working / input_required / completed / failed / cancelled, чтобы нативный адаптер можно было подключить позже без ломающих изменений.
Статус
Компонент | Статус |
Запасной фасад Tasks (прогресс / ввод / отмена) | ✅ реализовано |
Долговечные хранилища сессий (память, JSON-файл) | ✅ реализовано |
Передача файлов частями с возобновлением и контрольными суммами | ✅ реализовано |
Нативный адаптер ext-tasks ( | 🔜 следит за экспериментальным Tasks API в SDK |
Внеполосная конечная точка TUS 1.0 для больших файлов | 🔜 запланировано — см. mcp#189 |
Хранилища Redis / SQLite, порт на Python | 🔜 запланировано |
Быстрый старт
git clone https://github.com/ljppanda/mcp-longjobs
cd mcp-longjobs
npm install && npm run build
node dist/examples/report-generator.js(После публикации в npm тот же сервер запускается одной командой: npx mcp-longjobs.)
Подключите к нему свой клиент (stdio):
{
"mcpServers": {
"report-generator": {
"command": "node",
"args": ["/absolute/path/to/mcp-longjobs/dist/examples/report-generator.js"]
}
}
}Затем попросите: «Сгенеруй отчёт об аккумуляторах для электромобилей из 3 разделов». Наблюдайте, как модель запускает задание, опрашивает durable_task_get и забирает результат. Убейте клиент на середине выполнения, перезапустите его и попросите тот же taskId — он продолжит работу.
Разработка
npm install
npm test # vitest
npm run build # tsc -> dist/
npm run example # build + run the demo serverУчастие
PR приветствуются — особенно: бэкенды хранилищ (SQLite/Redis), нативный адаптер ext-tasks и конечная точка TUS. Для чего-то более крупного, пожалуйста, сначала откройте issue.
Лицензия
Maintenance
Related MCP Servers
- AlicenseAqualityBmaintenanceAsync MCP server for running long-running AI tasks with real-time progress monitoring, enabling users to start, monitor, and manage complex AI workflows across multiple models.6345MIT
- FlicenseNot gradedqualityDmaintenanceA reference implementation demonstrating proper MCP server patterns with HTTP transport, featuring session management, progress notifications, and example tools for testing server functionality. Serves as a clean template for building MCP servers with streamable responses and comprehensive error handling.7
- AlicenseAqualityAmaintenanceA fire-and-poll MCP server that lets Claude Code run long background jobs without hitting tool-call timeouts.3MIT
- FlicenseNot gradedqualityBmaintenanceRemote MCP server that launches user-supplied scripts inside disposable Docker containers, returning task IDs for async tracking and bounded output tails.
Related MCP Connectors
MCP server for the FFmpeg Micro video transcoding API — create, monitor, download transcodes.
MCP protocol requiring task acceptance and provenance tags. Self-hosted only - see README.
Remote MCP server for RunComfy Serverless API (ComfyUI): deployments and async inference.
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/ljppanda/mcp-longjobs'
If you have feedback or need assistance with the MCP directory API, please join our Discord server