Skip to main content
Glama
northidaholabs

TrimbleX9-mcp

TrimbleX9-mcp

An MCP server for the Trimble X9 terrestrial laser scanner. It gives an AI agent tools to read the scanner's status, list projects and scans, read the scan-mode menu, download point-cloud files and previews, and, behind an explicit opt-in, create projects and start full-dome or area scans.

It runs in one of two modes:

  • Direct mode talks to the scanner's own local HTTP/REST API (/api/v1) over its USB or Wi-Fi link. The safety checks run in this server.

  • Bridge mode talks to a separate x9 bridge service that sits next to the scanner and enforces the rules (approvals, battery, busy scanner, echo checks) for every client. The tools are thin calls to the bridge's API.

What the agent gets back is something it can look at. The minimap of a 19-station colour scan of an administration building, redrawn by scanner_minimap after every station:

And where to set up next: scanner_plan_station picks the spot from the station's points, and scanner_propose_station shows the person the spot, the landmark beside it and a tripod drawn its real size there, with its shadow on the ground:

Tested with Claude Opus 5.5 and Grok 4.6 as the model in the chat: given the same plan, both put the tripod on the same spot of pavement.

Interoperability project: written from scratch against the instrument's network API, with behavior verified on a live instrument. No vendor code, firmware or vendor documentation is included or redistributed. How the API knowledge was obtained is recorded in PROVENANCE.md. Independent project, not affiliated with or endorsed by Trimble Inc.; "Trimble" and "X9" are trademarks of their respective owners.

Contents: Install · Modes · Configuration · Tools · Safety · Errors · Client configuration · Compatibility · Tests · full tool reference in docs/tools.md.

Install

Python 3.10 or later. Install from the GitHub release tag; this provides the console script trimblex9-mcp, which speaks MCP over stdio.

pip install "git+https://github.com/northidaholabs/TrimbleX9-mcp@v0.15.0"
# or, isolated:
pipx install "git+https://github.com/northidaholabs/TrimbleX9-mcp@v0.15.0"
# or run without installing:
uvx --from "git+https://github.com/northidaholabs/TrimbleX9-mcp@v0.15.0" trimblex9-mcp
# no git on the host (e.g. a minimal Raspberry Pi image):
pip install https://github.com/northidaholabs/TrimbleX9-mcp/archive/refs/tags/v0.15.0.tar.gz

Dependencies: mcp and httpx (see Compatibility).

On a Raspberry Pi without internet

A field Pi is often on the scanner's Wi-Fi, with no internet. Build a wheelhouse on a machine that has internet, for the Pi's Python (Raspberry Pi OS trixie: CPython 3.13, aarch64), copy it over and install from it:

# on a machine with internet
tag=v0.15.0
pip wheel --no-deps -w wheelhouse "git+https://github.com/northidaholabs/TrimbleX9-mcp@$tag"
pip download -d wheelhouse --only-binary=:all: --platform manylinux2014_aarch64 \
  --python-version 3.13 --implementation cp "mcp>=1.7,<3" "httpx>=0.27"
curl -L -o wheelhouse/trimblex9-mcp-auto \
  https://raw.githubusercontent.com/northidaholabs/TrimbleX9-mcp/$tag/extras/trimblex9-mcp-auto
scp -r wheelhouse <pi>:

# on the Pi
python3 -m venv ~/x9-bridge/.venv
~/x9-bridge/.venv/bin/pip install --no-index --find-links ~/wheelhouse trimblex9-mcp
install -D -m 0755 ~/wheelhouse/trimblex9-mcp-auto ~/x9-bridge/bin/trimblex9-mcp-auto

The launcher (extras/trimblex9-mcp-auto, from v0.12.1) starts the server for a field kit; see On a field Pi, through the bridge.

trimblex9-mcp is meant to be started by an MCP client, not run by hand; on a terminal it just waits for MCP messages on stdin.

Related MCP server: Bambuddy MCP Server

Modes

The mode is chosen once, at start-up, from the environment:

SCANNER_BRIDGE_URL

SCANNER_BRIDGE_TOKEN

Result

unset or empty

(ignored)

Direct mode: talks to the scanner at SCANNER_BASE.

set

set

Bridge mode: talks to the bridge at SCANNER_BRIDGE_URL.

set

unset or empty

Exits at start-up with bridge mode needs SCANNER_BRIDGE_URL and SCANNER_BRIDGE_TOKEN. It does not fall back to direct mode.

The two modes expose different tool sets (see Tools). In bridge mode SCANNER_BASE and SCANNER_ALLOW_ACTUATION are ignored: the bridge decides what a token may do.

Direct mode

