envimcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@envimcpcalculate NDVI for the QuickBird demo and export it to #ndvi.dat"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
envimcp — drive ENVI 5.2 from an AI agent over MCP
English | 简体中文
envimcp exposes the 58 ENVITask operators of a local ENVI 5.2 / IDL 8.4
installation to any MCP client (DeepSeek Harness, Codex, ZCode, Claude Desktop,
…) as eight tools and five resources, with no ENVI GUI and no Python bridge.
MCP client (agent)
│ stdio or streamable HTTP
▼
envimcp MCP server (Python 3.12, mcp 2.3.0)
├─ TaskCatalog reads D:\ENVI52\resource\templates\tasks\*.task (58 operators)
├─ ScriptGen emits literal-property IDL source per job
└─ JobRunner spawns idl.exe -e "@<job>\run_job.pro" (1 job at a time)
▼
IDL 8.4 process → ENVI(/HEADLESS) → ENVITask → status.jsonWhy not envipyengine
The obvious approach for "Python drives ENVI" does not work on this machine, for two independent reasons:
IDL 8.4 ships no IDL-Python bridge. There is no
omniidl, no*.pydand no Python DLL anywhere underD:\IDL84;D:\IDL84\lib\bridgescontains only Java and COM bridges. NV5's own documentation: "IDL versions prior to 8.5 do not support Python bridge functionality."Stronger:
envipyenginedoes not use the bridge at all. It spawnstaskengine.exeand exchanges JSON on stdin/stdout, and that executable first appears in IDL 8.5.2 / ENVI 5.3 (idltask.htmlversion history:8.5.2 | Introduced). IDL 8.5 alone is still not enough.
So this project takes the route that is supported: ENVI 5.2's documented
ENVI(/HEADLESS), driven through idl.exe -e. The price is that ENVI task
parameters can only be set as compile-time literal keywords, so Python
generates IDL source instead of making calls. See
IDL_NOTES.md for the 24 traps that shaped the generator.
Related MCP server: Task Tracker MCP
Install
Everything below assumes a local ENVI 5.2 / IDL 8.4 install and one checkout of
this repository. <repo> stands for the absolute path of that checkout — it may
contain spaces, which is why every command below quotes it. The project has only
been verified on the machine that produced it. Create the environment first — the
.venv directory is not part of the source tree:
# 1. create/reuse .venv, install the package, print a health report
pwsh -File '<repo>\tools\setup_env.ps1'
# ...or, if the interpreter is not at the default path:
pwsh -File '<repo>\tools\setup_env.ps1' -Python D:\python312\python.exeThen, from the repository root:
$root = '<repo>'
& "$root\.venv\Scripts\python.exe" -m envimcp.cli --config "$root\config\envimcp.toml" doctorsetup_env.ps1 needs D:\python312\python.exe (bare python does not resolve in
this shell) and reaches packages through the Tsinghua mirror, because pypi.org
is unreachable from this network. It finishes by running doctor, which prints a
JSON health report and exits non-zero if anything is missing.
Configure
config/envimcp.toml is the only configuration file.
Every key can be overridden by an ENVIMCP_* environment variable.
Section | Key | Meaning |
|
| ENVI installation ( |
|
| Where the |
|
| Dataset used by |
|
|
|
|
| Where jobs and |
|
| ENVI is single-licensed: one job at a time |
|
| MCP identity and listen address |
|
| Registers the |
|
| Allows writes outside the job/output roots (default false) |
envi_idl.exe and idlde.exe are byte-identical Eclipse Workbench GUI launchers
(MD5 E786D30B00ACE364B26657F98D79F03A) and are the wrong binary.
Command line
$py = '<repo>\.venv\Scripts\python.exe'
$cfg = '<repo>\config\envimcp.toml'
& $py -m envimcp.cli --config $cfg doctor # is this machine usable?
& $py -m envimcp.cli --config $cfg smoke # end-to-end NDVI regression
& $py -m envimcp.cli --config $cfg tasks spectral # search the operator catalog
& $py -m envimcp.cli --config $cfg schema SpectralIndex
& $py -m envimcp.cli --config $cfg inspect D:\ENVI52\data\qb_boulder_msi
& $py -m envimcp.cli --config $cfg run SpectralIndex `
--input INPUT_RASTER=D:\ENVI52\data\qb_boulder_msi --input INDEX=NDVI --output '#ndvi.dat'
& $py -m envimcp.cli --config $cfg export D:\ENVI52\data\qb_boulder_msi '#msi.tif'
& $py -m envimcp.cli --config $cfg serve --transport stdio
& $py -m envimcp.cli --config $cfg serve --transport http --port 8765smoke asserts the NDVI statistics against the measured golden baseline within
1e-6 and prints the deltas:
quantity | golden value |
|
|
|
|
|
|
|
|
|
|
MCP client setup
stdio (DeepSeek Harness, Codex, ZCode):
{
"mcpServers": {
"envi": {
"command": "<repo>\\.venv\\Scripts\\python.exe",
"args": [
"-m", "envimcp.cli",
"--config", "<repo>\\config\\envimcp.toml",
"serve", "--transport", "stdio"
]
}
}
}streamable HTTP (long-lived server, multiple clients):
& $py -m envimcp.cli --config $cfg serve --transport http --port 8765The endpoint is http://127.0.0.1:8765/mcp.
DeepSeek Harness does not use a JSON blob: it registers servers by patching its profile composition. The exact entry, the configuration keys it accepts, and a dependency-free handshake probe for verifying any client are in DSH_MCP_SETUP.md.
Tools
Tool | Purpose |
| List/search operators by name, display name or description |
| Full JSON Schema plus accepted |
| Run one operator; returns output geometry and statistics |
| Chain operators, wiring intermediate rasters without files |
| Metadata/statistics; reads the file directly unless |
| Write GeoTIFF or ENVI via |
| Status, result, outputs and an IDL output tail |
| Cancel and kill the job's IDL process tree |
| Escape hatch; only registered when |
Every tool that starts work returns a job_id even when it also waits, because
ENVI is single-licensed: a second request either queues or is rejected according
to limits.on_busy.
Resources
URI | Contents |
| Effective configuration plus the health checks |
| The whole catalog (58 tasks) — read this instead of 58 tool schemas |
| One task's schema, choices and source |
| The most recent jobs, newest first |
| One job record including every step result |
Output URI tokens
Form | Destination | Use for |
|
| Deliverables |
|
| Intermediates, throwaway results |
| nowhere | A workflow intermediate passed as a runtime handle |
absolute path | as given | Must resolve inside a permitted root |
A bare filename (name.dat) is rejected — an unconstrained path is an
unconstrained write. .dat is appended when the name has no known extension.
ENVI 5.2 caveat: the
*nametoken is not an ENVI feature. ENVI treats the URI literally, soenvimcpgives the task a throwaway temporary file (e.GetTemporaryFilename()) and carries only the runtime raster handle to the next step. The observable contract is what matters: no intermediate file is delivered, and the next step sees the in-memory raster.
Typical agent flow
envi_list_tasks("vegetation")— find candidate operators.envi_get_task_schema("SpectralIndex")— read required parameters and, forINDEX, the 64 accepted names. NoteNDVIis not one of them;envimcpresolves the acronym, but the long name isNormalized Difference Vegetation Index.envi_run_task(task="SpectralIndex", inputs={...}, output="#ndvi.dat").Read
steps[0].statsandsteps[0].rasterfrom the result.
For multi-step chains use envi_create_task_workflow with *NAME wiring:
{
"steps": [
{"task": "SpectralIndex",
"inputs": {"INPUT_RASTER": "D:/ENVI52/data/qb_boulder_msi", "INDEX": "NDVI"},
"outputs": {"OUTPUT_RASTER": "*NDVI"}},
{"task": "LinearPercentStretchRaster",
"inputs": {"INPUT_RASTER": "*NDVI", "PERCENT": 2.0},
"outputs": {"OUTPUT_RASTER": "!stretched.dat"}}
]
}Security model
envi_run_idlis off by default. When on it executes arbitrary IDL with the full privileges of the ENVI session.Absolute output paths must resolve inside
outputs_dir,jobs_diror an explicitly pinned output root unlessallow_outputs_outside_rootis set.The
#/!/*tokens accept only bare file names, so a token cannot escape its directory (#../../evilis rejected, not silently relocated).MCP resources use
ResourceSecurity(reject_path_traversal=True, reject_absolute_paths=True, reject_null_bytes=True)— the mcp 2.x default.Every job is confined to
jobs/<id>/; itsstatus.jsonis the verdict.
Testing
cd '<repo>'
& .\.venv\Scripts\python.exe -m pytest tests -q -m "not slow" # 52 fast tests, ~1.5 s
& .\.venv\Scripts\python.exe -m pytest tests -q -m slow # 4 acceptance tests, ~20 sThe slow tests really launch idl.exe and re-assert the golden NDVI baseline
(AC1), the virtual-raster workflow (AC6), the inspect path and a real GeoTIFF.
Troubleshooting
Symptom | Cause |
Job ends with no | The |
| The batch file never ran, or an identifier exceeded IDL's 32-character limit. |
| A zero-length array reached a |
| A host token was handed to ENVI literally instead of being resolved. |
| Set |
| A routine parameter shares a name with a common-block variable. |
| Licensing, or another ENVI/IDL process still holds the single licence. |
Every later job fails after one bad job | An orphan |
Deterministic detail for each of these lives in IDL_NOTES.md.
This server cannot be deployed
Maintenance
Related MCP Connectors
Hosted MCP server for task-first delegation to remote workstations and workers.
MCP Server for an Agent Task Marketplace
Query, browse, and automate OmegaAI workspaces from any MCP client. Streamable HTTP with OAuth 2.0.
MCP server for aerospace calculations: orbital mechanics, ephemeris, DSN operations, ...
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceAdds async processing to any MCP server, with timeout, task management, and subscription to task status via resources.4 npmMIT
- FlicenseNot gradedqualityBmaintenanceEnables managing tasks through a simple task tracker with REST API and MCP tools for listing, creating, updating, assigning, and deleting tasks.-
- AlicenseNot gradedqualityBmaintenanceEnables Codex to spawn, monitor, send messages to, and list DSH background tasks via a local REST bridge, wrapping six task-coordination endpoints as MCP tools.MIT
- FlicenseNot gradedqualityCmaintenanceEnables job-application lookup and domain support through MCP, exposing tools such as job lookup by ID over a local HTTP endpoint.-