Odel
nexus mcp

nexus mcp

Local
@j7an4PythonMITUpdated 5 days ago

Invoke CLI agents (Codex, Claude, OpenCode) as MCP tools with parallel execution

Nexus MCP

PyPI Python 3.13+ License: MIT Ruff type-checked: mypy pre-commit MCP

An MCP server that enables AI models to invoke AI CLI agents (Codex, Claude Code, OpenCode) as tools. Provides durable workspace-scoped jobs, parallel execution, automatic retries with exponential backoff, JSON-first response parsing, discoverable prompt templates, model tier classification, and persistent preferences through MCP tools, resources, and prompts.

Use Cases

Nexus MCP is useful whenever a task benefits from querying multiple AI agents in parallel rather than sequentially:

  • Research & summarization — fan out a topic to multiple agents, then synthesize their responses into a single summary with diverse perspectives
  • Code review — send different files or review angles (security, correctness, style) to separate agents simultaneously
  • Multi-model comparison — prompt the same question to different models and compare outputs side-by-side for quality or consistency
  • Bulk content generation — generate multiple test cases, translations, or documentation pages concurrently instead of one at a time
  • Second-opinion workflows — get independent answers from separate agents before making a decision, reducing single-model bias

Features

  • Parallel executionbatch_prompt fans out tasks with asyncio.gather and a configurable semaphore (default concurrency: 3)
  • Durable jobs — start, observe, cancel, and resume normalized agent work through stable job and session identities backed by a private per-user SQLite database
  • Automatic retries — exponential backoff with full jitter for transient errors (HTTP 429/503)
  • Output handling — JSON-first parsing, brace-depth fallback for noisy stdout, temp-file spillover for outputs exceeding 50 KB
  • Execution modesdefault (safe, no auto-approve), yolo (full auto-approve)
  • CLI detection — auto-detects binary path, version, and JSON output capability at startup
  • Persistent preferences — set defaults for execution mode, model, retries, output limit, and timeout; preferences persist across MCP sessions via the backing store (MemoryStore default, FileTreeStore/RedisStore for restart persistence)
  • Prompt templates — 10 discoverable workflow scaffolds (code review, debug, research, implement feature, etc.) via list_prompts/get_prompt; each returns structured messages with expert framing the client can use or ignore
  • Model tier classification — heuristic-based model classification into quick/standard/thorough tiers; clients can override with sampling or live benchmarks. The nexus://runners resource includes tier data per model
  • Tool timeouts — configurable safety timeout (default 15 min) cancels long-running tool calls to prevent the server from blocking indefinitely
  • Client-visible logging — runner events (retries, output truncation, error recovery) are sent to MCP clients via protocol notifications, not just server stderr
  • Elicitation — interactive parameter resolution via MCP elicitation; disambiguates missing CLI, offers model selection, confirms YOLO mode, and prompts for elaboration on vague prompts. Auto-detects client support and skips gracefully when unavailable. Suppression flags prevent repeat prompts within a session
  • Benchmark data sources — server instructions include URLs for Artificial Analysis, OpenRouter, Chatbot Arena, and LLM Stats so clients can fetch live model benchmarks without API keys
  • Extensible — implement build_command + parse_output, register in RunnerFactory
AgentStatus
CodexSupported
Claude CodeSupported
OpenCodeSupported

Installation

Run with uvx (recommended)

uvx nexus-mcp

uvx installs the package in an ephemeral virtual environment and runs it — no cloning required.

To check the installed version:

uvx nexus-mcp --version

To update to the latest version:

uvx --reinstall nexus-mcp

MCP Client Configuration

Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):

{
  "mcpServers": {
    "nexus-mcp": {
      "command": "uvx",
      "args": ["nexus-mcp"],
      "env": {
        "NEXUS_CODEX_MODEL": "gpt-5.2",
        "NEXUS_CODEX_MODELS": "gpt-5.4,gpt-5.4-mini,gpt-5.3-codex,gpt-5.2-codex,gpt-5.2,gpt-5.1-codex-max,gpt-5.1-codex-mini",
        "NEXUS_CLAUDE_MODEL": "claude-sonnet-4-6",
        "NEXUS_CLAUDE_MODELS": "claude-sonnet-4-6,claude-haiku-4-5-20251001",
        "NEXUS_OPENCODE_MODEL": "ollama-cloud/kimi-k2.5",
        "NEXUS_OPENCODE_MODELS": "ollama-cloud/glm-5,ollama-cloud/kimi-k2.5,ollama-cloud/qwen3-coder-next,ollama-cloud/minimax-m2.5,ollama/gemini-3-flash-preview"
      }
    }
  }
}

