mcp-longjobs
mcp-longjobs
Durable, resumable operations for MCP — long-running tasks and large files that survive timeouts, disconnects, and client restarts. On every client, today.
The problem
Three things break every MCP server that does real work:
Long-running tool calls time out. Clients impose per-call timeouts (often 10–60s). A crawl, a build, a batch job fails — and the model's "retry" restarts the whole operation from scratch.
Failures are unrepairable. A failed call returns a freeform error, so the model guesses: retry blindly, or give up. It can't fix one parameter and resume.
Large files have no transfer story. Binary content is base64-in-JSON (33% overhead, hard message-size caps) or a bare URL with zero conventions — no chunking, no resume, no integrity checks.
The 2026-07-28 MCP spec added Tasks — async execution with mid-flight input and durable handles. But no client supports it yet, and the spec requires servers to refuse tasks for clients that didn't opt in. Every long-running server therefore needs a fallback path that works on today's clients. That is this package.
Related MCP server: Simple Streamable HTTP MCP Server
What you get
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" });What the model experiences on today's clients (no Tasks support required):
crawl-sitereturns instantly with ataskIdand instructions to polldurable_task_get— no more timeouts.Polls show live progress:
{ "status": "working", "progress": { "message": "Crawled /pricing", "fraction": 0.4 } }.Mid-flight questions pause the task as
input_required; the model answers viadurable_task_respondand the task continues where it stopped.Client crash? New session?
durable_task_getwith the sametaskIdstill works — state lives in the store, not in the connection.durable_task_cancelaborts the work cooperatively at its next checkpoint.
Failures are data, not protocol errors — a structured envelope the model can repair in one round-trip:
{
"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 }
}
}Packages (subpath exports)
Import | Purpose |
|
|
|
|
| Session model, pluggable stores (memory, JSON file), structured error envelope |
Design notes
Bytes never flow through the model. The model sees metadata only: handle, size, sha256, progress. Chunks through tool calls are for small-to-medium payloads; large files should move out-of-band (TUS endpoint planned) with the model verifying integrity.
The model is the director, not the courier. Facade tool results carry their own instructions ("call
durable_task_getwith this id", "resume at offset N"), so any capable model can drive the protocol with zero host-side support.Failures are repairable data. Every failure carries
code,retryable,recoveryHint, andpartial.cursor— what went wrong, whether a retry can work, what to do instead, and what already succeeded.Lifecycle vocabulary matches the spec.
working / input_required / completed / failed / cancelled, so the native adapter can slot in later without breaking changes.
Status
Component | Status |
Tasks fallback facade (progress / input / cancel) | ✅ implemented |
Durable session stores (memory, JSON file) | ✅ implemented |
Chunked file transfer with resume + checksums | ✅ implemented |
Native ext-tasks adapter ( | 🔜 tracks the SDK's experimental Tasks API |
TUS 1.0 out-of-band endpoint for large files | 🔜 planned — see mcp#189 |
Redis / SQLite stores, Python port | 🔜 planned |
Quickstart
git clone https://github.com/ljppanda/mcp-longjobs
cd mcp-longjobs
npm install && npm run build
node dist/examples/report-generator.js(Once published to npm, the same server runs with a single command: npx mcp-longjobs.)
Point your client at it (stdio):
{
"mcpServers": {
"report-generator": {
"command": "node",
"args": ["/absolute/path/to/mcp-longjobs/dist/examples/report-generator.js"]
}
}
}Then ask: "Generate a report on EV batteries with 3 sections." Watch the model start the job, poll durable_task_get, and pick up the result. Kill the client mid-run, restart it, and ask for the same taskId — it resumes.
Development
npm install
npm test # vitest
npm run build # tsc -> dist/
npm run example # build + run the demo serverContributing
PRs welcome — especially: store backends (SQLite/Redis), the native ext-tasks adapter, and the TUS endpoint. Please open an issue first for anything larger.
License
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