Skip to main content
Glama
finnhyun12

genicam-mcp

by finnhyun12

genicam-mcp

Vendor-neutral industrial camera control via GenICam GenTL.

An MCP server that enumerates, connects to, configures, and captures from industrial cameras through the GenICam GenTL standard — not through a single vendor's SDK.

Point it at any GenTL producer (.cti) and you get the camera's entire GenICam feature tree: exposure, gain, trigger, ROI, pixel format, and everything else the camera exposes.

Why this exists

Existing camera MCP servers bind to one vendor's SDK (typically pypylon, Basler-only) and expose a single "grab a frame" call. This one goes through the vendor-neutral GenTL layer instead, so one code path is meant to drive any GenTL-compliant camera — Basler, HIKrobot, Teledyne FLIR, Allied Vision, XIMEA — regardless of vendor. (Actually tested producers are listed under Verified below.) It also exposes feature control, not just capture.

No vendor SDK is imported. The only path to the hardware is harvesters.cti. That constraint is the point of the project.

Related MCP server: Vicon DataStream MCP Server

Install

Requires Python 3.12 (the genicam wheels this depends on are version-specific).

git clone https://github.com/finnhyun12/genicam-mcp.git
cd genicam-mcp
uv venv --python 3.12
uv sync

Claude Desktop setup

Add to your claude_desktop_config.json:

{
  "mcpServers": {
    "genicam": {
      "command": "uv",
      "args": ["--directory", "/absolute/path/to/genicam-mcp", "run", "genicam-mcp"]
    }
  }
}

If uv is not on your PATH, point at the venv interpreter directly:

{
  "mcpServers": {
    "genicam": {
      "command": "/absolute/path/to/genicam-mcp/.venv/bin/python",
      "args": ["-m", "genicam_mcp.server"]
    }
  }
}

On Windows use .venv\\Scripts\\python.exe and escape backslashes in JSON.

Tools

Tool

Purpose

add_producer(cti_path)

Register a GenTL producer (.cti). Call this first. Multiple producers may be registered.

discover_cameras()

List cameras: index, vendor, model, serial, tl_type.

connect(index)

Open a camera. Required before feature access or capture.

disconnect()

Release the camera.

list_features(category=None)

Walk the node map. Returns name, type, readable/writable, current value, range or options.

get_feature(name)

Read one feature with its type and min/max (or enum options).

set_feature(name, value)

Write any GenICam feature — ExposureTime, Gain, TriggerMode, PixelFormat, Width… Values are cast to the node's type.

grab(save_path=None)

Capture one frame → PNG. Returns width, height, pixel_format, saved_path.

Feature names are case-sensitive and vary by camera. Use list_features() to see what a given camera actually offers rather than guessing.

Finding a .cti producer

Vendor SDKs install their producers and register the directory in the GENICAM_GENTL64_PATH environment variable:

# Linux / macOS
echo $GENICAM_GENTL64_PATH
# Windows
$env:GENICAM_GENTL64_PATH

Common locations:

Vendor

Producer

Allied Vision Vimba X

.../Vimba X/cti/VimbaGigETL.cti, VimbaUSBTL.cti

Basler pylon

.../pylon 6/Runtime/x64/ProducerGEV.cti, ProducerU3V.cti

HIKrobot MVS

.../Common Files/MVS/Runtime/Win64_x64/MvProducerGEV.cti, MvProducerU3V.cti

If add_producer is given a bad path, the error message lists the .cti files it found on your machine.

Demo without hardware

Allied Vision Vimba X ships a Camera Simulator TL — a GenTL producer that presents simulated cameras with real test-pattern frames. No camera required.

  1. Install Vimba X (free).

  2. Register the simulator producer and grab a frame:

add_producer("C:\\Program Files\\Allied Vision\\Vimba X\\cti\\VimbaCameraSimulatorTL.cti")
discover_cameras()
connect(0)
set_feature("TestPattern", "GreyVerticalRamp")
grab("frame.png")

This is the exact producer path used for the end-to-end verification below. On that machine it enumerated three simulated cameras; index 0 was Camera Simulator (G1-030_VSWIR) at 656×520 Mono8. TestPattern accepts Off, GreyVerticalRamp, GreyVerticalRampMoving, ColorVerticalBar, ColorHorizontalBar, and ColorVerticalBarMoving — check get_feature("TestPattern") on your install rather than assuming, since the simulator's model list can differ by Vimba X version.