Run the server on the host the scanner is connected to (by USB, or on the scanner's Wi-Fi). Out of the box the server is read-only: tools that move the instrument, fire its laser or delete data are not registered at all unless SCANNER_ALLOW_ACTUATION is set. With it set, the server validates every scan request locally, refuses to start a scan or delete data while the scanner is busy, and cancels any scan the instrument did not accept exactly as sent.

Bridge mode

The bridge is the only thing that talks to the scanner. This server sends each call to the bridge's HTTP API with a bearer token the bridge issued, usually with role agent. With an agent token, a scan waits in awaiting_approval until a person approves it in the bridge app, unless the person granted a set of scans in advance. Approval, grant and delete tools are deliberately absent from this server. Bridge mode adds exports to PLY/LAS, previews returned as images, aiming, recipes and a field log.

The bridge itself is a separate service and is not part of this repository.

Configuration

All configuration is through environment variables.

Variable

Mode

Default

Effect

SCANNER_BASE

direct

http://192.168.42.1:34567/api/v1

Base URL of the scanner's HTTP API. The default is the scanner over its USB network link; use its Wi-Fi address (e.g. http://192.168.43.1:34567/api/v1) when connected over Wi-Fi. A trailing / is removed.

SCANNER_ALLOW_ACTUATION

direct

unset (off)

1, true or yes (any case) registers the actuating and destructive tools. Any other value, including on, leaves them unregistered.

SCANNER_DOWNLOAD_DIR

both

~/scanner-downloads

Directory that download tools write into and that scanner_preview_to_angles reads from. ~ is expanded and the path is resolved (symlinks followed) at start-up. Created on first download. Nothing is written outside it.

SCANNER_BRIDGE_URL

bridge

unset

Bridge API base, e.g. http://bridge.local:8795/v1. Setting it selects bridge mode. A trailing / is removed.

SCANNER_BRIDGE_TOKEN

bridge

unset

Bearer token issued by the bridge, sent as Authorization: Bearer <token>. Required in bridge mode.

SCANNER_PERSON_TOKEN

bridge

unset

An operator token for the person at the scanner. Used only after they answer yes in the chat (MCP elicitation, which the model can't answer): to allow scanning for the visit and to sign off a run. The model never sees it. Without it, or with a client that can't ask, the person uses the bridge's web UI.

SCANNER_VISIT_HOURS

bridge

8

How long the permission given in the chat lasts (up to 20 scans)

Timeouts are fixed in the code, not configurable:

Request

Direct mode

Bridge mode

Ordinary API calls

15 s (5 s to connect)

30 s (5 s to connect)

File downloads (streamed)

600 s (5 s to connect)

600 s (5 s to connect)

scanner_preview image

—

120 s (5 s to connect)

The long-running waits (scanner_wait_for_scan, scanner_wait_for_job) poll and take their own timeout_s argument (1 to 7200 s, default 900).

Tools

A summary; parameters, return values and rules for each tool are in docs/tools.md.

"Read" tools only query. "Local write" tools write files into SCANNER_DOWNLOAD_DIR but do not change the scanner. "Actuating" tools move the instrument or fire its laser. "Destructive" tools stop work or delete data. Each tool carries matching MCP annotations (readOnlyHint, destructiveHint).

Direct mode, always registered

Tool

Kind

What it does

scanner_status

read

Battery, AC, levelling, storage, IMU, laser pointer, Wi-Fi config (secrets masked), errors

scanner_projects

read

List projects

scanner_scans

read

List scans as compact records

scanner_scan_modes

read

The scan-mode menu (range, points, size, time, spacing, rate)

scanner_errors

read

Current error list

scanner_tasks

read

Running or queued tasks with percent complete; empty means idle

scanner_wait_for_scan

read

Wait until no task is running, reporting progress

scanner_scanfile_info

read

Metadata of one point-cloud file

scanner_download_scanfile

local write

Download a scan's .tzf point-cloud file

scanner_download_preview

local write

Download a scan's preview JPEG

scanner_preview_to_angles

read (local only)

Turn a box on a downloaded preview into area-scan angles

Direct mode, only with SCANNER_ALLOW_ACTUATION

Tool

Kind

What it does

scanner_create_project

write

Create a project

scanner_launch_scan

actuating

Start a full-dome scan

scanner_launch_area_scan

actuating

Start a scan of an angular window

scanner_cancel_task

destructive

Stop a running task

scanner_delete_scan

destructive

Delete a scan and its file, by scanUUID; refused while busy

scanner_delete_project

destructive

Delete a project and its scans, by projectUUID; refused while busy

A typical direct-mode scan: scanner_scan_modes → scanner_projects (or scanner_create_project) → scanner_launch_scan → scanner_wait_for_scan → scanner_scans → scanner_download_scanfile.

Bridge mode

Tool

Kind

What it does

scanner_status

read

Bridge and scanner state: connection, busy, battery, storage, levelling, moved-since-last-scan, queue length

scanner_scan_modes

read

Scan modes with range, angular step, point count and timings

scanner_projects

read

Projects with scan counts

scanner_create_project

write

Create a project by name

scanner_scans

read

Scans, newest first, optionally for one project

scanner_scan

read

One scan's record

scanner_start_scan

actuating (queued; usually needs approval)

Queue a full-dome or area scan

scanner_job

read

One job's state and progress

scanner_wait_for_job

read

Wait for a job, returning early if it needs approval

scanner_cancel_job

destructive

Cancel a job; a running scan is stopped

scanner_preview

read

A scan's preview, returned as an image the agent can see

scanner_aim

read

Turn a box on a preview into an area-scan window

scanner_export

write (on the bridge)

Decode a scan to levelled PLY or LAS

scanner_export_info

read

An export's point count, bounds and checks

scanner_download_export

local write

Download an export file

scanner_download_scanfile

local write

Download a scan's raw .tzf

scanner_recipes

read

Installed recipes

scanner_recipe

read

One recipe's inputs and steps

scanner_start_run

write (may lead to scans)

Start running a recipe

scanner_run

read

A run's state and steps

scanner_advance_run

write

Complete a person step of a run

scanner_measure

write

Measure a window opening in a scan on the bridge (bridge 0.4.0+)

scanner_measurement

read

A measurement's values, checks and evidence image

scanner_permissions

read

Standing permissions to scan: project, expiry, scans left, who gave them, and the run they cover

scanner_plan_station

read

Computes where to set up next from a full dome's points: up to three spots on open ground a person can walk to, a real step out, seeing the most of what this station couldn't (behind and beside a named subject, if given), each with its preview pixel and a landmark beside it (bridge 0.14.0+)

scanner_propose_station

write

Marks where to set up next on the station's preview: the tripod, the one landmark the person walks past, and a sentence naming it (no left, right or compass words). The bridge refuses a proposal that breaks that, shows it in its web UI and returns the picture (bridge 0.8.0+)

scanner_set_mission

write

Sets what the job is for (the person's goal, kind, done-when criteria) and its first ledger targets (bridge 0.8.0+)

scanner_mission

read

The mission, its state and its ledger

scanner_registration

read

The project's stations in one frame (registered by the bridge after each full dome), the walls found, and the plan picture (bridge 0.9.0+)

scanner_processing

read

Where registration runs: the laptop workers connected, or the Pi; how to start a worker on the laptop. Ask the person once per laptop (bridge 0.10.0+)

scanner_sign_off

write

Asks the person, in the chat, to confirm a run's sign-off step (or to correct the numbers); relayed with person_said where the chat can't ask

scanner_minimap

read

The job so far as a simple line map: walls, numbered stations (amber when not trusted), wall targets; redrawn after every station (bridge 0.11.0+)

scanner_wall_face

read

How much of a wall face the registered stations saw: share of its length, spacing, stations, unseen stretches (bridge 0.9.0+)

scanner_update_targets

write

Adds or updates ledger targets: open, covered (by stations), measured (with a measurement), declined (with a reason). A wall target can carry a question (question: "plumb", bridge 0.15.0+), covered only by its answer, and the job's tolerance in the person's words

scanner_ask

write

Asks a question target of the point cloud now (bridge 0.15.0+): answer with plumb (mm and inches, over what height) and flatness and the stations it rests on, and within or outside the target's tolerance; not_yet with what is missing and the planner subject for each unseen stretch, or does_not_fit. Numbers only with an answer. Up to 90 s

scanner_ready_to_leave

write (asks the person)

Says the job is done (refused while a target is open) and asks the person to sign it off in the chat, or relays their words

scanner_coverage

read

A scan's point spacing and coverage plan (seen, shadows), with the plan image (bridge 0.6.0+)

scanner_propose

write

Propose a person step's answer (e.g. the box around the window) for a person to confirm (bridge 0.3.0+)

scanner_cancel_run

destructive

Stop a run and any scan in flight

scanner_log

read

The field log

scanner_add_note

write

Add a note to the field log

Prompt (bridge mode)

Prompt

Arguments

What it does

survey_mission

project_id, goal

Runs a site visit toward one goal: asks once about processing on the laptop, asks which one when the view shows more than one thing the job could mean, sets the mission, scans station by station with the minimap and registration, proposes each next station by the landmark the person walks past, and ends with the one sign-off

run_recipe

recipe_id, project_id

Fetches the recipe from the bridge and returns step-by-step instructions for walking it with the person: which inputs to ask for, then scanner_start_run and scanner_run, how each action / person / check step is handled, to ask for approval when the run waits for it, and to report measured values with their uncertainty.

Direct mode has no prompts.

The person, in the chat

AI-first: the person says what they want and signs off the result; the agent does the rest. Two moments need the person, and both are asked in the chat itself, through MCP elicitation, where the answer comes from the person and not from the model:

  1. Permission for the visit. scanner_start_run and scanner_start_scan check for a grant in the project. Without one it asks once: "Let the agent scan in "" for the next 8 hours (up to 20 scans)?". Yes creates the grant with SCANNER_PERSON_TOKEN; no starts nothing. Later runs in the visit ask nothing.

  2. The sign-off. scanner_sign_off(run_id, summary) asks about the bridge's proposal at the run's sign-off step: confirm it, or change the numbers, which the form shows prefilled with the proposal. The agent shows the evidence image in the chat first. This is the run's one person intervention.

Clients (October 2026): the Claude Code CLI shows elicitation from 2.1.76. The Claude desktop app's Code tab declares it but closes the question without showing it (seen 2026-10-21), and the desktop chat and claude.ai don't support it. A Claude Code Elicitation hook can answer without showing a dialog; don't configure one for this server.

When the chat can't ask (or closes the question unshown), the tool says so and tells the agent to ask the person in plain chat, then call again with person_said = the person's exact words (and, for the sign-off, corrections for numbers they changed). That is weaker: the model carries the person's answer. So the bridge records it: the grant's note reads "relayed by the agent: "…"", the sign-off's input has confirmed_via, and the run counts it in interventions.relayed. Questions answered in a dialog are noted "asked in the chat".

Missions: a defined "done"

The person's sentence ("scan the exterior of the house", "measure all the windows") becomes the project's mission: their goal, a kind, and done-when criteria the agent writes, concrete enough to check. The agent keeps a ledger of targets (each facade, each window) as it works: open, covered by named stations, measured, or declined with a reason. It chooses the next station for what is still open, and scanner_ready_to_leave is refused while anything is. Then the person signs the job off once, in the chat (or relayed), and the mission is done. The survey_mission prompt runs the whole visit.

From bridge 0.9.0 the bridge registers every full dome into one project frame by itself. Facade targets then name their wall (wall_id from scanner_registration), and the bridge covers them from the geometry: a wall seen over 80% of its length is covered, with the stations that saw it, and covered is refused while less is seen.

Safety

For agents, in short:

  • Permission: scans run under a grant the person gives once per visit, in the chat. Never use the operator (person) token yourself; the server uses it only after the person said yes.

  • Secrets: never print, paste or log tokens or the scanner's Wi-Fi passphrase. Keep tokens in files (~/x9-bridge/*.token), not in client settings: prefer the launcher over --env SCANNER_BRIDGE_TOKEN=…, which stores the token in the client's config.

  • The laser pointer (the scanner's own laser-pointer API, not exposed by these tools): only with the person at the scanner and having said so, and never without explicit angles aimed at the floor.

Read-only by default (direct mode)

Without SCANNER_ALLOW_ACTUATION the actuating and destructive tools are not registered, so a client sees a read-only server and cannot call them. Starting a scan moves the instrument and fires its laser; make sure the instrument is safely set up before enabling them.

Launch checks (direct mode)

The scanner answers HTTP 202 even to a scan request it cannot parse, and then runs a default full-dome scan. It also ignores projectUUID unless projectName matches too. So scanner_launch_scan and scanner_launch_area_scan, in this order:

  1. Validate the angles (area scan): delta_ha in (0, 2π], 0 ≤ start_va, 0 < delta_va, start_va + delta_va ≤ π. start_ha is wrapped into [0, 2π).

  2. Refuse while busy: if any task is running or queued, they fail with instrument is busy: <type> <taskUUID> (<state>)… and send nothing. Another client, or another agent, may be driving the instrument.

  3. Check the project: project_uuid must parse as a UUID, and a project with that name and that UUID must exist.

  4. Check the mode: scan_mode must be one of the names in scanner_scan_modes.

  5. Send, then compare the echo: the instrument echoes the request it accepted. Every field sent (scanMode, projectName, projectUUID, scanType, fastMode, colorMode, and the four angles for an area scan) must come back unchanged (floats to within 1e-9). On any mismatch the server cancels the task at once and returns instrument did not accept the request as sent (sent vs echoed: …); cancelled its task (HTTP <status>).

scanner_delete_scan and scanner_delete_project refuse in the same way while any task is running or queued: a scan being filed renumbers the scan and project lists that the delete verification relies on, and deleting the project a scan is writing into is never wanted. scanner_create_project does not check, since a new project does not touch the running task. scanner_cancel_task is meant for a running task, so it does not check either.

Delete by UUID, verified (direct mode)

Scan and project ids on the scanner are list positions and are renumbered after every delete. The delete tools therefore take the stable scanUUID or projectUUID, look up its current id immediately before deleting, and read the list again afterwards. The delete is reported as done only if that UUID is gone and the list is exactly one shorter; otherwise the tool fails with delete of <uuid> not confirmed; re-check <kind> before retrying.

Downloads stay in one directory (both modes)

Download tools write only inside SCANNER_DOWNLOAD_DIR:

  • filename must be a plain name: it starts with a letter or digit, then up to 127 more letters, digits, ., _ or -. No directories, no .., no leading dot.

  • The resolved path must sit directly in the download directory, so a symlink that points elsewhere is refused.

  • An existing file is kept unless overwrite=true.

  • Data streams into <name>.part and is renamed into place only when complete; the .part file is removed on failure.

scanner_preview_to_angles reads files only from the same directory, by plain name.

Secrets masked (direct mode)

The scanner returns its Wi-Fi passphrase in plain text in its status. scanner_status replaces every non-empty string value whose key ends in key, psk, passphrase, password, secret or token (any case) with ***, at any depth. Booleans such as isFactoryKey are kept. Only scanner_status is masked. In bridge mode the bridge decides what its status contains.

Bridge mode

The bridge enforces approvals, battery limits, a busy scanner, a scanner that moved since the reference scan, and request echo checks, for every client. This server adds only input shape checks (for example, all four window angles or none) and the download-directory rules above. Ids (scan_id, job_id, export_id, run_id, recipe_id) are sent as single URL path segments with every reserved character escaped, and an empty id, . or .. is refused, so an id cannot reach a different bridge endpoint. Whether projects can be created while the scanner is busy is the bridge's decision; bridge mode has no delete tools.

Errors

Tool failures are raised as MCP tool errors (ToolError), so the message text reaches the client on both mcp 1.x and mcp 2.x (mcp 2.x hides the text of other exceptions). The messages are:

Situation

Message

Scanner answered with an HTTP error (direct)

HTTP <status> from <url>: <first 200 chars of body>

Scanner unreachable (direct)

instrument unreachable (<ErrorType>: <detail>)

Bridge refused (bridge)

<code>: <title> — <detail> (hint: <hint>), from the bridge's problem response, e.g. battery_low, instrument_moved, invalid_request

Bridge answered an error without a problem body (not JSON, or JSON without code/title)

HTTP <status>: <first 200 chars of body>

Scanner or bridge answered success with a body that is not JSON

instrument sent a response that is not JSON (<detail>) / bridge sent a response that is not JSON (<detail>)

Truncated or malformed .tzf or JPEG in scanner_preview_to_angles (direct)

<name> is truncated or not a scan file / <name> is a truncated or malformed JPEG

Any other bad value in a reply or file

truncated or malformed data (<detail>) or invalid value (<ErrorType>: <detail>)

Bridge unreachable (bridge)

bridge unreachable (<ErrorType>: <detail>)

Validation, busy, echo, delete and file-name checks

A plain sentence saying what was wrong and what to do, e.g. unknown scan_mode 'X'; valid: …, <path> already exists; pass overwrite=true to replace it

Client configuration

The usual setup: the scanner talks to trimble-x9-bridge on a Raspberry Pi by the tripod (its ops/README.md builds the Pi), this server runs on the same Pi, and the agent on the laptop starts it over ssh. The launcher extras/trimblex9-mcp-auto (installed at ~/x9-bridge/bin/) runs bridge mode with the bridge's agent token from ~/x9-bridge/agent.token, and passes the person's token from ~/x9-bridge/operator.token as SCANNER_PERSON_TOKEN, used only after the person says yes in the chat. With an ssh alias x9-bridge for the Pi (key login, no prompts):

claude mcp add -s user x9-bridge -- ssh -o BatchMode=yes x9-bridge 'X9_MCP_MODE=bridge ~/x9-bridge/bin/trimblex9-mcp-auto'

Quote the remote command, so ~ is the Pi's home, not the laptop's.

Check: the client lists the scanner_* tools, and scanner_status shows the bridge and the scanner. X9_MCP_MODE=direct starts the same server in direct mode for diagnostics; it stays read-only while the bridge runs.

Claude Code

Direct mode, read-only:

claude mcp add trimblex9 -- trimblex9-mcp

Direct mode with actuation and custom paths:

claude mcp add trimblex9 \
  --env SCANNER_ALLOW_ACTUATION=1 \
  --env SCANNER_BASE=http://192.168.43.1:34567/api/v1 \
  --env SCANNER_DOWNLOAD_DIR=$HOME/scans \
  -- trimblex9-mcp

Without installing first:

claude mcp add trimblex9 -- uvx --from "git+https://github.com/northidaholabs/TrimbleX9-mcp@v0.15.0" trimblex9-mcp

Bridge mode:

claude mcp add trimblex9 \
  --env SCANNER_BRIDGE_URL=http://bridge.local:8795/v1 \
  --env SCANNER_BRIDGE_TOKEN=<agent token> \
  -- trimblex9-mcp

Generic mcpServers JSON

For clients configured with an mcpServers object (Claude Desktop, many others):

{
  "mcpServers": {
    "trimblex9": {
      "command": "trimblex9-mcp",
      "env": {
        "SCANNER_BASE": "http://192.168.42.1:34567/api/v1",
        "SCANNER_DOWNLOAD_DIR": "/home/user/scanner-downloads"
      }
    }
  }
}

Add "SCANNER_ALLOW_ACTUATION": "1" to env to enable actuation. For bridge mode, set SCANNER_BRIDGE_URL and SCANNER_BRIDGE_TOKEN instead. If trimblex9-mcp is not on the client's PATH, give its full path (for a virtualenv, <venv>/bin/trimblex9-mcp).

On a remote host over SSH (e.g. a Raspberry Pi by the scanner)

The scanner is usually reached from a small computer on site, not from the machine running the agent. Install the server on that host and let the client start it over SSH; MCP's stdio transport passes through ssh unchanged. With the bridge on that host, use the launcher as in On a field Pi, through the bridge. Without a bridge (direct mode):

# on the agent's machine
claude mcp add trimblex9 -- ssh -T pi@scanner-pi.local \
  'SCANNER_DOWNLOAD_DIR=$HOME/scanner-downloads ~/x9-bridge/.venv/bin/trimblex9-mcp'
{
  "mcpServers": {
    "trimblex9": {
      "command": "ssh",
      "args": ["-T", "pi@scanner-pi.local",
               "SCANNER_ALLOW_ACTUATION=1 ~/x9-bridge/.venv/bin/trimblex9-mcp"]
    }
  }
}

Notes:

  • Set the environment variables in the remote command, as above. A client's --env / "env" applies to the local ssh process and is not forwarded to the remote host.

  • Use key-based login with no password prompt (ssh -T and no TTY); the client cannot answer prompts.

  • On current Raspberry Pi OS and Debian the system pip refuses to install into the system Python; use pipx or a virtualenv, and point the command at that trimblex9-mcp.

  • Downloaded files land on the remote host, in its SCANNER_DOWNLOAD_DIR.

  • Use the full path to trimblex9-mcp if the remote non-interactive shell's PATH does not include it.

Compatibility

Item

Supported

Python

3.10 and later

mcp SDK

1.x (FastMCP, 1.7 or later, see below) and 2.x (MCPServer); the import is chosen at run time

httpx

0.27 and later

OS

Linux and macOS (anything that can reach the scanner's network link)

Transport

stdio

CI runs the tests on Python 3.10 with mcp 1.7 (the floor; pinned with pydantic<2.14, because mcp 1.7 imports a pydantic internal that 2.14 removed), on Python 3.10 with the newest mcp 1.x, and on Python 3.13 with mcp 2.x. If you must stay on mcp 1.7, keep pydantic below 2.14. The tools use tool annotations, which FastMCP accepts from mcp 1.7, so the requirement is mcp>=1.7,<3.

Tests

python3 -m venv .venv
.venv/bin/pip install -e . pytest
.venv/bin/pytest -q

Test file

Needs

Covers

tests/test_offline.py

nothing (starts in-process stubs of the scanner and bridge APIs)

Direct mode with actuation on: delete by UUID across renumbering, local launch validation, area-scan echo check, cancel on a mismatched echo, scanner_preview_to_angles including the seam wrap, launch and delete refused while busy, readable errors for truncated .tzf/JPEG files, non-JSON replies and an unreachable scanner, plain default download names, Wi-Fi key masking. Bridge helpers: ids escaped in URL paths, sanitised default download names, readable errors for non-JSON and non-problem replies

tests/test_bridge_mode.py

X9_BRIDGE_BIN

Bridge mode against a real bridge and scanner simulator: tool list has no approve/grant/delete tools, approval flow and progress, bridge error codes, an escaped id that would otherwise reach another endpoint, recipe prompt and run, field log

Bridge-mode tests are skipped unless these are set:

Variable

Effect

X9_BRIDGE_BIN

Directory holding the x9-bridge and x9-mock binaries (from the bridge project's cargo build --release). The tests start both on 127.0.0.1:34611 and 127.0.0.1:8811. The recipe test also needs the bridge project's test recipe at <bin>/../../crates/x9-bridge/tests/recipes/window-measurement.toml, and is skipped without it.

X9_FIXTURES

Directory of dome/area .tzf and .jpg fixtures for the simulator. With it, the preview, aim and export test runs too.

X9_BRIDGE_BIN=/path/to/bridge/target/release pytest -q tests/test_bridge_mode.py

Development

.venv/bin/trimblex9-mcp        # run over stdio
python scanner_mcp.py          # same, from a checkout

scanner_mcp.py is direct mode and the entry point; scanner_bridge.py is bridge mode and reuses the download-path helpers from scanner_mcp.py.

License

MIT (see LICENSE).

Available Tools

11 tools
scanner_download_previewB

Download a scan's panoramic preview JPEG into the server's download directory (default name 'scan--preview.jpg'); same rules as scanner_download_scanfile.

NameRequiredDescriptionDefault
filenameNo
overwriteNo
scan_file_idYes

TDQS

B3.1/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Adds real behavioral context beyond the annotations: the artifact lands in the server's download directory and gets a derived default filename. However, it says nothing about what happens if the file already exists (the overwrite flag exists), whether the download can fail on missing previews, or how the resulting path is reported; the 'same rules as ...' clause pushes the rest onto a sibling description.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One front-loaded sentence with the verb and artifact first and the parenthetical default kept tight. Minimal waste, though the trailing cross-reference is a partial substitute for information rather than genuine conciseness.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

A file-writing tool with no output schema, three parameters at 0% schema coverage, and openWorldHint=true needs more: how overwrite and filename interact, what scan_file_id is, and what the caller gets back (path, bytes, URL). The definition leaves these to an external sibling reference an agent may not have in context.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must carry parameter meaning, and it largely does not. It hints at the filename default ('scan-<id>-preview.jpg', ambiguous whether id is the scan or scan_file_id) but never explains the overwrite parameter or what scan_file_id must reference.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Names a specific verb (download) and resource (a scan's panoramic preview JPEG) plus the destination (the server's download directory). This distinguishes it from scanner_download_scanfile by artifact type, though the distinction is by resource only and not reinforced with an explicit contrast.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is only implied: the agent must infer it wants the preview rather than the full scan file. The phrase 'same rules as scanner_download_scanfile' defers to a sibling instead of stating when to pick preview versus scanfile, and gives no exclusions or prerequisites.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

scanner_download_scanfileA

Download a scan's point-cloud file into the server's download directory (SCANNER_DOWNLOAD_DIR). filename is a plain name (default 'scan-.tzf'); paths are rejected. Existing files are kept unless overwrite is true. Streams the full file; returns bytes and path.

NameRequiredDescriptionDefault
filenameNo
overwriteNo
scan_file_idYes

TDQS

A4.1/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations give only a coarse profile (readOnly=false, destructive=false, openWorld=true); the description adds the substantive detail an agent needs: the file lands in a server-side directory rather than the client, the default filename pattern, path rejection, the no-clobber default with an explicit overwrite escape, full-file streaming, and the returned bytes/path. The write-to-disk behavior is consistent with readOnlyHint=false and the non-destructive default is consistent with destructiveHint=false.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The destination and core action come first, followed by the two parameter caveats and the return summary. Every sentence conveys a distinct operational fact with no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 3-parameter tool with no output schema, the description is nearly self-sufficient, including where output lands and what is returned. It does not say how failures are surfaced (e.g., unknown scan_file_id, insufficient disk space), which is the one operational gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the description must carry parameter meaning and it largely does: filename is described as a plain name with a concrete default ('scan-<id>.tzf') and a hard rejection rule, and overwrite is defined in terms of preserving existing files. scan_file_id is only implicitly characterized as identifying the scan file, which is the remaining gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific verb and resource ('Download a scan's point-cloud file') and pins the destination ('into the server's download directory (SCANNER_DOWNLOAD_DIR)'). That scope implicitly separates it from scanner_download_preview, but no sibling is named explicitly, so differentiation is left to inference.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Constraints that shape invocation are stated clearly: 'filename is a plain name ... paths are rejected' and 'Existing files are kept unless overwrite is true.' However, there is no explicit guidance on when to pick this over scanner_download_preview or scanner_scanfile_info, so the usage condition is only implied by the word 'point-cloud'.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

scanner_errorsA
Read-only

Get the instrument's current error list (type, errorLevel, description).

NameRequiredDescriptionDefault

No parameters

TDQS

A3.7/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The readOnlyHint=true annotation already establishes this is a safe read, so the description's burden is lower. It usefully adds that the list is 'current' (live/transient rather than historical), but does not say whether errors persist, are cleared on read, or are scoped to the instrument session.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single sentence with the resource and the returned fields front-loaded; every clause earns its place and nothing is padded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, so naming the three returned fields (type, errorLevel, description) is genuinely valuable compensation. What is missing is small but real: nothing about whether the list can be empty, how errors age out, or how this differs from scanner_status.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so there is nothing to document and the baseline of 4 applies. The description correctly adds no parameter noise.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb ('Get') and resource ('the instrument's current error list') and even enumerates the returned fields (type, errorLevel, description). It is clear what the tool returns, though it never explicitly distinguishes itself from ambiguous siblings like scanner_status, which an agent might reasonably confuse with an error-retrieval tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied rather than stated: 'current error list' suggests troubleshooting/diagnostic use, but there is no explicit 'use this when...' or 'use scanner_status instead for...' guidance. With a sibling set of eleven scanner tools including a status tool, an explicit boundary would have real value.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

scanner_preview_to_anglesA
Read-only

Convert a box drawn on a scan's preview JPEG into instrument angles for scanner_launch_area_scan. Both files must already be in the download directory (scanner_download_scanfile + scanner_download_preview of the same scan, taken from the station the instrument is still standing on). (x0, y0)-(x1, y1) are preview pixel corners, y down; if x1 < x0 the box wraps across the panorama seam. margin_deg pads every side. Returns start_ha/delta_ha/start_va/delta_va in radians (VA from zenith).

NameRequiredDescriptionDefault
x0Yes
x1Yes
y0Yes
y1Yes
margin_degNo
scan_filenameYes
preview_filenameYes

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=false, and the description is consistent with a pure computation. Beyond the annotations it discloses real behavior: y-down pixel coordinates, seam wrapping when x1 < x0, margin padding on every side, and output units (radians, VA measured from zenith). It omits error behavior for malformed boxes or mismatched file pairs.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the purpose, then preconditions, then coordinate semantics, then the return contract. Dense and technical with several parentheticals, but each sentence adds information an agent would otherwise have to guess.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, and the description compensates by naming every returned field (start_ha/delta_ha/start_va/delta_va) with units and the VA reference axis. Combined with the prerequisite chain and coordinate conventions, an agent has everything needed to call this correctly in sequence.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the description carries the burden and mostly succeeds: it defines (x0,y0)-(x1,y1) as preview pixel corners with y down, explains the seam-wrap edge case, and says margin_deg pads every side. The two filename parameters are covered only implicitly — the agent must infer they are bare filenames in the download directory rather than paths.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: convert a box on a scan's preview JPEG into instrument angles, and names the downstream consumer scanner_launch_area_scan. An agent can distinguish this from every sibling in the list (download, status, scanfile_info) without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit preconditions: both files must already sit in the download directory and must come from scanner_download_scanfile + scanner_download_preview of the same scan, taken from the station the instrument is still standing on. That is a genuinely useful pipeline ordering constraint. It does not state failure behavior or a when-not case, which keeps it short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

scanner_projectsA
Read-only

List all projects on the instrument (id, projectName, projectUUID, nextProjectScanID).

NameRequiredDescriptionDefault

No parameters

TDQS

A3.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=true, so the safety profile is covered; the description contributes the 'on the instrument' scope and the exact field list, which tells the agent what shape of data comes back. It says nothing about ordering, pagination, or behavior when no instrument is connected.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One sentence, front-loaded with the verb and resource, with the field list tucked into a compact parenthetical. Nothing is wasted and nothing important is buried.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, listing id/projectName/projectUUID/nextProjectScanID in the description is the only disclosure of return shape and is appropriately included. The remaining gap is only the lack of any note on how downstream tools consume these project identifiers.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Zero parameters, so the baseline is 4 — there is nothing for the description to compensate for. The parenthetical does useful double duty by naming returned fields rather than arguments.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Specific verb 'List' plus a precise resource ('projects on the instrument') and an enumeration of the fields returned. It is clearly distinguishable from siblings like scanner_scans or scanner_scan_modes by the word 'projects', though it never explicitly contrasts itself with them.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance on when to call this versus scanner_scans or scanner_status, and no stated prerequisites (e.g. needing a connected instrument). The likely use — resolving project IDs/names for other scanner calls — is left entirely to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

scanner_scanfile_infoB
Read-only

Get metadata for one point-cloud file: file path, size, point count, and whether it has been downloaded.

NameRequiredDescriptionDefault
scan_file_idYes

TDQS

B3.3/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The readOnlyHint annotation already tells the agent this is a safe read, and the description usefully discloses the returned fields (path, size, point count, downloaded flag). It says nothing about behavior on an invalid or missing scan_file_id, nor about permissions beyond openWorldHint.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One front-loaded sentence with zero filler; the verb and resource lead and the returned fields follow immediately.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter read-only lookup with annotations covering safety and no output schema, listing the return fields gets close, but an agent still lacks guidance on sourcing scan_file_id and on failure behavior.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% and the single required parameter scan_file_id is never mentioned in the description — no hint as to what an ID looks like or where to obtain one (presumably from scanner_scans). The description only describes outputs, not the input, so it does not compensate for the coverage gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description gives a specific verb and resource ('Get metadata for one point-cloud file') and enumerates exactly which fields come back, making it distinguishable from siblings like scanner_download_scanfile. It does not explicitly name siblings, so it stops short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The mention of 'whether it has been downloaded' hints at the natural check-before-download workflow, but the description never states when to use this versus scanner_download_scanfile or scanner_status. Usage is implied rather than instructed.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

scanner_scan_modesA
Read-only

List the available scan modes (the density/range/time menu): mode name, range (m), point count, file size, scan time (s), spacing, and rate. Use these names when starting a scan.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, so safety is covered; the description adds the useful behavioral detail that the response is a live menu of currently available modes with specific attributes. It does not note that the set of modes is device/browser-dependent (openWorldHint), which is the one piece of context an agent might trip on.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, front-loaded with the action and resource, then the payload fields, then the usage note. Every clause carries information and nothing is repeated from annotations.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description carries the burden of describing returns and does so by enumerating the fields (mode name, range, point count, file size, scan time, spacing, rate). Complete for a lookup tool; only the dynamic/environment-dependent nature of the list is unstated.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so there is nothing for the description to disambiguate and the baseline is 4. The schema is empty and fully described by its emptiness.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('List the available scan modes') and immediately clarifies what a scan mode is with the parenthetical 'density/range/time menu'. No sibling tool (scanner_scans, scanner_status, etc.) returns this menu, so the distinction is unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

'Use these names when starting a scan' gives a concrete downstream usage instruction that tells the agent this is a discovery/lookup step feeding a scan-start call. It stops short of naming the specific scan-starting sibling or stating when not to call it, so it is clear context rather than explicit routing.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

scanner_scansA
Read-only

List all scans as compact records (id, scanUUID, scanMode, points, file path, and the project folder the scan belongs to). Ids are list positions and change when scans are deleted; scanUUID is stable.

NameRequiredDescriptionDefault

No parameters

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover the safety profile (readOnlyHint, openWorldHint), yet the description still adds real behavioral context: 'compact records' warns output is a summary, and the note that ids are volatile list positions while scanUUID is stable is a concrete caveat an agent must know to avoid caching a bad key. It does not disclose ordering, size limits, or auth requirements.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, zero filler. The payload (what is listed, what fields come back) is front-loaded, and the id-versus-scanUUID caveat follows immediately as the single most actionable warning.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description carries the return-value burden and largely discharges it by naming the six record fields. What is left unsaid — ordering, whether the list is bounded, whether it spans all projects — is minor for a no-parameter read tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Zero parameters, so the baseline is 4; there is nothing in the schema for the description to clarify. The description instead spends its words on the shape of the returned record, which is a reasonable use of the space.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('List all scans') and enumerates the exact fields returned (id, scanUUID, scanMode, points, file path, project folder), which distinguishes it implicitly from detail-oriented siblings like scanner_scanfile_info. It never names a sibling explicitly, so it stops short of the top score.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is only implied: 'List all scans' tells an agent this is the overview/listing entry point, and 'compact records' hints that it is not for full detail. There is no explicit when-to-use or when-not statement despite ten siblings (scanner_scanfile_info, scanner_projects, scanner_tasks) that an agent could easily confuse it with.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

scanner_statusA
Read-only

Get the instrument's current status: battery, AC state, leveling, on-board storage, IMU, laser-pointer, WiFi config (passphrase masked), and the active error list.

NameRequiredDescriptionDefault

No parameters

TDQS

A3.7/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already establish readOnlyHint=true and openWorldHint=true, so the safety profile is covered. The description adds one genuinely useful disclosure — the WiFi passphrase is masked — but says nothing about whether the call actively polls hardware, how long it takes, or whether values are cached.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single dense sentence that front-loads the verb and then lists the returned state categories. No filler, no restating of the tool name.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description carries the burden of describing the return value, and it does so by enumerating the status categories. It stops short of explaining shapes or units (e.g. battery as percent vs. voltage), which is a minor but real gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so the baseline is 4. Nothing about argument semantics needs explaining, and the description correctly implies a parameterless call.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clear verb+resource: 'Get the instrument's current status', then enumerates the exact fields returned (battery, AC, leveling, storage, IMU, laser, WiFi, errors). That enumeration implicitly distinguishes it from narrow siblings like scanner_errors or scanner_scan_modes, but no sibling is named explicitly.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage (a snapshot of device health) but never says when to call this vs. scanner_errors, scanner_tasks, or scanner_scan_modes. The overlap with scanner_errors — since the active error list is included here — is left for the agent to infer.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

scanner_tasksA
Read-only

List running or queued tasks (e.g. a scan in progress) with their state and percent complete (0-100). An empty list means the instrument is idle.

NameRequiredDescriptionDefault

No parameters

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=true, so the safety profile is covered. The description adds real value on top: it names the returned fields (state, percent complete with the 0-100 range) and, most usefully, explains that an empty list means the instrument is idle rather than an error — a non-obvious interpretation rule an agent would otherwise guess at.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences with no filler. The core purpose is front-loaded in the first clause, and the idle-state interpretation follows immediately as the one piece of non-obvious context.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no parameters and no output schema, the description carries the burden of describing the response, and it does so adequately by naming state, percent complete, and the empty-list meaning. It falls short only on routing: nothing tells the agent how this differs from scanner_status or when to poll this versus scanner_wait_for_scan.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so there is nothing for the description to disambiguate and the baseline of 4 applies. The description correctly spends no words on arguments and instead uses the space for return-value semantics.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource (list running or queued tasks) and describes the payload: state plus percent complete (0-100). That is concrete enough to distinguish it from write tools like scanner_preview_to_angles or scanner_download_scanfile, but it never differentiates itself from the closely-related scanner_status or scanner_wait_for_scan, which also read scanner state.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is only implied: an agent can infer 'call this to see if a scan is in progress' from the scan-in-progress example and the idle-when-empty note. There is no explicit statement of when to prefer this over scanner_status or scanner_wait_for_scan, and no exclusions or prerequisites are given.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

scanner_wait_for_scanA
Read-only

Wait until no task is running (use after scanner_launch_scan), polling the task list every poll_s seconds (0.5-30) for up to timeout_s (1-7200). Reports progress to the client when supported. Returns {done, timed_out, elapsed_s, last} where last is the final task snapshot.

NameRequiredDescriptionDefault
poll_sNo
timeout_sNo

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover read-only and open-world nature, and the description adds real behavior: bounded polling interval, bounded timeout, and client progress reporting. It does not state what happens when no tasks are running at call time (immediate return?) or whether the timeout is a hard error, which keeps it from a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, front-loaded with the blocking behavior and the sequencing hint, then constraints, then return shape. Nothing is redundant.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, and the description compensates by naming the return fields {done, timed_out, elapsed_s, last} and explaining that `last` is the final task snapshot. For a two-parameter wait tool this is sufficient to invoke and interpret results correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0% and carries only defaults and titles, so the description supplies the missing semantics: valid ranges of 0.5-30 for poll_s and 1-7200 for timeout_s. It does not describe unit rounding or what happens if poll_s exceeds timeout_s, but the core meaning is added beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific blocking verb ('wait') on a specific resource state ('until no task is running'), and ties it to the launch tool, which cleanly separates it from sibling reads like scanner_status or scanner_tasks. An agent can tell what this does without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says 'use after scanner_launch_scan', which gives a clear sequencing rule. It does not contrast with scanner_tasks/scanner_status or state when-not to use it (e.g., if you just want a one-shot snapshot), so it stops short of full alternative guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 11 tool updatesv0.12.1
    • First observedscanner_download_preview
    • First observedscanner_download_scanfile
    • First observedscanner_errors
    • First observedscanner_preview_to_angles
    • First observedscanner_projects
    • First observedscanner_scan_modes
    • First observedscanner_scanfile_info
    • First observedscanner_scans
    • First observedscanner_status
    • First observedscanner_tasks
    • First observedscanner_wait_for_scan

TDQS

A3.6/5.0

Scored across 11 tools

Disambiguation3/5

Most tools are clearly separable, but several pairs overlap: scanner_status already returns the active error list that scanner_errors returns separately, scanner_scans records already include points and file path that scanner_scanfile_info extends, and scanner_wait_for_scan is a polling wrapper around scanner_tasks. Descriptions mitigate these overlaps, but an agent still has to choose between near-duplicates.

Naming Consistency4/5

All tools share a uniform snake_case 'scanner_' prefix, which makes the set very readable as a family. The only minor deviation is that some tools are bare noun getters (scanner_status, scanner_projects, scanner_errors) while others use verb phrases (scanner_download_scanfile, scanner_wait_for_scan, scanner_preview_to_angles).

Tool Count5/5

At 11 tools the set is well-scoped for an instrument-control server: inspection (status, projects, scans, modes, errors, tasks), acquisition helpers (wait_for_scan, file info, two downloads), and one geometry converter each earn their place. Nothing feels padded or redundant by count.

Completeness2/5

Descriptions repeatedly reference scanner_launch_scan and scanner_launch_area_scan, but neither tool is present, so scanner_preview_to_angles and scanner_wait_for_scan point at operations the agent cannot perform — a severe dead end for the core workflow of actually starting a scan. There are also no create/delete operations for projects or scans, only listings.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    Enables AI assistants to query Bambuddy-managed 3D printers' status, current prints, diagnostics, camera snapshots, and archived print sources, and to safely start prints via a two-stage confirmation token while keeping sensitive credentials out of MCP output.
    9
    AGPL 3.0
  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables local Mac agents to monitor and control Bambu Lab 3D printers via MCP tools, including status checks, camera snapshots, and print job management.
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables coding agents to inspect and control BLACS and Runmanager over local Windows or SSH connections, including device-tab and queue state, shot queue editing, application restarts, Runmanager globals and compilation, and leased multi-command operations.
    MIT