relaykeep-tasks
Allows tasks to be run through a local Ollama server over loopback HTTP, with the response model checked against the requested model.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@relaykeep-tasksSubmit a task to translate the attached document and read the result."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
RelayKeep
English | 日本語
RelayKeep lets an MCP client hand a task to a local queue and read the result back later, by the same ID, as many times as it likes. A worker that you start yourself picks up queued tasks and runs them through a local model or a coding-agent CLI. A separate, optional MCP server gives read-only lookup over a small memory file.
It is a small, early-stage (0.0.0.dev3) project for one person on one machine. It is not published on PyPI or any other package index.
Contents
Related MCP server: mcp-job-queue
How it works
flowchart LR
client["MCP client"] -->|"stdio: submit / read / cancel"| tasks["relaykeep-tasks<br/>MCP server"]
tasks --> queue[("file queue<br/>root/tasks/*.json")]
worker["relaykeep-worker<br/>started by you, one pass"] --> queue
worker --> providers["Claude Code CLI · Codex CLI<br/>Ollama (loopback) · echo"]
subgraph optional["Optional, independent"]
client2["MCP client"] -->|"stdio: search / read"| memory["relaykeep-memory<br/>read-only MCP server"]
memory --> store[("JSONL file")]
endrelaykeep-tasksis a stdio MCP server with three tools:submit,readandcancel. It only writes to the queue. It never starts a worker or a model.relaykeep-workerruns a single pass over the queue and then exits. There is no daemon or scheduler. Run it by hand, or from a scheduler of your own.relaykeep-memoryis a separate stdio MCP server withsearchandread. Neither server imports the other, so you can use either one on its own.
What it does and doesn't do
Area | What you get | Not included |
Submit |
| Deduplication across different IDs |
Read |
| Push notifications, streaming, access control |
Worker | Manual, one pass, | Daemon, FIFO ordering, retries, timeouts, fallback models |
Cancel | Works on | Stopping a |
Providers | Claude Code CLI, Codex CLI, Ollama over loopback HTTP, and a built-in echo for testing | Remote HTTP providers, choosing a different model automatically |
Model identity |
| Proof of which model actually answered (see below) |
Memory | Read-only | MCP write tools, embeddings, ranking, deletion, access control |
Transport | stdio only | Network MCP, authentication, isolation between users |
Requirements
A POSIX system (Linux or macOS). RelayKeep uses
fcntlfile locks.Python 3.13 or newer. Tests have only been run on CPython 3.13.13.
mcp==1.28.1(the official MCP Python SDK) for the two MCP servers. This is the only SDK version that has been tested. The queue CLI (python -m relaykeep.slice) and the memory import command use only the standard library.To delegate, you also need your own install of the Claude Code CLI, the Codex CLI or Ollama.
Quickstart
Run these from the repository root. Replace /absolute/path/to/... with real paths.
python3.13 -m venv .venv
. .venv/bin/activate
pip install "mcp==1.28.1"Try the queue without any MCP client or model. If a payload has no provider key, the worker just echoes it back.
python -m relaykeep.slice submit --root /absolute/path/to/relaykeep-data \
--id hello-1 --payload '{"message": "hello"}'
# {"created_at": ..., "payload_sha256": "<sha>", "task_id": "<task_id>"}
python -m relaykeep.slice worker --root /absolute/path/to/relaykeep-data
# {"dispatched": ["<task_id>"], "skipped": [], "unknown_running": [], "worker_pid": ...}
python -m relaykeep.slice read --root /absolute/path/to/relaykeep-data \
--task-id '<task_id>' --sha '<sha>'
# {"error": null, "payload_sha256": "<sha>", "result": {"echo": {"message": "hello"}, "provider": "fake-echo"}, "state": "complete", ...}Replace '<task_id>' and '<sha>' with the values from the submit output. Keep the quotes if you paste the placeholders as they are, because an unquoted < or > is a shell redirection.
Records are stored under <root>/tasks/. To cancel a queued task, use python -m relaykeep.slice cancel --root /absolute/path/to/relaykeep-data --task-id '<task_id>' --sha '<sha>'. This command exits 0 even when nothing was cancelled, so check the cancelled field in its output.
If the package is installed (for example from a locally built wheel), the same commands are also available as relaykeep-worker, relaykeep-tasks and relaykeep-memory. relaykeep-worker ARGS is the same as python -m relaykeep.slice worker ARGS.
Connecting an MCP client
Many MCP clients accept an mcpServers JSON entry like the one below. The file location and the exact keys depend on your client, so check its documentation.
Running from a checkout (the interpreter must have mcp installed). PYTHONPATH is the checkout directory that contains the relaykeep/ package folder, not the package folder itself:
{
"mcpServers": {
"relaykeep-tasks": {
"command": "/absolute/path/to/.venv/bin/python",
"args": ["-m", "relaykeep.mcp_server", "--root", "/absolute/path/to/relaykeep-data"],
"env": { "PYTHONPATH": "/absolute/path/to/checkout" }
}
}
}Running the installed console script:
{
"mcpServers": {
"relaykeep-tasks": {
"command": "/absolute/path/to/.venv/bin/relaykeep-tasks",
"args": ["--root", "/absolute/path/to/relaykeep-data"]
}
}
}After connecting, call submit with a JSON object payload, run the worker yourself, and then call read with the task_id and payload_sha256 from the receipt.
Delegating to Claude Code, Codex or Ollama
The payload chooses the provider. For Claude and Codex, the payload must contain exactly these three keys:
{"provider": "claude", "model": "<model-id>", "prompt": "Reply with READY."}
{"provider": "codex", "model": "<model-id>", "prompt": "Reply with READY."}
{"provider": "ollama", "model": "<model-name>", "prompt": "Reply with READY."}The worker's own command-line options decide which executables run and in which directory. A payload cannot change them.
relaykeep-worker --root /absolute/path/to/relaykeep-data \
--claude-executable /absolute/path/to/claude \
--codex-executable /absolute/path/to/codex \
--work-dir /absolute/path/to/project \
--ollama-url http://127.0.0.1:11434From a checkout, python -m relaykeep.slice worker takes the same options.
Executable and work-directory paths must be absolute. There is no
PATHlookup, and the work directory must already exist. If a path is missing, that task fails and nothing is started.The prompt is sent on stdin, never on the command line, and no shell is used.
For Claude and Codex,
modelmust match a strict character pattern and can't start with-. The Ollama model name is sent in the JSON request body instead.The child CLI is an ordinary child process of the worker. It runs as the same OS user, inherits the worker's environment variables, and is subject to whatever OS-level restrictions apply to the worker process.
RelayKeep runs
claude -p --output-format json --model <model>andcodex exec --json --model <model> -. It adds no permission, sandbox or approval flags. See Security.Ollama is called with plain
httpon a loopback address only (127.0.0.1,localhostor::1), without following proxies or redirects.
Model identity. For Claude and Codex, model is always null and model_evidence.verified is false, because neither CLI's output has a field for the model that served the response. Claude's modelUsage is saved as observed, and it may list helper models as well. For Ollama, the task fails unless the model in the response matches the requested model. That value is still reported by your local server, not independently verified.
Optional memory server
The store is a JSONL file. Records can only be added with the admin command, which validates every row first. If an ID already exists with different content, the whole import is rejected.
python -m relaykeep.memory_store import \
--store /absolute/path/to/memory.jsonl --fixture fixtures/synthetic_memory.jsonl{
"mcpServers": {
"relaykeep-memory": {
"command": "/absolute/path/to/.venv/bin/relaykeep-memory",
"args": ["--store", "/absolute/path/to/memory.jsonl"]
}
}
}From a checkout, use "command": "/absolute/path/to/.venv/bin/python" with "args": ["-m", "relaykeep.memory_mcp_server", "--store", "/absolute/path/to/memory.jsonl"] and the same PYTHONPATH as above.
The server never changes the records, but it takes a file lock through
<store>.locknext to the store and may create that file. The store's directory must therefore be writable by the server.search(query, limit=5)returns the records whose text contains every query term (case-insensitive). Each hit has only an ID, a short snippet and the source.limitmust be between 1 and 20.read(id)returns the full record exactly as it was imported.Nothing is summarized, rewritten or added to a client's context automatically.
Reliability notes
No exactly-once execution. Each
client_request_idis queued at most once, and arunningtask is never dispatched again. But the outcome of a task is not always knowable.unknown_runningis not proof of a crash. A worker lists every record it finds in therunningstate underunknown_running. That includes tasks another worker is still running right now, as well as tasks left behind by a worker that died after claiming them. A task left behind that way staysrunning: nothing recovers it automatically, andcanceldoes not clear it. A provider process that has already started may keep running, and its output is lost.No timeouts. A CLI that hangs blocks that worker until you stop it.
No output size limit. CLI output is buffered in memory.
Power loss and filesystem crashes have not been tested.
Security
RelayKeep is designed for one trusted user on one local POSIX machine. Please read SECURITY.md before you point it at a real CLI or a real working directory. In short:
Approvals are not carried over from the front door. Approvals given in the MCP client session that submitted the task are not automatically transferred to the child CLI. The child runs with its own native permissions and configuration, including settings the CLI has saved to disk, which it may read again. It also loads hooks, rules and MCP server settings from your home directory or
--work-dir. It inherits the worker's OS user, environment and any OS-level restrictions on the worker process. Nobody is present to answer approval prompts.Intended scope is one trusted user on a local POSIX machine. There is no network MCP transport, authentication or isolation between users.
Prompt injection: treat prompts, responses and memory records as untrusted input. Native hooks and project configuration in the work directory run as your user.
Model identity: results are not proof of which model answered (see above).
Local files are not encrypted. Payloads (including prompts), results and error text are stored as plain JSON. Error text may include CLI stderr and known stdout failure fields (up to 300 characters each) and Python exception text, and it is not scrubbed for secrets. There is no delete command or retention setting.
Cancel works on
queuedonly,unknown_runningneeds manual review, and there is no exactly-once execution.
The source has been read by LLM-based code reviewers (static review only). That is not a penetration test, a certification or an independent security audit, and RelayKeep has had none of those. How to report a vulnerability is also described in SECURITY.md. Please don't put exploit details in a public issue.
Status and license
Version
0.0.0.dev3, pre-release. It is not an audited release, and it is not published on any package index.Tested: 72 unit tests pass on CPython 3.13.13 with
mcp1.28.1. The tests use synthetic stand-ins for the CLIs and make no real model calls.Checked with real tools, on a single machine only:
The Claude Code CLI (requested model
claude-opus-5) and the Codex CLI (requested modelgpt-5.6-sol) each completed a task with the expectedREADYreply. Reading the same task ID twice returned the same result.A local Ollama task with
qwen3.8:27bcompleted.The CLI flags were checked against the help output of Claude Code 2.1.273 and Codex CLI 0.154.0.
These checks don't cover every environment, CLI version or model.
License: MIT, Copyright (c) 2026 tsunamayo7. Dependencies such as
mcpare not bundled and keep their own licenses.
Available Tools
3 toolscancelA
queuedのtaskだけ取り消す。runningはcancelled=False(already_running)で、停止はしない。
| Name | Required | Description | Default |
|---|---|---|---|
| task_id | Yes | ||
| payload_sha256 | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries full behavioral burden. It discloses that it does not stop running tasks and sets cancelled=False for them, which is important non-obvious behavior. However, it does not mention side effects, permissions, or return values.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two concise sentences with no wasted words. The primary purpose is front-loaded, and the second sentence adds essential nuance about running tasks. Excellent structure.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description explains the core queued/running behavior but omits parameter semantics and does not describe success/failure outcomes or any prerequisites. With two required parameters and no annotations or output schema, this is incomplete for reliable invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, and the description provides no explanation of task_id or payload_sha256. It does not compensate for the missing parameter documentation at all, leaving the agent without guidance on what these parameters mean or how to use them.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool cancels only queued tasks, and explicitly distinguishes running tasks by noting they get cancelled=False and are not stopped. This is a specific verb+resource with clear differentiation from siblings submit and read.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear usage context: it is for queued tasks only, and running tasks are not affected. It implies when not to use it, though it does not explicitly name alternative tools. The condition is specific and actionable.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
readB
投入時と同じtask_id+payload_sha256で状態と結果を反復読取する。
| Name | Required | Description | Default |
|---|---|---|---|
| task_id | Yes | ||
| payload_sha256 | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It does add value by disclosing the key behavioral trait that this is an iterative/repeated polling operation (反復読取) rather than a one-shot read. However, it does not state whether the call is read-only and side-effect-free, what happens when the task is not found or still pending, or whether it blocks. The disclosure is partial.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single compact sentence with no filler or redundancy. The key identification constraint (same task_id+payload_sha256 as submission) is included, and every element serves the purpose. The only structural downside is that the verb is end-loaded due to Japanese syntax, slightly reducing scannability for English-language agents.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a polling tool in a submit/read/cancel lifecycle with no output schema and no annotations, the description leaves significant gaps: it does not describe the result shape, status vocabulary, termination conditions, or error behavior. An agent knows what to pass but not what to expect back or when to stop polling. Given the low complexity of the input surface, more completeness was achievable.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate for the two bare parameters. It adds the meaningful constraint that both task_id and payload_sha256 must match the values used at submission time, which is essential for correct invocation. However, it does not explain what each parameter individually represents (e.g., that payload_sha256 is likely a content hash), leaving part of the semantic burden unmet.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: iteratively read (反復読取する) the status and result (状態と結果) of a task identified by task_id+payload_sha256. Although written in Japanese, it clearly conveys a polling/read operation that is distinguishable from the sibling tools submit and cancel. It lacks an explicit sibling-differentiation sentence, but the verb+resource pairing is specific enough to identify the tool's role.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage context is implied rather than stated: the phrase '投入時と同じ' (same as at submission time) signals that this tool is used after submit with the identifiers recorded at submission. However, there is no explicit guidance on when to use read versus submit/cancel, no mention of polling-until-complete behavior, and no exclusion or alternative routing. This meets the 'implied usage' level but nothing more.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
submitA
client_request_idごとに高々1回投入し、task_id+payload_sha256のreceiptを返す。
| Name | Required | Description | Default |
|---|---|---|---|
| payload | Yes | ||
| client_request_id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden and does well by disclosing the idempotency constraint ('at most once per client_request_id') and the exact receipt content. It does not cover duplicate-call behavior or error handling, but the key side-effect semantics are present.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The entire description is one dense, front-loaded sentence with no filler. It prioritizes the idempotency rule and then specifies the receipt format, making every word informative.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no annotations, no output schema, and an undocumented payload parameter, the description covers the essential receipt and dedup behavior but leaves out payload semantics, duplicate handling, and usage boundaries. It is minimally complete but not fully self-sufficient.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate. It clarifies that client_request_id is the deduplication key and implies that payload is hashed into payload_sha256. However, it does not explain what payload should contain, which is a notable gap for an arbitrary-object parameter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific behavior: submit at most once per client_request_id and return a receipt with task_id and payload_sha256. This is clear and functional, though it does not explicitly name the sibling tools or state that this creates a task.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage context is implied: this tool is for submitting payloads, while the siblings read and cancel suggest inspection and abatement. However, the description does not give explicit when-to-use or when-not-to-use guidance, nor does it mention alternatives by name.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
v0.1.0- First observed
cancel - First observed
read - First observed
submit
TDQS
Scored across 3 tools
Each tool maps to a distinct lifecycle operation: submit creates a task, read retrieves state and result, and cancel handles queued task cancellation. There is no meaningful overlap between the three actions.
All tool names are single-word imperative verbs—submit, read, cancel—following a consistent and predictable pattern. While not verb_noun style, the convention is uniform across the entire server.
Three tools is well-scoped for a task relay server: each tool covers a necessary operation and none are redundant. This is comfortably within the ideal 3-15 range.
The surface covers the core task lifecycle: submission, status/result reading, and cancellation of queued tasks. The inability to cancel running tasks is explicitly documented rather than an overlooked gap.
Maintenance
Related MCP Connectors
Hosted MCP server for task-first delegation to remote workstations and workers.
Local-first task manager: create, edit, and complete tasks, projects, and checklists via MCP.
Hosted MCP memory and agent control plane for durable conversations, jobs, and operations.
Project management MCP for AI agents with safe task reads and writes.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceExposes an agent orchestration task queue as an MCP tool interface, allowing agents to submit, list, get, and update tasks with typed validation.3MIT
- AlicenseAqualityDmaintenanceEnables MCP clients to submit long-running jobs that are executed safely in isolated child processes with a durable SQLite queue, configurable timeouts, retries with backoff, and backpressure.5MIT
- FlicenseNot gradedqualityCmaintenanceProvides a self-hosted shared work queue for AI agents via MCP, using a local SQLite database for task management.-
- AlicenseAqualityCmaintenanceEnables agents to submit and manage persistent, dependency-aware task graphs with immutable artifacts, resource reservations, durable event streaming, and retryable process execution over MCP.12MIT