Skip to main content
Glama
Wzhang3912

opendtect-mcp

by Wzhang3912
README.md
# opendtect-mcp

**Model Context Protocol (MCP) server that drives [OpendTect](https://dgbes.com/software/opendtect/) headlessly** — SEG‑Y import, 3D horizon auto‑tracking, ASCII export, and horizon‑agreement scoring.

It wraps two OpendTect surfaces, both public and documented:
- **`odpy`** (OpendTect's Python framework, Apache‑2.0) for database/survey introspection.
- the **Command Driver** (`od_main --cmd script.odcmd`) for GUI actions (import / track / export).

A pure‑Python scoring layer that turns two tracked horizons into quantitative numbers.

> **Not affiliated with, endorsed by, or sponsored by dGB Earth Sciences.** "OpendTect" is a trademark of dGB Earth Sciences; it is used here only to describe interoperability. This project ships **no** OpendTect code or data — it invokes a separately‑installed OpendTect via its documented interfaces.

---

## What it exposes (MCP tools)

| Tool | Needs OD? | What it does |
|---|---|---|
| `survey_info` | introspection | OD install + active survey + readiness check (**start here**) |
| `list_objects` | odpy | list DB objects of a translator group (`Seismic Data`, `Horizon`, …) |
| `import_segy` | Command Driver | import a 3D post‑stack SEG‑Y into the survey |
| `autotrack_horizon` | Command Driver | *(experimental, M2)* seed + auto‑track a 3D horizon |
| `export_horizon_ascii` | Command Driver | export a tracked horizon to ASCII `.xyt` |
| `read_horizon` | no | parse an exported horizon (pick count + extent) |
| `compare_horizons` | no | **score baseline vs candidate tracking**: coverage, holes, RMS pick deviation, verdict |

The GUI‑action tools accept `dry_run=True` to return the generated `.odcmd` **without executing**, so you can calibrate menu labels for your OD version safely.

---

## Install

```bash
cd opendtect-mcp
pip install -e .            # or: pip install -e ".[dev]" for tests
```

Python ≥ 3.10. `odpy` ships with OpendTect; if it isn't on your Python path, `pip install odpy` or run this server with OpendTect's bundled Python.

## Configure

All via env (auto‑detects common install locations otherwise) — see `.env.example`:

```bash
export OPENDTECT_BIN=/path/to/od_main
export OPENDTECT_DATA_ROOT=/path/to/SurveyDataRoot
export OPENDTECT_SURVEY=YourSurvey
```

Verify:
```bash
python -c "from opendtect_mcp.config import ODConfig; import json; print(json.dumps(ODConfig.from_env().check(), indent=2))"
```

## Connect to an MCP client (e.g. Claude Code)

Add to your MCP config (`.mcp.json` in a project, or the user‑level config):

```json
{
  "mcpServers": {
    "opendtect": {
      "command": "opendtect-mcp",
      "env": {
        "OPENDTECT_BIN": "/path/to/od_main",
        "OPENDTECT_DATA_ROOT": "/path/to/SurveyDataRoot",
        "OPENDTECT_SURVEY": "YourSurvey"
      }
    }
  }
}
```

Then ask the assistant to call `survey_info` first.

---

## Status & calibration

- ✅ **Works now, no OD:** `read_horizon`, `compare_horizons`, all `dry_run` script generation, the unit tests (`pytest`).
- ✅ **M0/M1 (introspection + import/export):** functional; the `.odcmd` **menu labels are version‑specific** and may need one‑time calibration (see `odcmd_templates.py`, lines marked `# CALIBRATE`). Use `dry_run=True`, run against your OD, read `odcmdlog.txt`, adjust labels.
- 🚧 **M2 (`autotrack_horizon`):** scaffolded; seed placement is the trickiest GUI op to script and needs calibration before `dry_run=False`.

The Command Driver navigates the GUI by label, so a given OD **version** only needs calibrating once — keep validated strings in `odcmd_templates.py`.

See **`examples/horizon_tracking_comparison.md`** for the end‑to‑end baseline‑vs‑candidate tracking workflow.

## Platform notes — headless Command Driver

The Command Driver tools (`import_segy`, `autotrack_horizon`, `export_horizon_ascii`)
need to launch OpendTect's GUI headlessly. Status by platform:

- **Linux / Windows:** expected to work — OD auto‑opens the last survey, so the CD
  runs the `.odcmd` at startup. (`command_driver.py` builds the command + a
  conda‑clean environment via OpendTect's bundled `odpy` — `getODCommand` +
  `getEnvForOpendTect` — so a conda‑hosted server doesn't clash with OD's Qt libs.)
- **macOS (OD 7.0.x): known blocker — not yet automatable.** Two issues compound:
  1. *(fixed)* a conda env launching bare `od_main` clashes with OD's Qt libraries
     and the process won't start. `command_driver.py` fixes this by running through
     `odpy.getEnvForOpendTect` (strips `CONDA_*`/Qt paths, sets `DTECT_APPL`).
  2. *(open)* OD shows a **modal "Survey Setup and Selection" dialog on every
     launch** and the Command Driver cannot dismiss modal dialogs (per the Command
     Driver Manual). It loads *before* the CD plugin, so the CD never runs (no
     `odcmdlog.txt` is ever written). Driving the dialog from outside via
     `osascript`/System Events (even with Accessibility granted, and launching via
     `open` as a foreground app) reports success but does **not** reliably activate
     the dialog's button on OD's Qt widgets.

  **Until this is resolved, on macOS use the GUI for the survey + tracking steps and
  this server only for scoring** (`compare_horizons`, `read_horizon`, which need no
  OD). See `docs/MACOS_HEADLESS_ISSUE.md` for the full reproduction and the open
  question filed with dGB.

## Layout
```
opendtect_mcp/
  config.py            env-driven OD/survey resolution + readiness check
  command_driver.py    write+run .odcmd, parse odcmdlog.txt
  odcmd_templates.py   parameterized .odcmd builders  (← calibrate here)
  odpy_bridge.py       odpy dbman/common wrappers (degrade gracefully)
  horizon_io.py        ASCII horizon parse + agreement scoring (no OD)
  server.py            FastMCP server + tool definitions
tests/                 no-OD unit tests
examples/              workflow guide
```