MCP¶
The MCP gateway uses FastMCP 3.x to expose LOOM workers, pipelines, query backends, Workshop operations, and session management as MCP tools.
Server¶
server ¶
MCP server assembly — wires config, discovery, bridge, and resources.
Creates a fully configured FastMCP server from a LOOM MCP gateway
config YAML. The server exposes LOOM workers, pipelines, query backends,
and Workshop operations as MCP tools, and workspace files as MCP resources.
Usage::
from loom.mcp.server import create_server, run_stdio
mcp, gateway = create_server("configs/mcp/docman.yaml")
run_stdio(mcp, gateway)
See Also
loom.mcp.config — config loading and validation loom.mcp.discovery — tool definition generation loom.mcp.bridge — NATS call dispatch loom.mcp.resources — workspace resource exposure loom.mcp.workshop_discovery — Workshop tool definitions loom.mcp.workshop_bridge — Workshop direct dispatch
ToolEntry
dataclass
¶
Registry entry linking an MCP tool name to its dispatch info.
MCPGateway
dataclass
¶
MCPGateway(config: dict[str, Any], bridge: MCPBridge, tool_registry: dict[str, ToolEntry] = dict(), tool_defs: list[dict[str, Any]] = list(), resources: WorkspaceResources | None = None, workshop_bridge: WorkshopBridge | None = None, session_bridge: SessionBridge | None = None, requires_bus: bool = True)
Holds all state for a running MCP gateway.
create_server ¶
Create a FastMCP server and MCPGateway from a config file.
Returns:
| Type | Description |
|---|---|
FastMCP
|
Tuple of (FastMCP, MCPGateway). |
MCPGateway
|
The gateway must be connected before the server can handle calls. |
Source code in src/loom/mcp/server.py
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | |
run_stdio ¶
Run the MCP server on stdio transport (blocking).
Source code in src/loom/mcp/server.py
run_streamable_http ¶
run_streamable_http(server: FastMCP, gateway: MCPGateway, host: str = '127.0.0.1', port: int = 8000) -> None
Run the MCP server on streamable HTTP transport (blocking).
FastMCP handles all Starlette/uvicorn setup internally.
Source code in src/loom/mcp/server.py
Bridge¶
bridge ¶
MCP-to-NATS call bridge.
Converts MCP tool invocations into LOOM messages (TaskMessage or
OrchestratorGoal), publishes them to the NATS bus, waits for the
corresponding TaskResult, and returns the output.
The bridge is transport-agnostic — it accepts any MessageBus
implementation (NATSBus for production, InMemoryBus for testing).
Three call patterns:
call_worker— direct worker dispatch vialoom.tasks.incomingcall_pipeline— pipeline goal vialoom.goals.incomingcall_query— worker dispatch withactionfield in payload
BridgeError ¶
Bases: Exception
Raised when a bridge call fails.
BridgeTimeoutError ¶
Bases: BridgeError
Raised when a bridge call times out.
MCPBridge ¶
Bridges MCP tool calls to the LOOM actor mesh via NATS.
Holds a MessageBus connection and provides three dispatch methods
corresponding to the three MCP tool kinds: worker, pipeline, query.
Source code in src/loom/mcp/bridge.py
connect
async
¶
close
async
¶
call_worker
async
¶
call_worker(worker_type: str, tier: str, payload: dict[str, Any], timeout: float = 60) -> dict[str, Any]
Dispatch a task to a LOOM worker and wait for the result.
Publishes a TaskMessage to loom.tasks.incoming (where the
router picks it up) and subscribes to the result subject.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
worker_type
|
str
|
Worker name (matches worker config |
required |
tier
|
str
|
Model tier (local, standard, frontier). |
required |
payload
|
dict[str, Any]
|
Tool arguments, validated against worker input_schema. |
required |
timeout
|
float
|
Seconds to wait for result. |
60
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
The worker's output dict. |
Raises:
| Type | Description |
|---|---|
BridgeError
|
If the worker returns a failed result. |
BridgeTimeoutError
|
If no result arrives within timeout. |
Source code in src/loom/mcp/bridge.py
call_pipeline
async
¶
call_pipeline(goal_context: dict[str, Any], timeout: float = 300, progress_callback: Callable[[str, int, int], Any] | None = None) -> dict[str, Any]
Submit an OrchestratorGoal and wait for the pipeline result.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
goal_context
|
dict[str, Any]
|
Dict of context fields (e.g. |
required |
timeout
|
float
|
Seconds to wait for the full pipeline. |
300
|
progress_callback
|
Callable[[str, int, int], Any] | None
|
Optional |
None
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
The pipeline's final output dict (all stage outputs). |
Source code in src/loom/mcp/bridge.py
call_query
async
¶
call_query(worker_type: str, action: str, payload: dict[str, Any], timeout: float = 30) -> dict[str, Any]
Dispatch a query action to a LOOM query worker.
Wraps the payload with an action field and dispatches as a
regular worker task.
Source code in src/loom/mcp/bridge.py
Config¶
config ¶
MCP gateway configuration loading and validation.
Defines the YAML config structure for exposing LOOM workers, pipelines,
query backends, and Workshop operations as MCP tools. Follows the same
validation pattern as loom.core.config.
Example config::
name: "docman"
description: "Document processing and querying"
nats_url: "nats://localhost:4222"
tools:
workers:
- config: "configs/workers/doc_classifier.yaml"
name: "classify_document"
description: "Classify a document by type"
tier: "local"
pipelines:
- config: "configs/orchestrators/doc_pipeline.yaml"
name: "process_document"
description: "Full document processing pipeline"
queries:
- backend: "docman.backends.duckdb_query.DocmanQueryBackend"
backend_config:
db_path: "/tmp/workspace/docman.duckdb"
actions: ["search", "filter", "stats", "get"]
name_prefix: "docman"
resources:
workspace_dir: "/tmp/workspace"
patterns: ["*.pdf", "*.json"]
load_mcp_config ¶
Load and validate an MCP gateway config YAML.
Raises:
| Type | Description |
|---|---|
ConfigValidationError
|
If the config has structural errors. |
FileNotFoundError
|
If the config file doesn't exist. |
YAMLError
|
If the file contains invalid YAML. |
Source code in src/loom/mcp/config.py
validate_mcp_config ¶
Validate an MCP gateway config dict.
Returns a list of error strings (empty = valid).
Source code in src/loom/mcp/config.py
Discovery¶
discovery ¶
Tool discovery — introspect LOOM configs and generate MCP tool definitions.
Reads worker YAML configs, pipeline configs, and query backend classes to
produce MCP Tool objects with correct inputSchema definitions.
This is the core of the zero-code MCP exposure: LOOM configs already
contain names, descriptions, and JSON Schema contracts — this module
reshapes them into the MCP format.
Three discovery functions correspond to the three tool sources:
discover_worker_tools— one tool per worker configdiscover_pipeline_tools— one tool per pipeline configdiscover_query_tools— one tool per query action
make_tool ¶
Build an MCP-compatible tool definition dict.
Source code in src/loom/mcp/discovery.py
discover_worker_tools ¶
Generate MCP tool definitions from worker config entries.
Each entry in worker_entries is a dict from the MCP gateway config::
{ "config": "path/to/worker.yaml", "name": "override", ... }
The worker YAML provides name, input_schema, and system_prompt.
MCP config entries can override name and description.
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
List of tool definition dicts (one per worker entry). |
Source code in src/loom/mcp/discovery.py
discover_pipeline_tools ¶
Generate MCP tool definitions from pipeline config entries.
The entry input schema is computed from the first stage's
input_mapping: keys whose source path starts with
goal.context. become the tool's input properties.
For example, input_mapping: {file_ref: "goal.context.file_ref"}
produces inputSchema: {type: object, required: [file_ref],
properties: {file_ref: {type: string}}}.
Source code in src/loom/mcp/discovery.py
discover_query_tools ¶
Generate MCP tool definitions from query backend entries.
Each query backend is instantiated and its _get_handlers() method
is called to discover available actions. Per-action input schemas
are generated from the backend's configuration (filter_fields,
stats_groups, id_column, etc.).
Source code in src/loom/mcp/discovery.py
Resources¶
resources ¶
Workspace resource exposure for MCP.
Exposes files in a LOOM workspace directory as MCP resources with
workspace:/// URIs. Tracks file modification times to detect
changes after tool calls and emit MCP resource change notifications.
Also listens on the NATS loom.resources.changed subject for
external change notifications from LOOM workers (future use).
WorkspaceResources ¶
Manages workspace files as MCP resources.
Scans the workspace directory and exposes matching files as
resources with workspace:///filename URIs. Maintains an
mtime cache to detect changes between tool calls.
Source code in src/loom/mcp/resources.py
list_resources ¶
Scan workspace and return MCP resource descriptors.
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
List of dicts matching |
list[dict[str, Any]]
|
|
Source code in src/loom/mcp/resources.py
read_resource ¶
Read a workspace resource by URI.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
uri
|
str
|
A |
required |
Returns:
| Type | Description |
|---|---|
str | bytes
|
Tuple of (content, mimeType). Text files return string content; |
str | None
|
binary files return raw bytes. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the URI scheme is wrong or the file is outside the workspace (path traversal). |
FileNotFoundError
|
If the file does not exist. |
Source code in src/loom/mcp/resources.py
detect_changes ¶
Detect workspace file changes since the last check.
Compares current mtimes against the cache. Returns a list of
workspace:/// URIs for new or modified files.
Call this after each tool invocation to determine which resources changed.
Source code in src/loom/mcp/resources.py
Session Bridge¶
session_bridge ¶
Session bridge — in-process dispatch for session management MCP tools.
Provides session lifecycle operations (start, end, status, sync-check, sync) as MCP tool actions. Operations execute git commands and file checks via subprocess — no NATS required.
Configuration is passed from the MCP gateway YAML tools.session section:
.. code-block:: yaml
tools:
session:
framework_dir: /path/to/framework
workspace_dir: /path/to/baft/itp-workspace
baft_dir: /path/to/baft
nats_url: nats://localhost:4222
ollama_url: http://localhost:11434
enable: [start, end, status, sync_check, sync]
SessionBridgeError ¶
Bases: Exception
Raised when a session bridge operation fails.
SessionBridge ¶
SessionBridge(*, framework_dir: str | Path, workspace_dir: str | Path, baft_dir: str | Path | None = None, nats_url: str = 'nats://localhost:4222', ollama_url: str = 'http://localhost:11434')
Dispatch session management operations in-process.
Source code in src/loom/mcp/session_bridge.py
dispatch
async
¶
Route to the appropriate session handler.
Source code in src/loom/mcp/session_bridge.py
Session Registry¶
session_registry ¶
Lightweight session registry for MCP session tools.
File-based session markers in ~/.loom/sessions/. Compatible with
baft.sessions — both read/write the same marker files.
get_active_sessions ¶
Return active session dicts (non-stale markers).
Source code in src/loom/mcp/session_registry.py
register_session ¶
Write or update a session marker.