Cursor (.cursor/mcp.json in your project or ~/.cursor/mcp.json globally):

{
  "mcpServers": {
    "nexus-mcp": {
      "command": "uvx",
      "args": ["nexus-mcp"],
      "env": {
        "NEXUS_CODEX_MODEL": "gpt-5.2",
        "NEXUS_CODEX_MODELS": "gpt-5.4,gpt-5.4-mini,gpt-5.3-codex,gpt-5.2-codex,gpt-5.2,gpt-5.1-codex-max,gpt-5.1-codex-mini",
        "NEXUS_CLAUDE_MODEL": "claude-sonnet-4-6",
        "NEXUS_CLAUDE_MODELS": "claude-sonnet-4-6,claude-haiku-4-5-20251001",
        "NEXUS_OPENCODE_MODEL": "ollama-cloud/kimi-k2.5",
        "NEXUS_OPENCODE_MODELS": "ollama-cloud/glm-5,ollama-cloud/kimi-k2.5,ollama-cloud/qwen3-coder-next,ollama-cloud/minimax-m2.5,ollama/gemini-3-flash-preview"
      }
    }
  }
}

Claude Code (CLI):

claude mcp add nexus-mcp \
  -e NEXUS_CODEX_MODEL=gpt-5.2 \
  -e NEXUS_CODEX_MODELS=gpt-5.4,gpt-5.4-mini,gpt-5.3-codex,gpt-5.2-codex,gpt-5.2,gpt-5.1-codex-max,gpt-5.1-codex-mini \
  -e NEXUS_CLAUDE_MODEL=claude-sonnet-4-6 \
  -e NEXUS_CLAUDE_MODELS=claude-sonnet-4-6,claude-haiku-4-5-20251001 \
  -e NEXUS_OPENCODE_MODEL=ollama-cloud/kimi-k2.5 \
  -e NEXUS_OPENCODE_MODELS=ollama-cloud/glm-5,ollama-cloud/kimi-k2.5,ollama-cloud/qwen3-coder-next,ollama-cloud/minimax-m2.5,ollama/gemini-3-flash-preview \
  -- uvx nexus-mcp

Generic stdio config (any MCP-compatible client):

{
  "command": "uvx",
  "args": ["nexus-mcp"],
  "transport": "stdio",
  "env": {
    "NEXUS_CODEX_MODEL": "gpt-5.2",
    "NEXUS_CLAUDE_MODEL": "claude-sonnet-4-6",
    "NEXUS_OPENCODE_MODEL": "ollama-cloud/kimi-k2.5"
  }
}

All env keys are optional — see Configuration for the full list.

Setup for Development

Prerequisites:

  • Python 3.13+ (download)
  • uv dependency manager (install guide)
    curl -LsSf https://astral.sh/uv/install.sh | sh
    

Optional (for integration tests):

  • Codex — check with codex --version
  • Claude Code — check with claude --version
  • OpenCode — check with opencode --version

Claude Code note: Nexus invokes Claude Code non-interactively via claude -p. Anthropic says claude -p and Agent SDK usage draw from separate monthly Agent SDK credits starting 2026-06-15, while interactive Claude Code usage remains on plan usage limits: https://support.claude.com/en/articles/15036540-use-the-claude-agent-sdk-with-your-claude-plan

Note: Integration tests are optional. Unit tests run without CLI dependencies via subprocess mocking.

# 1. Clone the repository
git clone <repository-url>
cd nexus-mcp

# 2. Install dependencies
uv sync

# 3. Install pre-commit hooks (runs linting/formatting on commit)
uv run pre-commit install

