Shared local MCP infrastructure for developers running multiple AI coding agents.
Irigate is a loopback-only MCP broker. It lets local agent sessions share explicitly qualified stdio MCP servers—including Windows-native servers launched from WSL—while reducing duplicate processes and providing metadata-only runtime reports, one audit record for every completed or rejected tool call, and local start-scoped MCP request/response logs.
Feature overview
| Capability | Capability | ||
|---|---|---|---|
| ⌁ | One local MCP endpoint Connect Hermes, Claude Code, Codex, and other Streamable HTTP clients to the same loopback broker. |
⟲ | Connection-preserving reloads Apply profile changes in the background without disconnecting active AI-agent sessions. |
| ◈ | Selective process reuse Share qualified stdio servers across compatible sessions and restart only upstreams whose configuration changed. |
⛨ | Fail-closed sharing Keep upstreams isolated by default; sharing requires explicit opt-in and an upstream-specific qualifier. |
| ⎇ | Exact namespaced routing Expose deterministic <upstream>__<tool> names and reject ambiguous or unknown routes. |
◎ | Session isolation Scope non-shareable workers to downstream sessions so context-bound state never leaks across agents. |
| ⚡ | Explicit concurrency Choose serial or parallel execution per upstream, with independent queues and bounded call timeouts. |
◷ | Bounded lifecycle Shut down each idle upstream on its configured timeout, restart it on demand, and terminate children without leaving orphans. |
| ◇ | Layered observability Keep reports and audit output metadata-only; inspect full local tool requests and responses only through protected rotating logs. |
⚖ | Measured compatibility Run qualification, multi-client compatibility checks, and repeatable 1/5/20-client resource benchmarks. |
| ⌘ | Direct CLI tool calls Invoke one namespaced MCP tool from automation without starting the HTTP listener. |
▦ | Process and usage inspection Use irigate status for the broker process and irigate ps for upstream state and per-agent usage. |
| ⊞ | First-class WSL/Windows bridging Run Windows-native MCP servers from a long-lived WSL broker with fresh interop selection and explicit path conversion. |
⋯ | Progressive tool disclosure Inspect upstreams, brief tool metadata, and one exact schema before calling a tool without registering the full catalog. |
Development highlights
Per-session inputs let a client supply an approved value when starting an isolated upstream. The currently supported input is a required directory named workspace:
- Profiles can declare one reserved, required
workspaceinput withtype: directory, a non-emptyallowed_rootslist, and exactly one standalone placeholder.{workspace}remains valid; ordered forms such as{filesystem.workspace|github.workspace|workspace}choose the first supplied source. - Each
allowed_rootsentry authorizes that directory and all of its subdirectories. Profile-side leading~and${ENV_NAME}references are expanded under strict rules; environment-derived roots must be absolute. - Workspace authorization resolves the requested directory and each literal pattern prefix canonically before matching. Traversal, sibling-prefix confusion, nonexistent paths, files, and symlink escapes fail closed.
- A valid scoped or global input is fixed for the downstream MCP session, rendered into each selected worker's local argument list, and included in each isolated worker's non-raw context fingerprint. One
workspace=query value can therefore feed multiple upstreams that list it as a fallback.
Input values are accepted only when a positively selected upstream or exact tool references that source in its placeholder. Bare and reverse-only URLs cannot supply dynamic inputs, unused sources fail closed, and workspace values never enter audit records or runtime reports.
Problem hypothesis
MCP-capable coding agents commonly start stdio MCP servers as child processes. Several concurrent Hermes, Claude Code, Codex, or worker sessions can therefore start duplicate copies of the same expensive server.
A broker may improve this when:
- Several agent sessions run concurrently.
- An upstream has meaningful startup or memory cost.
- The upstream is safe to share between sessions.
- The sessions use compatible state, workspace, and credential contexts.
Sharing is not universally safe. Some MCP servers retain client-specific state, and distinct workspaces or credentials may still require separate instances. Irigate must prove the benefit per upstream rather than assuming every N×M process set can collapse to M.
Requirements
- Python 3.11 through 3.14.
uvfor the recommended isolated installation and repository execution.- Node.js with
npxfor the Context7 upstream inprofiles/mvp.yaml. - An installed
code-review-graphexecutable for the isolated code-review-graph upstream.
The default profile defines real MCP upstreams. An upstream starts only when an agent selects it; its first selected use may download pinned or current package artifacts and require network access.
Installation
Install the published PyPI release as an isolated user application with uv:
uv tool install irigate
irigate --version
Or install it with pip in a virtual environment:
python3 -m venv ~/.venvs/irigate
~/.venvs/irigate/bin/python -m pip install irigate
~/.venvs/irigate/bin/irigate --version
uv provides the irigate console script from its tool bin directory. Ensure that directory—normally ~/.local/bin—is on PATH; run uv tool update-shell if needed. See the changelog for release highlights and upgrade notes.
Development installation
When working from a repository checkout—either to develop Irigate itself or to test a local change—pick the mode that matches how you intend to use the checkout.
Live edit mode (--editable)
uv tool install --editable .
This installs a .pth shim that points back at src/ in the checkout. Source edits take effect the next time you invoke irigate; no reinstall is needed. Use this when developing or debugging Irigate.
Snapshot mode (--force --from .)
uv tool install --force --from . irigate
This builds a regular installation from the checkout and copies the package into uv's tool environment. The installed tool is frozen at the current source state; later edits are invisible until you reinstall. Use this to exercise the checkout as a release-like build without allowing local edits to affect the running tool.
When to choose which
| Goal | Mode |
|---|---|
| Install the official PyPI release | uv tool install irigate |
| Developing or debugging Irigate | Live edit (--editable .) |
| Trying the current checkout as a release-like build | Snapshot (--force --from . irigate) |
| Switching back to live edits after a snapshot install | uv tool install --editable . (overwrites the snapshot) |
| Returning to the official PyPI release after a checkout install | uv tool install --force irigate |
The --force flag in snapshot mode matters when Irigate is already installed in that tool environment: it forces replacement instead of skipping the existing installation. Omit it on a clean install. Remove either installation with uv tool uninstall irigate.
Project environment
Tool installation does not install test dependencies into the checkout. Create the locked project environment before running the test suite or repository commands:
uv sync --frozen
Run the checkout and confirm that the default profile loads without starting upstream processes:
uv run --frozen irigate --help
uv run --frozen irigate --config profiles/mvp.yaml --check
For source checkout setup, repository checks, and release-artifact verification, see DEVELOPMENT.md. Maintainers should use docs/RELEASING.md for PyPI trusted-publisher setup and tag-driven releases.
Website
The public website and documentation are maintained in the site/ git submodule, backed by irigate/irigate.github.io. The website repository owns Astro dependencies, content, verification, GitHub Pages deployment, and custom-domain operations. This source repository remains authoritative for broker behavior, architecture, benchmark evidence, and brand source assets.
Initialize the website after cloning this repository:
git submodule update --init site
Website changes require two commits: commit and push inside site/ first, then commit the updated site pointer in this repository.
Configuration
Irigate reads ~/.config/irigate/config.yaml by default. Set IRIGATE_CONFIG to use another file, or pass --config; the command-line argument takes precedence over the environment variable. Profiles are validated before any upstream process starts: unknown fields, duplicate YAML keys, unsupported transports, invalid routing keys, non-loopback listeners, and missing environment references are rejected.
name: local
host: 127.0.0.1
port: 8765
upstreams:
context7:
description: Resolve library identifiers and query current library documentation.
transport: stdio
command: npx
args: ["-y", "@upstash/context7-mcp"]
env:
CONTEXT7_API_KEY: ${CONTEXT7_API_KEY}
LOG_LEVEL: info
shareable: true
qualifier: context7-readonly-v3
concurrency: serial
call_timeout_seconds: 30
idle_timeout_seconds: 300
failure_threshold: 5
crash_threshold: 2
Broker fields
| Field | Required | Default | Contract |
|---|---|---|---|
name |
Yes | — | Profile identifier using lowercase letters, digits, and hyphens. It labels --check output and runtime reports so operators can distinguish configurations; it does not affect routing. |
host |
No | 127.0.0.1 |
Listener address. Only localhost or an IP loopback address is accepted. |
port |
No | 8765 |
Streamable HTTP listener port, from 1 through 65535. |
runtime_report_path |
No | ${XDG_STATE_HOME:-~/.local/state}/irigate/<name>/runtime-report.json |
Exact JSON report destination override. Relative paths are anchored to the profile directory. The file contains process statistics plus validated agent labels, never payloads or credentials. |
runtime_log_path |
No | ~/.local/log/irigate/<name>/ |
Exact directory for protected start-scoped MCP payload logs. Relative paths are anchored to the profile directory. Changing it requires a broker restart. |
upstreams |
Yes | — | Non-empty mapping of routing keys to stdio upstream definitions. |
Required broker-field example
name and upstreams belong in the selected broker profile. They are not HTTP query parameters, and Irigate does not accept --name or --upstreams CLI flags. The smallest useful profile includes both required broker fields and the two required fields for each upstream:
name: local-development
upstreams:
context7:
command: npx
args: ["-y", "@upstash/context7-mcp"]
idle_timeout_seconds: 300
Choose name as a stable identifier for where or why the profile runs, such as local-development, project-docs, or benchmark. It appears in validation output and runtime reports but does not change routing. Choose each upstream key, such as context7, as the stable tool namespace: the key becomes the prefix in <upstream-key>__<tool-name>. Add every MCP server Irigate should be able to expose under upstreams; at least one entry is required.
Save that profile as ~/.config/irigate/config.yaml to use it by default:
mkdir -p ~/.config/irigate
cp profiles/local.yaml ~/.config/irigate/config.yaml
uv run --frozen irigate --check
uv run --frozen irigate
The first command validates the required fields without starting upstreams. The second starts the broker with profile name local-development and the configured context7 upstream.
For a different profile, set its full path in the environment or override it for one invocation:
export IRIGATE_CONFIG="$HOME/.config/irigate/work.yaml"
uv run --frozen irigate --check
uv run --frozen irigate --config profiles/benchmark-heavy.yaml --check
If either broker field is absent, Irigate exits before starting the HTTP listener and writes an actionable configuration error to stderr with a minimal example.
An upstream key becomes the prefix in every exposed <upstream-key>__<tool-name> route. Keys must start with a lowercase letter and may contain lowercase letters, digits, and hyphens.
Upstream fields
| Field | Required | Default | Contract |
|---|---|---|---|
description |
No | — | Short operator-authored purpose used by the optional progressive-disclosure catalog. It never starts the upstream. |
transport |
No | stdio |
Only stdio is supported. |
command |
Yes | — | One executable token. Put command arguments in args. |
args |
No | [] |
Argument list. Environment references and credentials are not accepted. An upstream with inputs.workspace must contain exactly one standalone workspace placeholder. Pipe-separated sources are checked left to right and rendered from the canonical session input before process startup. |
execution |
No | native |
Process-launch environment. Use explicit wsl-windows for a Windows executable launched from WSL; Irigate refreshes the per-session WSL interop endpoint before every spawn. |
wsl_path_arguments |
No | {} |
Explicit tool-to-JSON-pointer map accepted only for wsl-windows. POSIX absolute strings at * and exact-tool pointers are converted with wslpath -w immediately before forwarding. |
cwd |
No | Inherit broker directory | Working directory for the stdio process. Migration resolves relative agent paths against the owning user or project directory. |
env |
No | {} |
Child environment mapping. Values may be literal strings or explicit ${BROKER_ENV_NAME} references. Use references for credentials so secret values stay out of the profile. |
inputs |
No | {} |
Per-session input declaration. Only a required workspace directory is supported. It needs non-empty allowed_roots, requires shareable: false, and is immutable after the MCP session is established. |
shareable |
No | false |
Requests one process shared across downstream sessions. Sharing is admitted only by a registered qualifier. |
qualifier |
Conditional | — | Required when shareable: true; rejected otherwise. Currently registered: context7-readonly-v3 for the context7 key. |
concurrency |
No | serial |
serial executes one call at a time; parallel permits concurrent calls within the worker. |
call_timeout_seconds |
No | 30 |
Per-call timeout greater than 0 and no more than 3600 seconds. It does not control process idleness. |
idle_timeout_seconds |
Yes | — | Per-process inactivity TTL greater than 0 and no more than 86400 seconds. |
failure_threshold |
No | 5 |
Error count from 1 through 100 that degrades a shared upstream. |
crash_threshold |
No | 2 |
Crash count from 1 through 100 that degrades a shared upstream. |
Each spawned worker tracks its own idle timeout. A worker shuts down only when its TTL expires with no queued or active calls. Shared and session-isolated workers expire independently, and the next routed call starts a fresh process in the same effective sharing mode. A long-running call remains governed by call_timeout_seconds, not by the idle timeout.
MCP application errors remain tool-level error results and count as call failures rather than process crashes. Connection-closed and session-terminated errors remain transport failures, so crash degradation still reflects lost upstream processes.
Long-running WSL brokers must mark Windows-native upstreams explicitly. WSL creates per-session interop sockets and removes them when their owning shell exits, so a broker that reuses its startup environment can later fail with Broken pipe. execution: wsl-windows selects the newest live /run/WSL/<pid>_interop socket for each process spawn. If no live endpoint exists, Irigate returns an actionable restart error without exposing the command or child environment.
upstreams:
pencil:
command: "/mnt/c/Users/<Windows-user>/AppData/Local/Programs/Pen/resources/app.asar.unpacked/out/mcp-server-windows-x64.exe"
args: ["--app", "desktop"]
execution: wsl-windows
wsl_path_arguments:
# filePath is the open .pen document path used across Pencil tools.
"*": ["/filePath"]
# These are exact Pencil MCP tool names and request fields.
export_html: ["/outputPath"]
export_nodes: ["/outputDir"]
env: {}
shareable: false
concurrency: serial
idle_timeout_seconds: 300
Replace <Windows-user> with the Windows account directory under /mnt/c/Users. wsl_path_arguments is opt-in and valid only with execution: wsl-windows. Its keys are exact upstream MCP tool names or *; its values are JSON Pointers into request objects, not filesystem directories. export_html and export_nodes are Pencil MCP tools, while outputPath and outputDir are fields in those tools' request schemas.
The actual paths arrive in MCP calls. For example, /home/<WSL-user>/designs/dashboard.pen converts to \\wsl.localhost\\<distribution>\\home\\<WSL-user>\\designs\\dashboard.pen. /mnt/c/Users/<Windows-user>/Documents/pencil-export/dashboard.html converts to C:\\Users\\<Windows-user>\\Documents\\pencil-export\\dashboard.html. These are caller-selected examples; Irigate neither creates nor selects those directories in the profile.
Missing pointers, empty strings, Windows drive paths, UNC paths, and other non-POSIX strings pass through unchanged. Slash-prefixed Windows drive paths such as /C:/Users/example/design.pen are normalized directly rather than sent through wslpath. A configured pointer that resolves to a non-string rejects the call. Irigate converts a rendered {workspace} process argument automatically for Windows-native upstreams. Protected MCP payload logs retain the original client-facing arguments; only the worker-boundary copy is transformed.
Literal strings are passed directly to the child process. ${ENV_NAME} values are resolved from the broker process without being written into the profile, audit log, runtime report, or validation output:
export CONTEXT7_API_KEY='...'
uv run --frozen irigate --config profiles/local.yaml --check
Migrate installed agent configurations
irigate migrate discovers user and current-project MCP configurations for Claude Code, Codex CLI, Cursor, Gemini CLI, and Hermes. It presents an interactive numbered selection, moves each selected stdio server into the Irigate profile, and replaces those direct server entries with one agent-specific Streamable HTTP connection to Irigate. Existing remote MCP entries and unrelated agent settings remain in place.
uv run --frozen irigate migrate --config ~/.config/irigate/config.yaml
uv run --frozen irigate migrate --all --config ~/.config/irigate/config.yaml
uv run --frozen irigate migrate ~/.cursor/mcp.json --config ~/.config/irigate/config.yaml
Providing a file bypasses discovery and migrates only that file. Non-interactive use requires either a file or --all. Existing files receive adjacent .irigate.bak backups before replacement; a pre-existing backup stops the migration rather than overwriting evidence. New upstreams default to isolated, serial execution with a 30-second call timeout and 300-second idle timeout. Existing remote MCP servers are not moved.
Agent-local environment values are never copied into the Irigate profile. Every migrated child environment variable becomes an explicit ${ENV_NAME} broker reference, and that variable must already be exported in the environment running the migration. The same environment must be available when Irigate runs. Conflicting normalized upstream names or unsupported configuration shapes fail before any file changes.
While serving, Irigate watches the selected profile. Changed active upstreams must initialize successfully before routing switches. Added and changed dormant upstreams remain stopped until selected. Invalid updates leave the last valid active configuration available. Changes to name, host, port, runtime_report_path, or runtime_log_path require restarting the broker.
Run
Start the broker in the foreground with strict sharing admission:
uv run --frozen irigate \
--config profiles/mvp.yaml \
--require-qualified-sharing
Inspect the installed version and all available commands:
uv run --frozen irigate --version
uv run --frozen irigate --help
The root help lists status, reload, stop, and systemd; the reload and stop help output also identifies the running Irigate version so a stale installed executable is visible immediately.
The broker listens at http://127.0.0.1:8765/mcp without starting upstreams. A client may use the bare URL to expose all configured upstreams, or add a selector to narrow the set. Qualification, schema discovery, and process startup happen on first use. Each upstream's idle_timeout_seconds shuts down that process independently after inactivity; the next routed call starts a fresh process without changing the downstream session.
systemd user service
On a host with a running systemd user manager, install the selected profile as the fixed irigate.service unit:
irigate systemd setup --config ~/.config/irigate/config.yaml
Setup writes ~/.config/systemd/user/irigate.service and ~/.config/irigate/irigate.env, then runs systemctl --user daemon-reload and systemctl --user enable --now irigate.service. The unit starts the same Python environment that ran setup and executes python -m irigate --config <selected-profile>. Its ExecReload invokes the existing connection-preserving irigate reload path.
The environment file is mode 0600 and contains only the current values for names explicitly referenced by upstream env fields. It never contains literal profile values, command arguments, or values in CLI output. After changing an exported credential or another referenced environment value, synchronize it from a shell where every required value is set:
irigate systemd sync --config ~/.config/irigate/config.yaml
sync rewrites both files, reloads the user manager, and restarts the service only when it is already active. A connection-preserving reload cannot refresh a running process's environment; use sync for changed environment values and irigate systemd reload for a profile-only reload through the installed unit. Run systemd setup first when the service has not been enabled.
Use systemd for normal service lifecycle operations:
# Inspect state and follow the service journal.
systemctl --user status irigate.service
journalctl --user -u irigate.service -f
# Start, stop, or restart the installed broker.
systemctl --user start irigate.service
systemctl --user stop irigate.service
systemctl --user restart irigate.service
# Start automatically at user-login, or disable and stop it.
systemctl --user enable --now irigate.service
systemctl --user disable --now irigate.service
systemctl --user reload irigate.service and irigate systemd reload --config ~/.config/irigate/config.yaml both use the unit's ExecReload and preserve active connections when the updated profile is valid. Use restart when changing startup-bound fields such as host, port, runtime paths, or after upgrading Irigate. irigate stop is for a foreground broker; use systemctl --user stop for the managed service so systemd records its state correctly.
The fixed irigate.service represents one selected profile. Running setup or sync with a different --config replaces the unit's profile. To remove the managed service, disable it, delete ~/.config/systemd/user/irigate.service and ~/.config/irigate/irigate.env, then run systemctl --user daemon-reload.
Agent-side selection
The agent URL does not repeat the broker profile's required name and upstreams fields. It connects to an already-running profile. The examples below assume Irigate was started with --config profiles/mvp.yaml; that profile contains name: mvp and defines the context7 and code-review-graph upstream keys. Use exact tools for the narrowest and recommended agent configuration:
http://127.0.0.1:8765/mcp?tools=context7__resolve-library-id,context7__query-docs
Select complete upstreams by their keys from the active broker profile when the agent needs their full tool surfaces:
http://127.0.0.1:8765/mcp?upstreams=context7,code-review-graph
Prefix an upstream with ! when the agent starts that MCP server directly and wants every other configured Irigate upstream:
http://127.0.0.1:8765/mcp?upstreams=!code-review-graph
Positive and reverse selectors may be mixed. Positive names form the base set and exclusions are subtracted regardless of order:
http://127.0.0.1:8765/mcp?upstreams=context7,code-review-graph,!code-review-graph
This selects only context7. Omitting both selector parameters exposes all configured upstreams unchanged. Reverse-only selection also starts from all currently configured upstreams, so profile reloads can broaden it when a new upstream is added. Prefer tools= for least privilege. When selection is used, provide only one tools or upstreams parameter; repeated parameters, unknown names, malformed tokens, unrelated query parameters, and an empty result are rejected. Exact tool selection never supports ! because excluding one tool cannot avoid starting its upstream.
Per-session input
An upstream may declare one required directory input named workspace. This filesystem server is one example; the input contract is not specific to that server. The following profile permits the home directory and all of its subdirectories:
filesystem:
command: npx
args: ["-y", "@modelcontextprotocol/server-filesystem", "{workspace}"]
inputs:
workspace:
type: directory
required: true
allowed_roots: ["${HOME}"]
shareable: false
idle_timeout_seconds: 300
Select the upstream positively and URL-encode the absolute workspace value:
http://127.0.0.1:8766/mcp?upstreams=filesystem&filesystem.workspace=${HOME}
Here ${HOME} denotes the absolute value supplied by the client; Irigate does not expand shell syntax in query values.
An exact tool selector for the configured upstream may carry the same input. Inputs do not select upstreams by themselves: bare URLs, reverse-only selectors, excluded upstreams, missing or duplicate values, relative paths, and unknown input names are rejected before activation. The canonical mapping is bound to the first successful MCP session response; later requests for that session must present the identical mapping.
Each configured allowed_roots entry permits its canonical directory and all descendants. Profile-side leading ~ and braced ${ENV_NAME} references expand at load time under strict validation, but query values never expand shell syntax. Irigate strictly resolves the requested directory before authorization, so traversal and symlink paths are judged by their canonical destination.
Add an explicit agent= label to attribute calls in the runtime report:
http://127.0.0.1:8765/mcp?upstreams=code-review-graph&agent=codex
Agent labels are metadata, not authentication. They must be 1–64 letters, digits, dots, underscores, or hyphens and are recorded only after a valid tool call. Omitted labels are grouped as anonymous; Irigate does not guess identity from client headers.
CLI operations
Configure Windows tool paths automatically
When a profile contains one or more execution: wsl-windows upstreams, let Irigate inspect their live MCP tool schemas instead of writing wsl_path_arguments by hand:
# Diagnose only; exit 1 means the profile needs path mappings.
irigate doctor --config ~/.config/irigate/config.yaml
# Add the schema-derived mappings to the profile.
irigate doctor --config ~/.config/irigate/config.yaml --apply
doctor starts only the configured wsl-windows upstreams, reads their advertised input schemas, and recognizes string fields whose names denote file or directory paths, including filePath, outputPath, outputDir, requestFilePath, and responseFilePath. It ignores unrelated strings such as prompts, URLs, JSONPath expressions, and XPath expressions. Existing operator-authored mappings are preserved; discovered paths already covered by a * mapping are not duplicated.
Without --apply, the command does not modify the profile and returns 1 when repair is needed. With --apply, Irigate validates a temporary repaired profile, preserves the original as <profile>.irigate-doctor.bak on the first repair, then replaces the profile atomically. Comments and unrelated profile text remain intact. Re-run doctor after an upstream package upgrade because its tool schemas may have added path fields. Use --json for machine-readable findings.
Optional progressive-disclosure Agent Skill
Irigate bundles an AgentSkills-compatible integration for agents that have shell access and need only a few tools from a large catalog. Locate the installed skill and copy that directory into the explicit skill directory used by the agent:
irigate skill-path
cp -R "$(irigate skill-path)" <agent-skill-directory>/irigate-progressive
The skill uses four explicit layers:
# Layer 1: static profile metadata; no upstream startup or environment resolution
irigate upstreams --config profiles/mvp.yaml --json
# Layer 2: names and descriptions from one upstream; schemas are omitted
irigate tools --config profiles/mvp.yaml --upstream code-review-graph --json
# Layer 3: one exact namespaced schema
irigate schema --config profiles/mvp.yaml code-review-graph__get_minimal_context_tool
# Layer 4: one exact call
irigate call --config profiles/mvp.yaml \
code-review-graph__get_minimal_context_tool \
--arguments '{"repo_root":"/path/to/project","task":"review changes"}'
upstreams reads only validated metadata and does not require referenced environment values. tools, schema, and call initialize only the selected upstream and close it before returning. Separate commands therefore do not preserve an isolated upstream session; use the standard Streamable HTTP endpoint for stateful or multi-call workflows. The progressive integration deliberately keeps exact tool names visible instead of adding a generic dispatcher that would hide them from approval and audit surfaces.
Tool discovery and calls
List the exact namespaced tools available from a profile before configuring an agent:
uv run --frozen irigate tools --config profiles/mvp.yaml
The command prints one <upstream>__<tool> name per line so its output can be copied into tools=. Tool schemas are runtime metadata rather than static profile fields, so discovery temporarily starts and initializes every configured upstream, then closes all spawned processes. It may therefore download upstream packages, use the network, and require the profile's referenced environment variables. Use --check instead when only configuration validation without startup is needed.
Call a namespaced tool directly from a shell without starting the HTTP listener:
uv run --frozen irigate call --config profiles/mvp.yaml \
code-review-graph__build_or_update_graph_tool \
--arguments '{"repo_root":"/path/to/project","full_rebuild":false}'
--arguments accepts one JSON object and defaults to {}. The command prints the complete MCP CallToolResult as JSON, exits 0 for a successful tool result, 1 for an MCP or upstream error, and 2 for invalid configuration or arguments. The upstream starts only for this call and is closed before the command exits. A valid direct call creates its own MCP log file. Pass credentials through the profile's environment references, never through tool arguments.
Inspect the latest runtime snapshot in a ps/netstat-style table:
uv run --frozen irigate ps --config profiles/mvp.yaml
uv run --frozen irigate ps --config profiles/mvp.yaml --json
Each table row identifies an upstream/agent pair and shows effective mode, live process count, activity state (busy, idle, or stopped), elapsed idle time, configured idle timeout, calls, and failures. IDLE_FOR is - while busy or stopped; while idle it is calculated from the report's UTC idle_since timestamp, so it continues advancing between report writes. --json returns the complete schema-version-3 report, including activity_state, active_calls, idle_since, and idle_timeout_seconds, for automation. This command reads runtime_report_path without starting upstreams or resolving their environment references; a report left by a stopped broker retains cumulative usage from that run.
Inspect the serving process separately from its potentially stale runtime report:
uv run --frozen irigate status --config profiles/mvp.yaml
uv run --frozen irigate status --config profiles/mvp.yaml --json
For a verified running process, status reports the PID, effective host, port and MCP endpoint, canonical configuration path, runtime-report path, current start-scoped MCP log file, instance ID, package version, and UTC start time. If no live control document exists, it reports state=stopped and whether a stale runtime report or control document remains. It does not resolve upstream environment references or start upstreams.
Print the newest MCP call log for the selected profile, or follow appended JSON-line records live on stdout:
uv run --frozen irigate logs --config profiles/mvp.yaml
uv run --frozen irigate logs -f --config profiles/mvp.yaml
Every server start and valid direct call starts a new file under ~/.local/log/irigate/<profile>/ by default. Set the optional profile field runtime_log_path to use another exact directory; relative values are anchored to the profile directory. Irigate retains the newest 10 files per profile. The directory is mode 0700 and files are mode 0600. Each record contains the agent label, complete tools/call arguments, complete MCP result or raised error, and duration. These payload logs can contain sensitive tool input or output; unlike audit records and runtime reports, they are not metadata-only. logs reads no upstream environment references and starts no processes. -f follows the selected file; run it again after a new broker start to follow the new file.
Gracefully stop the server selected by the same profile:
uv run --frozen irigate stop --config profiles/mvp.yaml
Request an immediate connection-preserving profile reload:
uv run --frozen irigate reload --config profiles/mvp.yaml
Serving writes a credential-free <runtime_report_path>.control document containing the effective process, listener, configuration, report, and current log-file metadata used by status, reload, and stop. These commands treat that document as an untrusted claim: they check the selected profile and configuration path and verify that the PID is a live Irigate process. reload sends SIGHUP to wake the same atomic reload path used by profile watching; exit 0 confirms delivery, while validation or activation failures remain in the server log and leave the last valid configuration active. stop sends SIGTERM and waits for normal application and child-process cleanup. None resolves upstream environment references. A missing runtime_report_path, stale or mismatched control document, non-Irigate PID, or signal failure fails without signaling another process; stop also fails when cleanup is not observed.
An agent can combine Irigate with a directly managed MCP server:
mcp_servers:
irigate:
url: "http://127.0.0.1:8765/mcp?upstreams=!code-review-graph"
code-review-graph:
command: code-review-graph
args: [serve]
For a foreground broker, Ctrl+C remains equivalent operationally: shutdown drains active calls and closes child processes.
Strict mode rejects the first selected use of Context7 if it cannot be qualified. Omit --require-qualified-sharing to downgrade failed selected shared upstreams to isolated mode.
Run qualification without opening the client endpoint when diagnosing startup:
uv run --frozen irigate qualify --config profiles/mvp.yaml
Audit records are written as metadata-only JSON lines to stderr. Each profile atomically refreshes ${XDG_STATE_HOME:-~/.local/state}/irigate/<name>/runtime-report.json by default with metadata-only process, reuse, timing, failure, and per-agent usage counters. Tool arguments and results remain excluded from both surfaces and are retained only in the protected rotating MCP call logs.
Not part of the MVP
- Enterprise governance or compliance claims
- User identity, tenant isolation, RBAC, OAuth, or remote access
- Credentials in URLs or dynamic HTTP environment overrides
- Generic secret scanning or response rewriting
- A custom filesystem MCP server or path-guessing middleware
- SSE as the primary client transport
- OpenAI or Anthropic API proxying
- Kubernetes, a web portal, daemon management, or dynamic configuration APIs
Architecture direction
Hermes / Claude Code / Codex / test clients
│
│ MCP over Streamable HTTP on loopback
▼
┌──────────────┐
│ Irigate │
│ │
│watched config│
│ exact routing│
│ process reuse│
│ metadata log │
└──────┬───────┘
│ stdio MCP
┌─────────┼─────────┐
▼ ▼ ▼
upstream A upstream B upstream C
isolated shareable shareable
by default only after only after
testing testing
Different upstreams must progress independently. Shared instances are permitted only after transport, concurrency, and state-isolation tests pass.
Implementation
IMPLEMENTATION.md documents runtime architecture, module ownership, safety contracts, extension workflows, and verification commands.
Positioning
The current market hypothesis is documented in MARKET-RESEARCH.md.
Irigate is positioned as local AI developer infrastructure, not as a competitor to enterprise control planes such as Microsoft MCP Gateway. Microsoft focuses on Kubernetes deployment, management APIs, Entra authorization, session-aware routing, and a portal. Irigate's narrower hypothesis is workstation-local stdio process consolidation across several coding-agent products.
Repository contents
IMPLEMENTATION.md— Current architecture, contracts, extension workflows, and verification.MARKET-RESEARCH.md— Market hypothesis, measured evidence, positioning, and go/no-go criteria.assets/— Reproducible Iris-gate project mark, lockup, and raster exports.profiles/— Validated loopback-only runtime and benchmark profiles.src/irigate/— Installable package, configuration models, loader, and CLI.tests/— Executable package and runtime contracts.
License
Irigate is available under the MIT License.
Status
- Product scope: loopback-only local MCP broker.
- Validation: Context7 is qualified for shared mode; code-review-graph remains isolated.
- Evidence: process and resident-memory consolidation is established for identical Context7 contexts. Call-latency evidence remains invalid because Context7 throttled the benchmark.
- Market evidence: hypothesis only.
- Next action: rerun the latency benchmark with adequate Context7 quota and observe normal development sessions before making a release decision.
Metadata
Release files for irigate 0.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| irigate-0.5.0.tar.gz | 279.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| irigate-0.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 345.9 kB
Release files / irigate-0.5.0.tar.gz
| Download URL | irigate-0.5.0.tar.gz |
|---|---|
| Size | 279.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0046921888ddef48eaa7377e4c9af937f73af5317f552eb52c01def18ed048b2
|
|
BLAKE2b-256 checksum How to use checksums |
5c918c578199b6f6f3790a61da65ce08fa411938643f489c390a930f9270716b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 28, 2026.
Transparency logRelease files / irigate-0.5.0-py3-none-any.whl
| Download URL | irigate-0.5.0-py3-none-any.whl |
|---|---|
| Size | 66.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bcda743d6378ea7b12e26adb204865b0b7e62633e0710f98136115bc03e0c34d
|
|
BLAKE2b-256 checksum How to use checksums |
1405b8ea916c722bacdb101517b9c6e780a1490dbb2536e1627032c30240ccc3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 28, 2026.
Transparency log