MCP Server for controlling PixInsight astrophotography processing via AI assistants
Overview
An MCP server that lets an AI assistant drive PixInsight: open images, measure them, run any installed process, read the results back. PixInsight stays responsive while it works, so you can watch and intervene. Cross-platform: Windows, macOS and Linux. Any MCP client works. This is a stock stdio server with no client-specific code. Claude Desktop, Cursor, Windsurf and anything else that takes an mcp.json: On Windows, if a client cannot resolve npx, use "command": "cmd" with "args": ["/c", "npx", "-y", "@pardovot/pixinsight-mcp"]. Build and sign it from source, see Building from source below. Process > Utilities > MCP Watcher > Start, then ask your assistant to list your open images. If it answers, you are connected. Requires PixInsight 1.9.4+ (V8 engine), Node 18+, and an MCP client.
README
PixInsight MCP
An MCP server that lets an AI assistant drive PixInsight: open images, measure them, run any installed process, read the results back. PixInsight stays responsive while it works, so you can watch and intervene.
Cross-platform: Windows, macOS and Linux.
Install
1. MCP server. Any MCP client works. This is a stock stdio server with no client-specific code.
Claude Code:
claude mcp add pixinsight -- npx -y @pardovot/pixinsight-mcp
Claude Desktop, Cursor, Windsurf and anything else that takes an mcp.json:
{
"mcpServers": {
"pixinsight": { "command": "npx", "args": ["-y", "@pardovot/pixinsight-mcp"] }
}
}
Codex CLI, in ~/.codex/config.toml:
[mcp_servers.pixinsight]
command = "npx"
args = ["-y", "@pardovot/pixinsight-mcp"]
On Windows, if a client cannot resolve npx, use "command": "cmd" with
"args": ["/c", "npx", "-y", "@pardovot/pixinsight-mcp"].
2. PixInsight module. Build and sign it from source, see Building from source below.
3. Start it. Process > Utilities > MCP Watcher > Start, then ask your assistant to list your
open images. If it answers, you are connected.
Requires PixInsight 1.9.4+ (V8 engine), Node 18+, and an MCP client.
Tools
24 tools. Bold ones carry the work.
| Category | Tools |
|---|---|
| Execution | run_process, get_process_parameters, run_recipe |
| Images | list_open_images, open_image, save_image, close_image, get_image_statistics |
| Measurement | get_noise, get_background_gradient, get_background_neutrality, get_star_metrics, get_structure_color |
| Rendering | render_view, render_critic_pack |
| History | get_history, get_full_history, undo, redo, snapshot, restore |
| Instances | list_instances, use_instance |
| Export | export_container |
run_process runs any process by class name, including third-party ones you have installed,
so nothing needs adding when you install a new tool.
Measurement tools exist so settings come from your frame instead of a tutorial. Two traps:
get_noise returns an MRS estimate, stdDev is signal-dominated on astro frames and will lie to
you. get_background_neutrality needs mode:'poststretch' after stretching, the sky-band metric
is only valid pre-curve.
Definitions live in src/tools/*.ts. Verified gotchas: docs/facts.md.
How it works
MCP client
│ MCP (stdio)
MCP server src/ (TypeScript)
│ file bridge ~/.pixinsight-mcp/bridge/{commands,results}
MCPWatcher-pxm.dll pcl::Timer on PixInsight's event loop
│ MetaModule::EvaluateScript
Embedded JS handlers generated from pjsr/pixinsight-mcp-watcher.js
PixInsight
PixInsight has no socket or HTTP API, so a file bridge is the only route in. Round trip is about one poll interval, 300 ms by default.
The runtime is a compiled module rather than a PJSR script because a running script holds PixInsight’s only thread, which blocks the interface until it returns. The module’s timer fires on the event loop instead, which is why the application stays usable while a run is in progress.
Anything that can write to
~/.pixinsight-mcp/bridge/commandscan run arbitrary code inside PixInsight. Keep the directory user-private, never on a shared or synced path.
Platform
| Module installed as | |
|---|---|
| Windows | bin/MCPWatcher-pxm.dll |
| macOS | MacOS/MCPWatcher-pxm.dylib, universal x86_64 + arm64 |
| Linux | bin/MCPWatcher-pxm.so |
All three build in CI on every change. Nothing in the design is platform-specific: Node server, file bridge, PJSR handlers, CMake build with per-platform branches.
Building from source
Needs Node 18+, a C++ toolchain and CMake:
| Toolchain | |
|---|---|
| Windows | Visual Studio 2022 or Build Tools, with the C++ workload (found via vswhere) |
| macOS | Xcode Command Line Tools, plus brew install cmake ninja |
| Linux | build-essential cmake ninja-build |
npm run module:pcl # once, builds PCL from PixInsight's bundled source
npm run module:build # regenerate handlers, compile
# sign the module with PixInsight, needs your own signing identity
npm run module:install # admin/root, PixInsight closed
node module/config.mjs prints every path resolved on your machine, start there when a build
cannot find something. install.mjs copies the module and its .xsgn, and refuses a signature
older than the binary.
PixInsight’s bundled makefiles compile in-tree, so on a read-only install (/opt/PixInsight is
root-owned) module:pcl mirrors the ~20 MB source tree into $PCL_BUILD_OUT/src and builds the
copy. The install directory is never written to except by module:install.
Full local setup per platform: docs/dev-setup.md.
Handler logic lives in pjsr/pixinsight-mcp-watcher.js only. module/src/BridgeHandlersJS.h is
generated from it, never edit it by hand.
Configuration
Every path is a default an environment variable overrides, derived rather than hardcoded, so a stock install needs nothing.
| Variable | Default | Purpose |
|---|---|---|
PIXINSIGHT_EXE |
probed | PixInsight executable, for the build scripts |
PIXINSIGHT_MCP_TIMEOUT_MS |
300000 |
per-command timeout |
PIXINSIGHT_MCP_EXTENDED_TIMEOUT_MS |
3600000 |
long operations |
PIXINSIGHT_MCP_POLL_INTERVAL_MS |
200 |
bridge poll cadence |
PI_ROOT |
probed | install root, build scripts derive the rest |
PCL_BUILD_OUT |
~/pcl-build |
where the PCL library and its source mirror land |
PI_ROOT is probed per platform: %ProgramFiles%\PixInsight, /Applications/PixInsight,
/opt/PixInsight. Everything else derives from it, so overriding one variable relocates the rest.
PI_ROOT=/opt/PixInsight-1.9.5 node module/build.mjs
Signing
PixInsight refuses unsigned modules by default. Sign with PixInsight’s own signer and your
.xssk (--sign-module-file, and --sign-xml-file for a repository index).
Release process: docs/RELEASING.md.
Layout
src/ MCP server: tools/, pjsr/, bridge/
module/ native module (the runtime), build/sign/install tooling
pjsr/ JS watcher, source of truth for handler logic
docs/ RELEASING, bridge-protocol, facts, architecture
scripts/ ping-watcher, build-pi-repo
test/ node --test suites
Origins
Began in 2026 as a fork of aescaffre/pixinsight-mcp, independent codebase since. The file bridge and the PJSR handler bodies come from there. The native module, the generic process runner, the measurement layer, signing and the update repository were added here.
Credits
- Alain Escaffre (@aescaffre), originator of the project this one grew from: the file bridge, the agentic architecture, and the PJSR watcher whose handler logic still runs here. Developed as a member of Astro ARO, a remote observatory in the Alentejo Dark Sky Reserve (Portugal), Bortle 2-3.
- Andre Couto (@4ndr3c0ut0), V8 runtime port of the watcher for PixInsight 1.9.4+ “Lockhart” (PR #1 on the original repository).
- pardovot, native module, generic process runner, measurement and knowledge layer, signing and release pipeline, packaging.
License
MIT © Alain Escaffre (original work) and pardovot. See LICENSE.
Install
npx -y @pardovot/pixinsight-mcpConfiguration
{
"mcpServers": {
"pixinsight": { "command": "npx", "args": ["-y", "@pardovot/pixinsight-mcp"] }
}
}