MCP Server
NODYN can be exposed as an MCP (Model Context Protocol) server so other MCP-compatible clients can use NODYN as a tool provider and async agent runtime.
Connecting to External MCP Servers
Section titled “Connecting to External MCP Servers”nodyn can also connect to external MCP servers as a client, gaining access to their tools:
# CLI — persisted to ~/.nodyn/config.json/mcp add context7 https://context7.example.com/mcp add my-tools https://localhost:8080/mcp list # Show all registered servers/mcp remove name # Remove a server
# Or in config directly{ "mcp_servers": [ { "name": "context7", "url": "https://context7.example.com" }, { "name": "my-tools", "url": "http://localhost:8080" } ]}Connections are persistent — servers are reconnected on every startup. MCP servers are self-describing: their tool definitions are automatically available to the agent.
Transports
Section titled “Transports”node dist/index.js --mcp-serverStandard MCP stdio transport. Input/output on stdin/stdout, logs on stderr.
node dist/index.js --mcp-server --transport sseStarts the HTTP transport on port 3042 by default (configurable via NODYN_MCP_PORT).
Exposed Tools
Section titled “Exposed Tools”Core execution
Section titled “Core execution”| Tool | Description |
|---|---|
nodyn_run | Run a task synchronously and return the final text |
nodyn_run_start | Start an async run and return run_id immediately |
nodyn_poll | Poll accumulated output, status, errors, waiting_for_input, and output_files |
nodyn_reply | Resume a paused async run waiting for user input |
nodyn_abort | Abort in-flight work by session_id |
nodyn_reset | Reset a session and abort any active run in it |
Data access
Section titled “Data access”| Tool | Description |
|---|---|
nodyn_memory | Read facts, skills, context, or errors memory |
nodyn_read_file | Read a produced file as base64 from the working dir or /tmp/nodyn-files |
| Tool | Description |
|---|---|
nodyn_batch | Submit a reduced-cost async batch |
nodyn_status | Inspect a batch by batch_id |
Async Run Lifecycle
Section titled “Async Run Lifecycle”- Call
nodyn_run_startwithtaskand optionalsession_id,user_context, andfiles. - Poll with
nodyn_poll. Passcursorfor incremental event streaming. - If
waiting_for_inputis returned, answer withnodyn_reply. - When
done: true, readtext,statusHistory,error, and optionaloutput_files. - If files were produced, fetch them with
nodyn_read_file.
Example nodyn_run_start response:
{ "run_id": "6af0d8d2-..." }Example nodyn_poll response while waiting on user input:
{ "done": false, "text": "Working on it...", "status": "⚡ write_file", "waiting_for_input": { "question": "Allow running: rm -rf ./dist?", "options": ["Allow", "Deny"] }}Example terminal nodyn_poll response:
{ "done": true, "text": "Finished.", "statusHistory": ["read_file", "write_file"], "output_files": [ { "name": "report.md", "path": "/tmp/nodyn-files/...", "type": "file" } ]}Event Log (Cursor-Based Polling)
Section titled “Event Log (Cursor-Based Polling)”The MCP server maintains an append-only event log per async run. Clients can poll with a cursor parameter to receive only new events since the last poll:
// Request: nodyn_poll({ run_id: "...", cursor: 5 })// Response:{ "done": false, "text": "...", "events": [ { "id": 6, "type": "tool_call", "timestamp": 1710345678901, "data": { "name": "bash", "input": { "command": "git status" } } }, { "id": 7, "type": "tool_result", "timestamp": 1710345679123, "data": { "name": "bash", "success": true, "preview": "On branch main..." } } ], "nextCursor": 7}Event types
Section titled “Event types”| Type | Data fields | Description |
|---|---|---|
thinking | summary (first 200 chars) | Thinking step (max 1 per API turn) |
tool_call | name, input | Tool invocation |
tool_result | name, success, preview (first 300 chars) | Tool completion |
text_chunk | text | Accumulated text (flushed on tool_call, turn_end, or >2000 chars) |
turn_end | stop_reason | API turn completed |
error | message | Error during run |
continuation | iteration, max | Auto-continuation |
Backward compatibility
Section titled “Backward compatibility”When cursor is omitted, the poll response is identical to the pre-event-log format. Existing clients (without cursor support) are unaffected.
Limits
Section titled “Limits”- Max 500 events per run (oldest evicted when exceeded)
- Text buffer flushed as
text_chunkevents at 2000 character intervals
Attachments
Section titled “Attachments”nodyn_run_start accepts optional files entries with:
{ "name": "notes.txt", "mimetype": "text/plain", "data": "<base64>", "size": 1234}Behavior:
- small text attachments can be safely inlined into the prompt
- larger or binary attachments are written to
/tmp/nodyn-files/<run_id>/... - attachment wrappers are XML-escaped before prompt injection
- duplicate names are sanitized and de-conflicted
Limits and Persistence
Section titled “Limits and Persistence”Current MCP safety limits:
- max
12active sessions with running async work - max
64tracked async runs - max
2MBbuffered text per run - max
8MBbuffered text across all tracked runs - max
10MBper attachment,25MBtotal attachment payload per run - max
200KBinline text attachment budget per run
Async run metadata is persisted to:
$NODYN_MCP_STATE_DIR/mcp-runs.json, or.nodyn/mcp-runs.jsonunder the current working directory
Restart behavior:
- completed runs remain pollable after restart
- runs that were still active at shutdown are restored as completed with error text
Server restarted before run completed
Health Check
Section titled “Health Check”GET /healthAlways returns:
{ "status": "ok" }This endpoint is intentionally unauthenticated for health checks.
Authentication
Section titled “Authentication”Set NODYN_MCP_SECRET to enable bearer auth on the HTTP transport:
export NODYN_MCP_SECRET="your-secret-token"node dist/index.js --mcp-server --transport sseOr store it in the encrypted vault (loaded automatically when env var is not set):
nodyn vault set NODYN_MCP_SECRET "your-secret-token"Clients must send:
Authorization: Bearer your-secret-tokenWithout NODYN_MCP_SECRET, the HTTP transport is open and binds to 127.0.0.1 only. Token comparison uses crypto.timingSafeEqual.
Network exposure: When auth is enabled, the server binds to 0.0.0.0. A startup warning recommends TLS termination via reverse proxy since the Bearer token travels in cleartext over plain HTTP.
Secret rotation: Vault-stored secrets older than 90 days trigger a startup warning. Rotate with nodyn vault set NODYN_MCP_SECRET <new-token>. Generate a strong token: openssl rand -hex 32.
Session Management
Section titled “Session Management”The MCP server uses Engine (not Nodyn directly) internally. SessionStore (src/core/session-store.ts) holds per-session Session instances (created via engine.createSession()), each with its own conversation history, mode, and active run state.
Only one active async run is allowed per session at a time. Starting another one returns an error with the existing run_id when available.
Usage Examples
Section titled “Usage Examples”stdio (Docker)
Section titled “stdio (Docker)”docker run -i --rm -e ANTHROPIC_API_KEY=sk-ant-... nodyn --mcp-serverHTTP (Docker)
Section titled “HTTP (Docker)”docker run -it --rm \ -e ANTHROPIC_API_KEY=sk-ant-... \ -p 3042:3042 \ nodyn --mcp-server --transport sseProgrammatic
Section titled “Programmatic”import { NodynMCPServer } from '@nodyn-ai/core';
const server = new NodynMCPServer({ model: 'sonnet', effort: 'high',});await server.init();
await server.startHTTP(3042);Environment Variables
Section titled “Environment Variables”| Variable | Default | Description |
|---|---|---|
NODYN_MCP_PORT | 3042 | HTTP server port |
NODYN_MCP_SECRET | (none) | Bearer token for HTTP auth. Can also be stored in vault |
NODYN_MCP_STATE_DIR | .nodyn/ under cwd | Directory for persisted async run state |
ANTHROPIC_API_KEY | — | API key |
ANTHROPIC_BASE_URL | — | Custom API endpoint |