Note: a vendor's virtual camera feature is not the same thing as a simulator transport layer. Basler's pylon Camera Emulator (PYLON_CAMEMU) and HIKrobot's MVS Virtual Device both live above the GenTL layer and are visible only through their own vendor SDKs — GenTL consumers cannot see them. Vimba X's Camera Simulator is itself a .cti, which is why it works here.

Verified

  • ✅ End-to-end (enumerate → connect → feature R/W → grab → PNG): Allied Vision Vimba X — Camera Simulator TL (GenTL producer)

  • ✅ Producer load & camera enumeration: Basler pylon ProducerU3V.cti

genicam-mcp is built against the GenICam GenTL standard and is expected to work with any compliant producer (Basler, HIKrobot, Teledyne FLIR, Allied Vision, XIMEA, …). Only the producers listed above are actually tested; others are unverified — field reports welcome.

Development

uv run pytest                    # fake-GenTL suite; no hardware, no SDK

The real-producer suite is gated on an env var so day-to-day runs stay fast:

GENTL_CTI=/path/to/Producer.cti uv run pytest -m real_producer -v -s

If GENTL_CTI is set but no camera is found, the suite fails rather than skipping — a gate that silently passes is not a gate.

Known quirks

These are documented because each one looks like a broken server when you hit it cold. Knowing them in advance turns a bug report into a shrug.

  • First add_producer can be slow. Basler's ProducerU3V.cti took over 60 s on its first load (USB bus enumeration). Nothing is wrong — it is enumerating the bus. Allow a generous tool timeout on the first call; a short client timeout will cancel the request and can tear down the MCP session with it.

  • A bad producer can take the process down. MvFGProducerCML.cti segfaults (0xC0000005) when loaded without Camera Link hardware. This happens inside the native library, so no Python try/except can catch it — register only producers that match your hardware.

  • Trigger mode blocks capture. If TriggerMode is On, grab() waits for a trigger and then times out. Set set_feature("TriggerMode", "Off") for free-run.

Implementation notes

Three findings shaped this code. They are recorded here because none of them is visible from the source alone, and each cost real time to track down.

A vendor "virtual camera" is not a simulator transport layer. Basler's pylon Camera Emulator (PYLON_CAMEMU) and HIKrobot's MVS Virtual Device both look like the answer to "test without hardware" — and both are invisible to GenTL. They live above the GenTL boundary, inside the vendor SDK, so only that vendor's own tools can see them. Measured on HIKrobot MVS: the vendor SDK enumerated the virtual camera (MV_VIR_GIGE_DEVICE → 1 device, and only when run elevated), while MvProducerGEV.cti reported 0 through the same machine. Vimba X's Camera Simulator works because it is a .cti. The question to ask of any "virtual camera" feature is not whether it exists but which layer it lives in.

Import the native extensions at module load, never inside a tool call. Deferring import harvesters into the tool body seems tidy — it lets the module import cleanly on machines with no SDK. But FastMCP runs tool functions off the event loop, and loading the genicam native extension there hangs forever: the server accepts the CallToolRequest and then simply never answers. A silent hang is far worse than an immediate error, because the client only sees a timeout and the user has nothing to go on. So the import happens at module load and the failure reason is stashed and raised, as a readable message, at first use.

A GenTL buffer is borrowed, not owned. ia.fetch() hands out a buffer from the acquisition queue; once it is returned, the memory is recycled. Keeping a numpy view past that point does not raise — it silently yields whatever the driver wrote next, which in practice means an all-black frame. This surfaced only against the real Vimba simulator, never against the fakes, because a fake buffer is just a live numpy array. The fake now zeroes its data on __exit__ to model the real lifetime contract, so the failure is reproducible without hardware, and test_real_producer.py asserts the frame is not uniformly zero. A capture test that only checks "a PNG exists" passes on a black image.

Scope

One camera at a time; one frame per grab(). Simultaneous multi-camera acquisition, async streaming, GUI, and web serving are deliberately out of scope.

License

MIT — see LICENSE.

Install Server
A
license - permissive license
A
quality
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/finnhyun12/genicam-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server