# 4. Verify installation
uv run pytest                    # Run tests
uv run mypy src/nexus_mcp        # Type checking
uv run ruff check .              # Linting

# 5. Run the MCP server
uv run python -m nexus_mcp

OpenCode Server (Docker) — experimental

⚠️ Experimental — This integration has not been validated end-to-end by the maintainer. Expect rough edges in setup, auth, and tool exposure. The MCP tools surfaced from upstream OpenCode track the upstream project and may change without notice. Feedback and bug reports are welcome.

Run an isolated OpenCode server for HTTP-based agent execution alongside the CLI runner. Provides session management, file search, permissions, and 38 additional MCP tools when the server is healthy.

Quick start:

  1. Copy .env.example to .env and set PROJECT_DIR to your project path:
    cp .env.example .env
    # Edit .env: set PROJECT_DIR=/path/to/your/project
    
  2. Start the server:
    docker compose up -d
    
  3. Authenticate with your provider:
    docker exec -it opencode-server opencode auth login
    
  4. Verify the server is healthy:
    curl -u opencode:nexus http://localhost:4096/global/health
    

The server binds to 127.0.0.1 (localhost only) by default for security. See docs/opencode-server-setup.md for the full guide including remote access, multi-project setup, and network security.

Usage

Once nexus-mcp is configured in your MCP client, your AI assistant automatically sees its tools. The reliable trigger is explicitly asking for output from an external AI agent (e.g. Codex, Claude Code, OpenCode). Generic "do this in parallel" prompts may be handled by the host AI's own capabilities instead. The cli parameter is optional — if omitted and the client supports MCP elicitation, the server will ask which runner to use. The server provides runner metadata (names, models, availability, execution modes) in its connection instructions — no discovery call needed. The cli parameter includes a JSON schema enum listing valid runner names.

Usage Examples

Fan out a research question (batch_prompt)

You say: "Get perspectives from Codex, Claude Code, and OpenCode on transformer architectures."

{
  "tasks": [
    { "cli": "codex", "prompt": "Summarize the key findings of the Attention Is All You Need paper", "label": "codex-summary" },
    { "cli": "claude", "prompt": "What are the main limitations of transformer architectures?", "label": "claude-limitations" },
    { "cli": "opencode", "prompt": "List 3 real-world applications of transformers beyond NLP", "label": "opencode-applications" }
  ]
}

Code review from multiple angles (batch_prompt)

You say: "Have Codex, Claude Code, and OpenCode each review this diff in parallel."

{
  "tasks": [
    { "cli": "codex", "prompt": "Review this diff for security vulnerabilities:\n\n<paste diff>", "label": "codex-security-review" },
    { "cli": "claude", "prompt": "Review this diff for correctness and edge cases:\n\n<paste diff>", "label": "claude-correctness-review" },
    { "cli": "opencode", "prompt": "Review this diff for style and maintainability:\n\n<paste diff>", "label": "opencode-review" }
  ]
}

Single-agent prompt

You say: "Ask Codex to explain the difference between TCP and UDP."

{ "cli": "codex", "prompt": "Explain the difference between TCP and UDP in simple terms", "model": "gpt-5.2" }

Elicitation (server picks the runner)

You say: "Explain the CAP theorem using one of the available agents."

{ "prompt": "Explain the CAP theorem in simple terms" }

If the client supports MCP elicitation, the server asks which runner to use. Pass "elicit": false to skip.

Persistent preferences

You say: "Use YOLO mode with Codex from now on."

{ "execution_mode": "yolo", "model": "gpt-5.2", "max_retries": 5 }

Subsequent calls inherit these settings. Preferences persist across MCP sessions until explicitly cleared.

Fallback chain: explicit parameter → saved preference → per-runner env → global env → hardcoded default.

MCP Tools

Nexus exposes a durable agent_* surface and the original compatibility prompt surface. Every durable tool requires an explicit workspace selector containing exactly one of an existing workspace_id or a filesystem path; Nexus never infers a durable workspace from the server's current directory. A path is resolved to one canonical workspace identity before admission.

Execution-starting durable tools return a JobHandle immediately. Clients use the observation and control tools to follow the normalized job independently of an MCP request lifetime.

