# https://github.com/yusong652/yade-mcp Project Manual

Generated at: 2026-06-24 15:09:29 UTC

## Table of Contents

- [Overview and Architecture](#page-overview)
- [MCP Tools Reference](#page-tools)
- [YADE Bridge Internals](#page-bridge)
- [Knowledge Base, Search, and Deployment](#page-knowledge)

<a id='page-overview'></a>

## Overview and Architecture

### Related Pages

Related topics: [MCP Tools Reference](#page-tools), [YADE Bridge Internals](#page-bridge)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [README.md](https://github.com/yusong652/yade-mcp/blob/main/README.md)
- [yade-mcp-bridge/README.md](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/README.md)
- [src/yade_mcp/tools/execute_code.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/execute_code.py)
- [src/yade_mcp/tools/query_api.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/query_api.py)
- [src/yade_mcp/tools/browse_api.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/browse_api.py)
- [yade-mcp-bridge/src/yade_mcp_bridge/utils/response.py](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/src/yade_mcp_bridge/utils/response.py)
- [yade-mcp-bridge/src/yade_mcp_bridge/runtime/pyrunner.py](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/src/yade_mcp_bridge/runtime/pyrunner.py)
- [yade-mcp-bridge/src/yade_mcp_bridge/execution/script_runner.py](https://github.com/yusong652/yade-mcp-bridge/src/yade_mcp_bridge/src/yade_mcp_bridge/execution/script_runner.py)
- [src/yade_mcp/knowledge/resources/python_api_docs/runtime/FileGenerator.json](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/knowledge/resources/python_api_docs/runtime/FileGenerator.json)
- [src/yade_mcp/knowledge/resources/python_api_docs/runtime/MaterialContainer.json](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/knowledge/resources/python_api_docs/runtime/MaterialContainer.json)
</details>

# Overview and Architecture

## Purpose and Scope

**yade-mcp** is an [MCP (Model Context Protocol)](README.md) integration that lets AI agents drive [YADE](https://yade-dem.org) — an open-source discrete element method (DEM) simulator — directly from a chat client. The repository tagline captures the spirit: `O.engines += [LLM()]  # yet another engine.` Source: [README.md](https://github.com/yusong652/yade-mcp/blob/main/README.md).

The project solves a recurring problem in agentic DEM workflows: LLMs need a live, callable interface to YADE for code execution, simulation control, and API discovery. The repository is therefore split into **two Python packages** that share a wire protocol but live in different processes:

| Package | Role | Where it runs |
|---|---|---|
| `yade-mcp` | MCP server. Registers FastMCP tools (`yade_query_api`, `yade_browse_api`, `yade_execute_code`, `yade_execute_task`, `yade_list_tasks`, …). | Inside the agent runtime (e.g., Claude Desktop). |
| `yade-mcp-bridge` | WebSocket/HTTP server embedded in YADE. Owns the task pump, script execution, console capture, and cancellation. | Inside the YADE Python console / Qt process. |

Sources: [README.md](https://github.com/yusong652/yade-mcp/blob/main/README.md), [yade-mcp-bridge/README.md](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/README.md).

## System Architecture

The two packages are intentionally decoupled. The MCP server never imports YADE; the bridge never imports `fastmcp`. They meet on a JSON-over-WebSocket boundary exposed by the bridge, and a knowledge base of pre-scraped API JSONs sits beside the MCP server so agents can grep YADE's class hierarchy without a live round-trip.

```mermaid
flowchart LR
    subgraph Agent["Agent runtime"]
        A[LLM / Claude Desktop]
    end
    subgraph MCP["yade-mcp (MCP server)"]
        T1[yade_query_api]
        T2[yade_browse_api]
        T3[yade_execute_code]
        T4[yade_execute_task]
        KB[(python_api_docs/*.json)]
    end
    subgraph Bridge["yade-mcp-bridge (inside YADE)"]
        WS[WebSocket / HTTP server]
        Pump[Background pump thread]
        PR[PyRunner self-heal]
        SR[ScriptTask runner]
        CC[Console capture]
    end
    YADE[(YADE __main__ + O.engines)]

    A -->|MCP tool call| T1
    A --> T2
    A --> T3
    A --> T4
    T1 --> KB
    T2 --> KB
    T3 -->|JSON-RPC| WS
    T4 -->|JSON-RPC| WS
    WS --> Pump
    Pump --> SR
    Pump --> PR
    PR --> YADE
    SR --> YADE
    CC --> Pump
    Pump -->|SSE events| A
```

The bridge auto-detects whether it is loaded inside the YADE Qt GUI or a headless Python console and switches its pump between a `QTimer` tick and a blocking daemon-thread poll, so `start()` never freezes the user's REPL (a behavior introduced in **bridge-v0.2.0**). Source: [yade-mcp-bridge/README.md](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/README.md).

## Component Responsibilities

### MCP Server (`yade-mcp`)

Tools are registered through FastMCP. Each tool wraps a thin domain call into a uniform response envelope (`build_ok` / `build_error` from `yade_mcp.contracts`). Examples:

- `yade_query_api(query, limit=10)` performs keyword search across the bundled knowledge base and returns ranked matches. Source: [src/yade_mcp/tools/query_api.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/query_api.py).
- `yade_browse_api(path)` walks YADE's native class tree (e.g., `engine.GlobalEngine.NewtonIntegrator`) and returns the full class JSON for a leaf. Source: [src/yade_mcp/tools/browse_api.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/browse_api.py).
- `yade_execute_code(code, timeout_ms)` is fire-and-return; it documents three distinct failure shapes — `interrupted`, `terminated`, and `timeout` — that the agent must handle differently (added in **v0.3.0**). Source: [src/yade_mcp/tools/execute_code.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/execute_code.py).

The knowledge base lives under `src/yade_mcp/knowledge/resources/python_api_docs/runtime/` and contains 371 runtime-introspected class JSONs (last fully refreshed in **v0.2.2**). Each JSON describes a class's category, parent, attributes, and methods — e.g., `MaterialContainer` exposes `append`/`index` for material lookup, and `FileGenerator` is the base for `SimpleShear` and `TriaxialTest`. Sources: [MaterialContainer.json](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/knowledge/resources/python_api_docs/runtime/MaterialContainer.json), [FileGenerator.json](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/knowledge/resources/python_api_docs/runtime/FileGenerator.json).

### Bridge (`yade-mcp-bridge`)

The bridge owns three runtime concerns:

1. **Task pump** — a daemon thread (console mode) or Qt timer (GUI mode) that polls the task queue and dispatches `ScriptTask` instances. **bridge-v0.2.4** aligned task lifetime with cycling lifetime so that `O.run(N, wait=False)` no longer leaves orphan cycling after the task "completes". Source: [yade-mcp-bridge/README.md](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/README.md).
2. **PyRunner self-heal** — injects and re-injects a `_mcp_pyrunner_tick` engine at `O.engines[0]` so user scripts that mutate `O.engines` cannot silently disable interrupt/observation. **bridge-v0.2.2** fixed the case where IPython's `%run script.py` re-resolves `sys.modules['__main__']` and breaks the name lookup. Source: [yade-mcp-bridge/src/yade_mcp_bridge/runtime/pyrunner.py](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/src/yade_mcp_bridge/runtime/pyrunner.py).
3. **Response shaping** — `TaskDataBuilder` constructs a uniform `data` block (`task_id`, `task_type`, `script_path`, `status`, `start_time`, `elapsed_time`, …) shared with the MCP server's `data.task_status` field, keeping lifecycle metadata on the subject rather than the envelope. Source: [yade-mcp-bridge/src/yade_mcp_bridge/utils/response.py](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/src/yade_mcp_bridge/utils/response.py).

## Communication Protocol and Lifecycle

The bridge exposes:

- `POST /execute_task` / `check_task_status` / `list_tasks` / `interrupt_task` / `execute_code` / `console_history` (JSON-RPC over HTTP)
- `GET /events` — Server-Sent Events stream emitting `task_status_changed` and `console_entry`
- `GET /health` — liveness probe and runtime-mode report

The MCP server fans these out as MCP tools. **v0.2.1 / bridge-v0.2.1** moved pagination (`skip_newest`, `limit`, `filter_text`) from the server to the bridge so `check_task_status` returns an explicit `{total_lines, line_range, has_older, has_older}` envelope against the complete log file rather than a moving window. Sources: [yade-mcp-bridge/README.md](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/README.md), [yade-mcp-bridge/src/yade_mcp_bridge/execution/script_runner.py](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/src/yade_mcp_bridge/execution/script_runner.py).

The latest **bridge-v0.3.1** simplifies startup to a single banner line (`YADE MCP Bridge on ws://<host>:<port>, log: <path>`) and quiets stdout to `WARNING` so initialization INFO only lands in `bridge.log`, leaving the YADE prompt clean. It also fixes a spurious "Console capture not installed" warning. Source: [bridge-v0.3.1 release notes](https://github.com/yusong652/yade-mcp/releases/tag/bridge-v0.3.1).

## See Also

- [Tools Reference](Tools-Reference.md) — per-tool argument and response shapes
- [Bridge Internals](Bridge-Internals.md) — PyRunner, task pump, cancellation
- [Knowledge Base](Knowledge-Base.md) — how the API JSON corpus is generated and refreshed

---

<a id='page-tools'></a>

## MCP Tools Reference

### Related Pages

Related topics: [YADE Bridge Internals](#page-bridge), [Knowledge Base, Search, and Deployment](#page-knowledge)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [src/yade_mcp/tools/__init__.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/__init__.py)
- [src/yade_mcp/tools/browse_api.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/browse_api.py)
- [src/yade_mcp/tools/query_api.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/query_api.py)
- [src/yade_mcp/tools/execute_code.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/execute_code.py)
- [src/yade_mcp/tools/execute_task.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/execute_task.py)
- [src/yade_mcp/tools/check_task_status.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/check_task_status.py)
- [src/yade_mcp/tools/list_tasks.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/list_tasks.py)
- [src/yade_mcp/tools/interrupt_task.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/interrupt_task.py)
- [yade-mcp-bridge/src/yade_mcp_bridge/handlers/tasks.py](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/src/yade_mcp_bridge/handlers/tasks.py)
- [yade-mcp-bridge/src/yade_mcp_bridge/handlers/__init__.py](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/src/yade_mcp_bridge/handlers/__init__.py)
</details>

# MCP Tools Reference

## Overview

The **yade-mcp** server exposes seven tools to AI agents via the Model Context Protocol (MCP). They are organized into two functional groups: **knowledge/discovery** tools for navigating the YADE Python API, and **runtime** tools that drive the YADE process through the companion `yade-mcp-bridge` WebSocket service.

Source: [src/yade_mcp/tools/__init__.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/__init__.py)

Every tool returns a uniform response envelope produced by `build_ok()` / `build_error()`. Since v0.2.0, `build_ok()` is async and injects `_context.user_console` so agents can see what the human user just typed in the YADE IPython console, providing live context for multi-user workflows.

## Knowledge & API Discovery

These two tools operate on locally bundled JSON documents under `knowledge/resources/python_api_docs/runtime/` (371 classes scraped from a live YADE runtime in v0.2.2). They do **not** require a live bridge connection.

### yade_browse_api

Walks the YADE-native class tree using a dot-separated `path` argument. Navigation is strictly tree-driven: there are no class-name shortcuts, and every leaf must be reached through its parent chain.

| Path Example | Result |
|--------------|--------|
| `None` / `""` | Top-level categories (engine, functor, material, shape, …) |
| `"engine"` | Sub-trees of `Engine` (Dispatcher, GlobalEngine, PartialEngine, …) |
| `"engine.GlobalEngine"` | Sub-trees plus leaf classes (NewtonIntegrator, InteractionLoop, …) |
| `"engine.GlobalEngine.NewtonIntegrator"` | Full documentation with attributes, methods, default values |

Source: [src/yade_mcp/tools/browse_api.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/browse_api.py)

### yade_query_api

Performs keyword search (similar to `grep`) across the bundled docs via `APISearch.search()`. Useful when the agent knows what it wants — for example `"friction material"`, `"gravity engine"`, `"contact force"`, `"triaxial stress"`, `"sphere create"` — but not the exact class name. The `limit` parameter defaults to 10 matches and results are ranked by relevance.

Source: [src/yade_mcp/tools/query_api.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/query_api.py)

## Runtime Execution

These tools call into the `yade-mcp-bridge` WebSocket service and require it to be running inside the target YADE process. Both follow the same envelope pattern:

- `client = await get_bridge_client()` then `await client.<method>(...)`
- Connection failures return `build_bridge_error(exc)`
- Logic failures return `build_operation_error(code, message, action)`

Source: [src/yade_mcp/tools/execute_code.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/execute_code.py), [src/yade_mcp/tools/execute_task.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/execute_task.py)

### yade_execute_code

Synchronous, fire-and-return execution. Code runs in the YADE `__main__` namespace; **side effects persist** and the tool is **not** tracked by `yade_list_tasks`.

The tool remains responsive even while a long simulation is running in another task, making it ideal as a live REPL for inspecting `O.bodies`, energy balance, stress tensor, viewport screenshots, etc.

The `timeout` parameter (default `10` seconds) drives cancellation. As of v0.3.0, the response distinguishes three failure codes:

| `error.code` | Meaning |
|--------------|---------|
| `interrupted` | Code was inside an `O.run` cycle and was paused cleanly at an iteration boundary. Use `yade_execute_task` for long cycles. |
| `terminated` | Async-exception abort succeeded; pump is free but YADE state may be partially modified. |
| `timeout` | Abort failed (stuck in C extension or inside a running task's PyRunner tick). Bridge may still be blocked; consider restart. |

Source: [src/yade_mcp/tools/execute_code.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/execute_code.py)

### yade_execute_task

Asynchronous execution backed by `ScriptRunner.run()`. The tool generates a 6-character `task_id` immediately and returns it; the script proceeds in the background. Use this for production simulation runs and any operation expected to take minutes or longer.

Source: [src/yade_mcp/tools/execute_task.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/execute_task.py)

## Task Lifecycle Management

Once a task has been submitted, three companion tools manage its lifecycle. They all wrap the bridge handlers `handle_execute_task`, `handle_check_task_status`, `handle_list_tasks`, and `handle_interrupt_task`.

Source: [yade-mcp-bridge/src/yade_mcp_bridge/handlers/__init__.py](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/src/yade_mcp_bridge/handlers/__init__.py)

```mermaid
flowchart LR
    A[yade_execute_task] -->|returns task_id| B[(ScriptTask log)]
    B --> C[yade_check_task_status]
    B --> D[yade_list_tasks]
    B --> E[yade_interrupt_task]
    C -->|status / output| F[Agent]
    D -->|history| F
    E -->|terminated flag| B
```

### yade_check_task_status

Polls a task by `task_id`. Pagination has been moved **to the bridge side** since v0.2.1 (released in bridge-v0.2.1):

- `skip_newest` — skip the most recent N output lines
- `limit` — bounded page size (default `64`); the bridge returns `pagination.total_count` so the caller knows how much remains
- `filter` — substring filter against the complete log file

A separate `wait_seconds` parameter (default `1`) lets the caller block briefly for new output before returning.

The lifecycle status is read with `_lifecycle_status()`, which prefers `data.status` and falls back to the envelope top-level for cross-version compatibility with older bridges.

Source: [src/yade_mcp/tools/check_task_status.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/check_task_status.py), [yade-mcp-bridge/src/yade_mcp_bridge/handlers/tasks.py](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/src/yade_mcp_bridge/handlers/tasks.py)

### yade_list_tasks

Browses the task history with `skip_newest` (offset) and `limit` (default `32`) pagination. The tool tolerates legacy bridges that signal success via `status:"success"` instead of `ok:true`.

Source: [src/yade_mcp/tools/list_tasks.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/list_tasks.py)

### yade_interrupt_task

Requests cancellation of a running task. The bridge applies **two cancellation paths together** (bridge-v0.3.0):

1. **flag_only** — sets an interrupt flag that YADE's `PyRunner` tick observes between iterations (graceful path for `O.run` tasks).
2. **flag_and_async_exc** — additionally injects a `TaskInterrupt` exception into the script thread so pure-Python deadloops with no `O.run` on the stack can also be force-aborted.

The response `method` field reports which path actually ran. When async-exc is refused (e.g. target thread is a `Dummy-N` `boost::python` frame), `async_exc_skipped_reason` explains why. After an interrupt, the YADE `__main__` namespace is **preserved** — variables defined by the interrupted script (including `O` state) survive.

Source: [src/yade_mcp/tools/interrupt_task.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/interrupt_task.py)

## Common Failure Modes

- **Bridge unreachable** — every runtime tool returns `build_bridge_error(exc)`; no `task_id` is generated, so there is nothing to poll.
- **Long simulation in `yade_execute_code`** — `O.run` inside the synchronous tool will not be tracked; switch to `yade_execute_task`.
- **Pure-Python deadloop** — submit it via `yade_execute_task`, then call `yade_interrupt_task`. The async-exc path (bridge-v0.3.0) terminates the loop without a bridge restart.
- **`wait=False` `O.run`** — fixed in bridge-v0.2.4; previously caused silent-success bugs or orphan cycling because the task could complete in milliseconds while cycling continued.
- **Stale `%run script.py` NameError** — fixed in bridge-v0.2.2; caused by IPython's `%run` reassigning `sys.modules['__main__']` and YADE's `PyRunner` re-resolving it on every tick.

## See Also

- [Bridge Architecture & WebSocket Protocol](bridge-architecture.md)
- [YADE Python API Knowledge Base](yade-api-knowledge.md)
- [Task Lifecycle & Cancellation Semantics](task-lifecycle.md)

---

<a id='page-bridge'></a>

## YADE Bridge Internals

### Related Pages

Related topics: [MCP Tools Reference](#page-tools), [Overview and Architecture](#page-overview)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [yade-mcp-bridge/src/yade_mcp_bridge/__init__.py](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/src/yade_mcp_bridge/__init__.py)
- [yade-mcp-bridge/src/yade_mcp_bridge/__main__.py](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/src/yade_mcp_bridge/__main__.py)
- [yade-mcp-bridge/src/yade_mcp_bridge/bootstrap.py](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/src/yade_mcp_bridge/bootstrap.py)
- [yade-mcp-bridge/src/yade_mcp_bridge/paths.py](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/src/yade_mcp_bridge/paths.py)
- [yade-mcp-bridge/src/yade_mcp_bridge/runtime/__init__.py](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/src/yade_mcp_bridge/runtime/__init__.py)
- [yade-mcp-bridge/src/yade_mcp_bridge/runtime/pump.py](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/src/yade_mcp_bridge/runtime/pump.py)
- [yade-mcp-bridge/src/yade_mcp_bridge/runtime/pyrunner.py](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/src/yade_mcp_bridge/runtime/pyrunner.py)
- [yade-mcp-bridge/src/yade_mcp_bridge/tasks/task.py](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/src/yade_mcp_bridge/tasks/task.py)
- [yade-mcp-bridge/src/yade_mcp_bridge/transport/server.py](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/src/yade_mcp_bridge/transport/server.py)
- [src/yade_mcp/tools/interrupt_task.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/interrupt_task.py)
- [src/yade_mcp/tools/check_task_status.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/check_task_status.py)
- [yade-mcp-bridge/README.md](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/README.md)
</details>

# YADE Bridge Internals

The **yade-mcp-bridge** is the in-process runtime that lets a remote MCP client drive a live [YADE](https://yade-dem.org/) discrete-element simulation. It exposes two execution surfaces — `yade_execute_code` for a live REPL and `yade_execute_task` for long-running scripts — over an HTTP + Server-Sent-Events (SSE) protocol that the [yade-mcp](https://github.com/yusong652/yade-mcp) MCP server consumes. Source: [yade-mcp-bridge/src/yade_mcp_bridge/__init__.py:1-23](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/src/yade_mcp_bridge/__init__.py)

This page documents the internals that the public tools depend on: bootstrap and module surface, runtime coordination around YADE's Qt-bound main thread, task lifecycle, and transport.

## Bootstrap and Module Surface

The package root re-exports a single `start()` function so both interactive and batch users hit the same code path. Source: [yade-mcp-bridge/src/yade_mcp_bridge/__init__.py:18-23](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/src/yade_mcp_bridge/__init__.py)

```python
import yade_mcp_bridge
yade_mcp_bridge.start()                # interactive / GUI mode
yade_mcp_bridge.start(mode="console")  # batch mode
```

Running the package as a module (`python -m yade_mcp_bridge`) dispatches to the same `start()` via `__main__.main`. Source: [yade-mcp-bridge/src/yade_mcp_bridge/__main__.py:7-12](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/src/yade_mcp_bridge/__main__.py)

`bootstrap.start()` selects a transport, installs runtime hooks, and starts the request handler. Since bridge-v0.2.0 it has run the task pump in a background daemon thread, so the YADE IPython prompt stays usable while the bridge is active (community release notes: "Non-Blocking Console Mode"). A non-blocking startup is essential because the bridge is normally started from inside the YADE console and must not freeze it.

## Runtime Coordination: Pump, PyRunner, Signals

YADE's Qt GUI objects are main-thread-only. The bridge keeps `execute_code` on the main thread while running each task on its own thread, so a long `O.run()` never blocks the server or live code. Source: [yade-mcp-bridge/README.md:18-28](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/README.md)

The runtime package exposes two coordination primitives plus an installer:

- `start_background_pump` — drains the `execute_code` queue from a daemon thread; used in non-GUI / batch mode.
- `start_qt_pump` — drains the queue from the Qt event loop; used in interactive GUI mode.
- `install_pyrunner` — injects `_mcp_pyrunner_tick` into `O.run()` so the bridge can observe iterations and apply graceful interrupts.

Source: [yade-mcp-bridge/src/yade_mcp_bridge/runtime/__init__.py:11-21](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/src/yade_mcp_bridge/runtime/__init__.py)

The `_mcp_pyrunner_tick` symbol is the bridge's only foothold inside `O.run()`. Community release notes for bridge-v0.2.2 call out that YADE's `PyRunner` re-resolves `sys.modules['__main__']` on every tick, so the symbol must be installed on the `__main__` namespace that IPython's `%run` uses; otherwise a `NameError: name '_mcp_pyrunner_tick' is not defined` fires when a user `%run`s a script after the bridge has started.

```mermaid
flowchart LR
    Client[MCP client<br/>yade-mcp] -->|POST /command| Server[HTTP + SSE server<br/>background thread]
    Client -.->|GET /events| Server
    Server -->|execute_code| Pump[Runtime pump<br/>main thread]
    Server -->|execute_task| Tasks[ScriptTask<br/>own thread]
    Pump --> YADE[(YADE simulation<br/>__main__ namespace)]
    Tasks --> YADE
    Pump -. interrupt / pause .-> PyRunner
    Tasks -. log lines .-> Logs[".yade-mcp/logs"]
```

Cancellation uses two complementary paths described by the `yade_interrupt_task` tool:

- **`flag_only`** — sets an interrupt flag that the PyRunner tick observes between iterations; the graceful path for `O.run` tasks.
- **`flag_and_async_exc`** — additionally injects a `TaskInterrupt` exception into the script thread, terminating pure-Python deadloops.

When async-exc is refused (for example the target thread is a `Dummy-N` boost::python frame), `async_exc_skipped_reason` is returned so the agent can tell why the deadloop may still be alive. Source: [src/yade_mcp/tools/interrupt_task.py:14-32](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/interrupt_task.py)

Since bridge-v0.3.0, `execute_code` timeouts distinguish `terminated` (async-exc abort succeeded, the pump is free) from `timeout` (async-exc refused, the bridge may still be blocked). The split was promoted so agents can tell a graceful cancellation apart from a force-kill, and so they know that the YADE `__main__` namespace survives the interrupt. Before this split, agents could not tell whether a stuck `execute_code` would ever return and had no clean way to recover.

## Task Lifecycle

Tasks are managed by `ScriptTask` in the bridge's `tasks` package. The class tracks lifecycle states — `pending`, `running`, `completed`, `failed`, and `interrupted` — recorded on `_status` and surfaced through `check_task_status`. Source: [yade-mcp-bridge/src/yade_mcp_bridge/tasks/task.py:14-21](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/src/yade_mcp_bridge/tasks/task.py)

`ScriptTask` carries an `error_details` field that captures structured failure information (user-frame traceback, exception type, overflow log path) and surfaces it through `check_task_status` so the agent has full debugging context without chasing log files. Source: [yade-mcp-bridge/src/yade_mcp_bridge/tasks/task.py:39-42](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/src/yade_mcp_bridge/tasks/task.py)

Since bridge-v0.2.4, task lifetime is aligned with cycling lifetime: a script that calls `O.run(N, wait=False)` no longer creates "silent success" bugs or orphan cycling. Previously the task could complete in milliseconds while cycling continued in the background, masking real failures and leaving a runaway `O.run()` behind. This is a common failure mode for any agent that scripts a non-blocking run and immediately polls for status — the fix is the kind of detail that only becomes visible in production.

Since bridge-v0.2.1, output pagination (`skip_newest` / `limit` / `filter_text`) is handled on the bridge side via `ScriptTask.get_paginated_output`, which reads the full log file and returns an explicit pagination dict with `total_lines`, `line_range`, `has_older`, and `has_newer`. The MCP layer no longer paginates a possibly truncated window, which had been a long-standing source of off-by-one bugs. Source: [src/yade_mcp/tools/check_task_status.py:1-13](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/check_task_status.py)

## Transport and On-Disk Paths

The transport is a pure-stdlib HTTP + SSE server. Request/response flows over `POST /command`; server push flows over `GET /events`. Source: [yade-mcp-bridge/README.md:24-29](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/README.md)

On disk, all bridge artifacts live under a single dot-directory in the YADE process CWD: `DATA_DIR = ".yade-mcp"` and `LOGS_DIR = ".yade-mcp/logs"`. These constants are defined once in `paths.py` so every module refers to the same location. Source: [yade-mcp-bridge/src/yade_mcp_bridge/paths.py:13-19](https://github.com/yusong652/yade-mcp/blob/main/yade-mcp-bridge/src/yade_mcp_bridge/paths.py)

As of bridge-v0.3.1, the startup banner is reduced to a single line — `YADE MCP Bridge on ws://<host>:<port>, log: <path>` — and stdout is quieted to `WARNING`. Initialization `INFO` logs are routed to `bridge.log` only, keeping the interactive YADE prompt clean. The release also fixes a spurious "Console capture not installed" warning that previously fired even when console capture was wired correctly.

## See Also

- [MCP Tool Reference](mcp-tools.md) — the public surface area (`yade_execute_code`, `yade_execute_task`, `yade_check_task_status`, `yade_interrupt_task`, `yade_list_tasks`, `yade_query_api`, `yade_browse_api`).
- [YADE Bridge Quickstart](bridge-quickstart.md) — installing and launching the bridge inside YADE.
- [Bridge Cancellation Protocol](bridge-cancellation.md) — full semantics of `terminated` vs `timeout` and the two interrupt paths.

---

<a id='page-knowledge'></a>

## Knowledge Base, Search, and Deployment

### Related Pages

Related topics: [MCP Tools Reference](#page-tools), [Overview and Architecture](#page-overview)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [src/yade_mcp/tools/query_api.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/query_api.py)
- [src/yade_mcp/tools/browse_api.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/browse_api.py)
- [src/yade_mcp/tools/__init__.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/__init__.py)
- [src/yade_mcp/tools/execute_code.py](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/tools/execute_code.py)
- [src/yade_mcp/knowledge/resources/python_api_docs/runtime/MaterialContainer.json](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/knowledge/resources/python_api_docs/runtime/MaterialContainer.json)
- [src/yade_mcp/knowledge/resources/python_api_docs/runtime/FileGenerator.json](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/knowledge/resources/python_api_docs/runtime/FileGenerator.json)
- [src/yade_mcp/knowledge/resources/python_api_docs/runtime/TriaxialTest.json](https://github.com/yusong652/yade-mcp/blob/main/src/yade_mcp/knowledge/resources/python_api_docs/runtime/TriaxialTest.json)
- [yade-mcp-bridge/src/yade_mcp_bridge/utils/response.py](https://github.com/yusong652/yade-mcp-bridge/blob/main/yade-mcp-bridge/src/yade_mcp_bridge/utils/response.py)
- [yade-mcp-bridge/src/yade_mcp_bridge/runtime/pyrunner.py](https://github.com/yusong652/yade-mcp-bridge/blob/main/yade-mcp-bridge/src/yade_mcp_bridge/runtime/pyrunner.py)
- [README.md](https://github.com/yusong652/yade-mcp/blob/main/README.md)
- [yade-mcp-bridge/README.md](https://github.com/yusong652/yade-mcp-bridge/blob/main/yade-mcp-bridge/README.md)
</details>

# Knowledge Base, Search, and Deployment

## Overview

yade-mcp is a [Model Context Protocol](https://github.com/yusong652/yade-mcp) server that lets AI agents drive the [YADE](https://yade-dem.org/) discrete-element-method simulator. The system is split across two deliverables: the MCP server package (`yade-mcp`) and the in-process bridge (`yade-mcp-bridge`) that the user launches from inside YADE's Python console. This page covers three pillars that make the integration work end-to-end: the offline runtime-generated knowledge base of YADE classes, the search and browse tools that surface that knowledge to the agent, and the deployment model that wires an MCP client to a live YADE process.

The knowledge base is the static half of the system: 371 class JSON files produced by introspecting a live YADE runtime and frozen under `src/yade_mcp/knowledge/resources/python_api_docs/runtime/`. The bridge is the dynamic half: a WebSocket server embedded inside YADE's Python interpreter that pumps task output back to MCP clients.

## Knowledge Base

### Layout and schema

The knowledge base lives at `src/yade_mcp/knowledge/resources/python_api_docs/runtime/`, with one JSON file per introspectable YADE class. The v0.2.2 release notes describe this as a "complete refresh of the 371 class JSONs ... against a live YADE runtime". Every file follows the same shape: `name`, `category` (the top-level bucket such as `engines`, `functor`, `material`, `shape`), `parent` (the immediate superclass — the field that drives tree navigation), `description`, an `attributes` list of `{name, type, default, description}` records, and a `methods` list of `{name, args, returns, description, inherited_from?}` records.

For example, `MaterialContainer.json` declares `parent: "instance"` and exposes `append(Material) -> int` plus `index(str) -> int` Source: [src/yade_mcp/knowledge/resources/python_api_docs/runtime/MaterialContainer.json:1-31](). `FileGenerator.json` declares `parent: "Serializable"` and exports `generate(out)` and `load()` Source: [src/yade_mcp/knowledge/resources/python_api_docs/runtime/FileGenerator.json:1-30](). Concrete preprocessors such as `TriaxialTest` extend `FileGenerator` and pull in dozens of typed attributes (`sigmaIsoCompaction`, `sphereYoungModulus`, `compactionFrictionDeg`, `boxYoungModulus`, `maxWallVelocity`, ...) Source: [src/yade_mcp/knowledge/resources/python_api_docs/runtime/TriaxialTest.json:1-40]().

### Why runtime introspection

YADE is a hybrid C++/Boost.Python project whose API surface evolves between commits. Scraping from a live runtime is preferred over scraping reStructuredText docs because the docstrings are pinned to the actual method signatures. The scraper and the JSONs travel together, so a future bump of YADE re-runs introspection and replaces the bundle in a single commit (per v0.2.2 release notes).

## Search and Browse Tools

The MCP server exposes two complementary tools over the knowledge base, both registered through the `tools` package Source: [src/yade_mcp/tools/__init__.py:1-18]().

`yade_query_api` is a keyword search. It takes a free-text `query` and a `limit` (default 10) and delegates to `APISearch.search(query, top_k=limit)`. The docstring tells agents to use it when they have keywords but not the exact class name — examples include `"friction material"`, `"gravity engine"`, `"triaxial stress"`, `"sphere create"`, `"hertz mindlin"` Source: [src/yade_mcp/tools/query_api.py:1-42](). Results are wrapped with `build_docs_data(source="python_api", action="query", ...)` and emitted through `build_ok(...)`, the canonical success envelope.

`yade_browse_api` walks the YADE-native class tree by path. The `path` argument is dot-separated: `None` or `''` lists top-level categories, `engine` lists Engine sub-trees, `engine.GlobalEngine.NewtonIntegrator` resolves a leaf Source: [src/yade_mcp/tools/browse_api.py:1-49](). Navigation therefore goes through parent-class chains (`GlobalEngine → PeriodicEngine → ...`) rather than arbitrary class-name shortcuts. Unknown leaves yield `build_error("class_not_found", ...)` with a `details` block containing the requested `category`, `tree_path`, and `class_name`, which lets the agent self-correct without round-tripping.

The two tools are intentionally paired: `yade_query_api` for discovery, `yade_browse_api` for full documentation of a known class. Both call into the same `APILoader` / `APISearch` layer, so any future knowledge source (for example tutorial transcripts) can plug in behind the same `source` field.

## Deployment and Bridge

### Architecture

```mermaid
flowchart LR
    A[MCP client<br/>agent] -- stdio MCP --> B[yade-mcp server]
    B -- WebSocket<br/>ws://host:port --> C[yade-mcp-bridge]
    C -- py::exec / PyRunner --> D[YADE runtime]
    D -- O.engines[0] --> E[PyRunner tick]
    E -- async exception --> D
```

The MCP server is a normal Python package installed via `pip install yade-mcp` and registered with the model client (the README advertises `io.github.yusong652/yade-mcp` as the canonical server name) Source: [README.md:1-15](). The bridge is a separate package, `yade-mcp-bridge`, installed **inside** the YADE Python interpreter so it can reach `yade` C-extension bindings Source: [yade-mcp-bridge/README.md:25-45]().

### Startup and runtime modes

`yade_mcp_bridge.start()` is called from the YADE IPython console. It auto-detects the runtime: a Qt timer drives the task pump in GUI mode, while console mode spawns a background daemon thread so the user can keep typing Source: [yade-mcp-bridge/README.md:45-60](release context for bridge-v0.2.0). The bridge startup banner was collapsed to a single line — `YADE MCP Bridge on ws://<host>:<port>, log: <path>` — and stdout is quieted to `WARNING`, with initialization `INFO` logs going to `bridge.log` only (per bridge-v0.3.1 release notes).

### Task pump and PyRunner integration

Long-running work (`yade_execute_task`) is driven by a `PyRunner` engine injected at `O.engines[0]` whose command is the literal string `_mcp_pyrunner_tick()  # yade-mcp-bridge: DO NOT MODIFY`. The marker comment makes the engine self-identifying when users print `O.engines`. The bridge calls `_normalize_pyrunner()` on every tick, self-healing against user scripts that reassign `O.engines`, mutate `iterPeriod`, or move the engine out of slot 0, and logs a warning on tamper so the cause of any interrupt-latency bug is visible Source: [yade-mcp-bridge/src/yade_mcp_bridge/runtime/pyrunner.py:1-60](release context for bridge-v0.2.2). The same path was also where `%run script.py` triggered a `NameError: name '_mcp_pyrunner_tick' is not defined`, fixed in bridge-v0.2.2 by avoiding `__builtins__` re-resolution under IPython.

### Response envelope

The bridge emits a typed JSON envelope on every WebSocket response. `TaskDataBuilder` is the constructor: it carries `task_id`, `task_type`, `script_path`, and `description`, then layers in `status` (one of `pending`, `running`, `completed`, `failed`, `interrupted`) via `.with_status(...)` Source: [yade-mcp-bridge/src/yade_mcp_bridge/utils/response.py:1-35](). `status` intentionally rides inside `data` alongside `task_id` rather than parallel to the request-level `ok`, mirroring how the MCP server already nests it as `data.task_status`.

### Cancellation semantics

`bridge-v0.3.0` introduced abort-aware cancellation. The `execute_code` tool now distinguishes `terminated` (async-exc abort succeeded, pump free, YADE state may be partially modified) from `timeout` (abort failed, bridge may still be blocked). `yade_execute_code` mirrors this by exposing `interrupted`, `terminated`, and `timeout` as separate `error.code` values Source: [src/yade_mcp/tools/execute_code.py:1-50](). For long simulations, agents are steered toward `yade_execute_task` plus `yade_interrupt_task` rather than `yade_execute_code`, because task interrupts are cooperative and the `__main__` namespace survives the interrupt (per v0.3.0 release notes).

## See Also

- Tools reference (`yade_execute_code`, `yade_execute_task`, `yade_list_tasks`, `yade_interrupt_task`, `yade_check_task_status`)
- Bridge runtime internals — task pump, `PyRunner` tick loop, websocket protocol
- Bootstrap guide for PEP 668 externally-managed environments

---

<!-- evidence_pipeline_checked: true -->
<!-- evidence_injected: true -->

---

## Pitfall Log

Project: yusong652/yade-mcp

Summary: Found 7 structured pitfall item(s), including 0 high/blocking item(s). Top priority: Configuration risk - Configuration risk requires verification.

## 1. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a configuration risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: capability.host_targets | https://github.com/yusong652/yade-mcp

## 2. Capability evidence risk - Capability evidence risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: README/documentation is current enough for a first validation pass.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: capability.assumptions | https://github.com/yusong652/yade-mcp

## 3. Maintenance risk - Maintenance risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a maintenance risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/yusong652/yade-mcp

## 4. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: downstream_validation.risk_items | https://github.com/yusong652/yade-mcp

## 5. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: risks.scoring_risks | https://github.com/yusong652/yade-mcp

## 6. Maintenance risk - Maintenance risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: issue_or_pr_quality=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/yusong652/yade-mcp

## 7. Maintenance risk - Maintenance risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: release_recency=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/yusong652/yade-mcp

<!-- canonical_name: yusong652/yade-mcp; human_manual_source: deepwiki_human_wiki -->