ToolDescription
agent_startCreate a durable session and queue its first turn
agent_continueQueue another turn on an existing session
agent_forkCreate a child session when the backend supports forking
agent_reviewQueue a typed review operation on an existing session
agent_diagnoseQueue a sessionless backend diagnostic job
agent_statusRead the current normalized status of one job
agent_resultRead the pending or terminal typed result of one job
agent_listPage through authorized jobs in one workspace
agent_backendsList backend capabilities and current availability for one workspace
agent_cancelRequest idempotent cancellation of a queued or active job
agent_respondResolve a pending approval, permission, question, or form input

The compatibility prompt and batch_prompt tools retain their background-task behavior. They return FastMCP task IDs so clients can poll without holding a long-running MCP request open. Per-call concurrency defaults to 3. The shared process runtime starts with 3 workers and grows to a high-water maximum of 8; one call whose effective demand exceeds 8 is rejected explicitly, while concurrent calls share the process ceiling and may queue.

ToolTask?Description
batch_promptYesFan out prompts to multiple runners in parallel; returns MultiPromptResponse
promptYesSingle-runner convenience wrapper; routes to batch_prompt
set_preferencesNoSet or selectively clear persistent defaults for execution mode, model, retries, timeouts, elicitation, and trigger suppression
get_preferencesNoRetrieve current preferences
clear_preferencesNoReset all preferences
set_model_tiersNoSave model tier classifications (client sends sampling/benchmark results; server persists)
get_model_tiersNoRetrieve saved model tier classifications

Tool API Reference

batch_prompt

ParameterRequiredDefaultDescription
tasksYesList of task objects (see below)
max_concurrencyNo3Max parallel agent invocations for this call; effective demand above the process worker maximum of 8 is rejected
elicitNopref or trueEnable/disable interactive elicitation for this call

Task object fields:

FieldRequiredDefaultDescription
cliNoRunner name (e.g. "codex"); if omitted, elicitation asks which runner to use
promptYesPrompt text
labelNoautoDisplay label for results
contextNo{}Optional context metadata dict
execution_modeNopref or "default""default" or "yolo"
modelNopref or CLI defaultModel name override
max_retriesNopref or env defaultMax retry attempts for transient errors
output_limitNopref or env defaultMax output bytes
timeoutNopref or env defaultSubprocess timeout in seconds
retry_base_delayNopref or env defaultBase delay for exponential backoff
retry_max_delayNopref or env defaultMax delay cap for backoff

Note: elicit is a batch-level parameter. When enabled, the server runs a single upfront elicitation pass across all tasks rather than prompting per-task.

prompt

Same parameters as a single task object in batch_prompt, plus elicit (batch-level in batch_prompt, per-call here).

set_preferences

ParameterRequiredDefaultDescription
execution_modeNo"default" or "yolo"
modelNoModel name (e.g. "gpt-5.2")
max_retriesNoMax total attempts (≥1; 1 = no retries)
output_limitNoMax output bytes (≥1)
timeoutNoSubprocess timeout seconds (≥1)
retry_base_delayNoBackoff base delay seconds (≥0)
retry_max_delayNoBackoff max delay seconds (≥0)
elicitNotrueEnable/disable elicitation
confirm_yoloNotruePrompt before YOLO mode (auto-suppressed after first accept)
confirm_vague_promptNotruePrompt on very short prompts
confirm_high_retriesNotruePrompt when max_retries > 5
confirm_large_batchNotruePrompt when batch > 5 tasks
clear_*NofalseClear any field individually (e.g. clear_model: true)

get_preferences / clear_preferences

get_preferences — no parameters, returns all fields (null when unset). clear_preferences — no parameters, resets all to null. Does not clear model tiers.

set_model_tiers

ParameterRequiredDefaultDescription
tiersYesDict mapping model names to tiers ("quick", "standard", "thorough")

Persists tier classifications. Clients typically call once via sampling or benchmark fetch.

get_model_tiers

No parameters. Returns saved tiers as dict[str, str], or {} if none saved.

Managing Preferences

OperationToolNotes
Set fieldsset_preferencesPersists across sessions
Read valuesget_preferencesnull for unset fields
Clear allclear_preferencesDoes not clear model tiers
Clear one fieldset_preferences with clear_*: trueOthers preserved
Suppress elicitationset_preferences with confirm_*: falseYOLO/batch/retry auto-suppress after accept
Re-enable promptset_preferences with clear_confirm_*: trueResets to default
Save/read tiersset_model_tiers / get_model_tiersPersists across sessions

Durable Job Architecture

The framework-independent core separates normalized domain contracts from concrete backends, storage, and the MCP transport. A job is one admitted operation and owns its retry attempts, events, controls, and terminal result. A session is a durable conversation identity bound to one workspace and backend; agent_start creates it, agent_continue reuses it, and agent_fork creates a child when supported. Diagnostic jobs may be sessionless. A session and a job are not MCP client sessions or FastMCP background-task IDs.

Jobs and sessions use private | workspace access policies:

  • private (the default) is visible only to the owning principal.
  • workspace is visible to the owner and to callers explicitly authorized for that same workspace. It never grants cross-workspace access. For the local MCP adapter, the operating-system user is the principal and the private database permissions form the trust boundary.

The SQLite database contains sensitive prompts, normalized events, provider references, and results. Set NEXUS_DB_PATH to override its location. Otherwise Nexus uses these per-user paths:

  • macOS: ~/Library/Application Support/nexus-mcp/nexus.sqlite3
  • Windows: %LOCALAPPDATA%\nexus-mcp\nexus.sqlite3 (falling back to ~/AppData/Local/nexus-mcp/nexus.sqlite3)
  • Linux and other Unix platforms: ${XDG_DATA_HOME:-~/.local/share}/nexus-mcp/nexus.sqlite3

On POSIX systems Nexus removes group and other access from the database directory and SQLite files. Normalized job, session, event, and result records are retained indefinitely by default; Nexus does not schedule automatic pruning. Applying retention cutoffs is an explicit store operation, and no public MCP pruning tool is currently exposed.

Codex, Claude Code, and OpenCode execution currently passes through the temporary LegacyRunnerBackend bridge while native backends are developed. The bridge supports normalized turns only: it does not provide backend cancellation, graceful interruption, session forking, or safe reconciliation after an interrupted attempt. These are legacy-backend limitations, not core job-model promises; clients should inspect agent_backends capabilities before selecting an operation.

MCP Prompts

Nexus MCP provides 10 discoverable prompt templates that clients can browse via list_prompts() and render via get_prompt(name, args). Each prompt returns structured messages with expert framing — the client decides how (or whether) to use them.

Design principle: Server informs, client decides. Prompts provide the scaffold (role, structure, methodology); the client decides runner, model, depth, and orchestration. Prompts are completely optional — existing prompt/batch_prompt tools work exactly as before.

PromptTagsParametersPurpose
code_reviewanalysisfile, instructionsStructured code review with findings by severity
debuganalysiserror, context, fileSystematic diagnosis: reproduce, isolate, root cause, fix
quick_triageanalysisdescription, fileFast assessment: what's wrong, severity, next step
researchanalysistopic, scopeStructured research with source citations
second_opinionanalysisoriginal_output, questionIndependent review of another AI's output
implement_featuregenerationdescription, language, constraintsFeature implementation with quality checklist
refactorgenerationfile, goal, constraintsBehavior-preserving restructuring
bulk_generategenerationtemplate, variablesExpand template across variable sets
write_teststestingfile, framework, coverage_goalTest generation with configurable coverage approach
compare_modelscomparisonprompt, criteriaMulti-runner comparison framework
Example — using a prompt template
# 1. Client discovers available prompts
list_prompts() → sees "code_review", "debug", "compare_models", etc.

# 2. Client renders a prompt with arguments
get_prompt("code_review", {file: "src/auth.py", instructions: "security vulnerabilities"})

# 3. Server returns structured messages
→ PromptResult(
    messages=[
      Message("You are a senior code reviewer...", role="assistant"),
      Message("Review the file `src/auth.py`...\nFocus: security vulnerabilities\n...", role="user"),
    ],
    description="Code review of src/auth.py"
  )

# 4. Client feeds messages into prompt/batch_prompt with chosen runner+model
prompt(cli="claude", prompt=<rendered messages>)

MCP Resources

Read-only data endpoints that clients query for runner metadata, configuration, and preferences.

Resource URIDescription
nexus://runnersAll registered CLI runners with models (enriched with tier data), modes, availability
nexus://runners/{cli}Single runner details by name (URI template)
nexus://configResolved operational config defaults (timeouts, retries, output limits)
nexus://preferencesCurrent preferences with config fallback

Models in nexus://runners include tier data: {"name": "gpt-5.4-mini", "tier": "quick"}. Tiers are quick (fast/cheap), standard (balanced), or thorough (max quality). Models with only heuristic tiers appear in unclassified_models — calling set_model_tiers moves them out.

Model tier enrichment examples

Before set_model_tiers — all tiers are heuristic guesses, all models are unclassified:

{
  "models": [
    {"name": "gpt-5.1-codex-max", "tier": "thorough"},
    {"name": "gpt-5.4-mini", "tier": "quick"},
    {"name": "claude-sonnet-4-6", "tier": "standard"}
  ],
  "unclassified_models": ["gpt-5.1-codex-max", "gpt-5.4-mini", "claude-sonnet-4-6"]
}

After set_model_tiers — saved tiers replace heuristics, classified models leave the list:

{
  "models": [
    {"name": "gpt-5.1-codex-max", "tier": "thorough"},
    {"name": "gpt-5.4-mini", "tier": "quick"},
    {"name": "claude-sonnet-4-6", "tier": "standard"}
  ],
  "unclassified_models": []
}

Configuration

Global Environment Variables

VariableDefaultDescription
NEXUS_DB_PATHPlatform per-user data directoryDurable SQLite job database; contains sensitive prompts and results
NEXUS_OUTPUT_LIMIT_BYTES50000Max output size in bytes before temp-file spillover
NEXUS_TIMEOUT_SECONDS600Subprocess timeout in seconds (10 minutes)
NEXUS_TOOL_TIMEOUT_SECONDS900Tool-level timeout in seconds (15 minutes); set to 0 to disable
NEXUS_RETRY_MAX_ATTEMPTS3Max attempts including the first (set to 1 to disable retries)
NEXUS_RETRY_BASE_DELAY2.0Base seconds for exponential backoff
NEXUS_RETRY_MAX_DELAY60.0Maximum seconds to wait between retries
NEXUS_CLI_DETECTION_TIMEOUT30Timeout in seconds for CLI binary version detection at startup
NEXUS_EXECUTION_MODEdefaultGlobal execution mode (default or yolo)

Per-Runner Environment Variables

Pattern: NEXUS_{AGENT}_{KEY} (agent name uppercased). Per-runner values override global values.

Valid {AGENT} values: CLAUDE, CODEX, OPENCODE, OPENCODE_SERVER

Variable patternExampleDescription
NEXUS_{AGENT}_MODELNEXUS_CODEX_MODEL=gpt-5.2Default model for this runner
NEXUS_{AGENT}_MODELSNEXUS_CODEX_MODELS=gpt-5.2,gpt-5.4-miniComma-separated model list (surfaced in server instructions)
NEXUS_{AGENT}_TIMEOUTNEXUS_CODEX_TIMEOUT=900Subprocess timeout override
NEXUS_{AGENT}_OUTPUT_LIMITNEXUS_CODEX_OUTPUT_LIMIT=100000Output limit override
NEXUS_{AGENT}_MAX_RETRIESNEXUS_CLAUDE_MAX_RETRIES=5Max retry attempts override
NEXUS_{AGENT}_RETRY_BASE_DELAYNEXUS_CLAUDE_RETRY_BASE_DELAY=1.0Backoff base delay override
NEXUS_{AGENT}_RETRY_MAX_DELAYNEXUS_OPENCODE_RETRY_MAX_DELAY=30.0Backoff max delay override
NEXUS_{AGENT}_EXECUTION_MODENEXUS_CODEX_EXECUTION_MODE=yoloExecution mode override

Invalid per-runner values are silently ignored (the global or hardcoded default is used instead).

Development

Testing

This project follows Test-Driven Development (TDD) with strict Red→Green→Refactor cycles.

# Run all tests
uv run pytest

# Run with coverage report
uv run pytest --cov=nexus_mcp --cov-report=term-missing

# Run specific test types
uv run pytest -m integration           # Integration tests (requires CLIs)
uv run pytest -m "not integration"     # Unit tests only
uv run pytest -m "not slow"            # Skip slow tests

# Run specific test file
uv run pytest tests/unit/runners/test_codex.py

Test markers:

  • @pytest.mark.integration — requires real CLI installations
  • @pytest.mark.slow — tests taking >1 second

Code Quality

All quality checks run automatically via pre-commit hooks. Run manually:

# Lint and format
uv run ruff check .              # Check for issues
uv run ruff check --fix .        # Auto-fix issues
uv run ruff format .             # Format code

# Type checking (strict mode)
uv run mypy src/nexus_mcp

# Run all pre-commit hooks manually
uv run pre-commit run --all-files

Adding Dependencies

uv add <package>              # Production dependency
uv add --dev <package>        # Development dependency
uv sync                       # Sync environment after changes

Tool Configuration

  • Ruff: line length 100, 17 rule sets (E/F/I/W + UP/FA/B/C4/SIM/RET/ICN/TID/TC/ISC/PTH/TD/NPY) — pyproject.toml → [tool.ruff]
  • Mypy: strict mode, all type annotations required — pyproject.toml → [tool.mypy]
  • Pytest: asyncio_mode = "auto", no @pytest.mark.asyncio needed — pyproject.toml → [tool.pytest.ini_options]
  • Pre-commit: ruff-check, ruff-format, mypy, trailing-whitespace, end-of-file-fixer — .pre-commit-config.yaml

Python 3.13+ Syntax

  • type keyword for type aliases: type AgentName = str
  • Union syntax: str | None (not Optional[str])
  • match statements for complex conditionals
  • NO from __future__ import annotations

Project Structure

nexus-mcp/
├── src/nexus_mcp/
│   ├── __main__.py          # Entry point
│   ├── core/                # Framework- and provider-independent domain contracts
│   ├── backends/            # Typed backend protocols and runtime registry
│   ├── jobs/                # Job service, worker, SQLite store, and migrations
│   ├── legacy/              # Temporary adapter over existing CLI runners
│   ├── mcp/                 # FastMCP transport adapter
│   │   ├── server.py        # Server, compatibility tools, and registration
│   │   ├── job_tools.py     # Typed durable agent_* tools
│   │   ├── runtime.py       # MCP lifespan ownership for job runtime services
│   │   └── prompts/         # Discoverable prompt templates
│   ├── server.py            # Compatibility re-export for the MCP server
│   ├── types.py             # Compatibility request and response models
│   ├── exceptions.py        # Exception hierarchy
│   ├── config.py            # Legacy environment configuration
│   ├── process.py           # Legacy subprocess wrapper
│   ├── parser.py            # Legacy JSON-to-text output parsing
│   ├── cli_detector.py      # CLI binary detection and version checks
│   └── runners/
│       ├── base.py          # Legacy runner protocol and template method
│       ├── factory.py       # RunnerFactory
│       ├── claude.py        # ClaudeRunner
│       ├── codex.py         # CodexRunner
│       ├── opencode.py      # OpenCodeRunner
│       └── opencode_server.py # OpenCode server runner
├── tests/
│   ├── unit/               # Fast, mocked tests
│   │   └── prompts/        # Prompt template tests
│   ├── e2e/                # End-to-end MCP protocol tests
│   ├── integration/        # Real CLI tests
│   └── fixtures.py         # Shared test utilities
├── .github/
│   └── workflows/          # CI, security, dependabot
├── pyproject.toml          # Dependencies + tool config
└── .pre-commit-config.yaml # Git hooks configuration

Releases

Stable releases are cut by running the Tag Release workflow from the Actions tab and choosing a bump (auto infers it from Conventional Commits since the last tag). Pre-releases are tagged manually. See RELEASE.md for the full maintainer workflow, recovery steps, and notes on server.json placeholder fields.

License

MIT