MCP server for fixRAgent: send a property maintenance photo, get back what it is, how urgent it is, and which trade to call.
概览
Connect an AI assistant to the remote MCP server at (nothing to install) and give it one photo a resident sent: you get back a Triage Profile with what it is, how urgent it is (one of four words: EMERGENCY, TODAY, THIS WEEK, WHENEVER), which trade to call, and a line to say to the resident. Made by AR Logic LLC, Saint Marys, Ohio. Website: fixragent.com. Setup for each assistant: fixragent.com/connect-ai. Agents installing this server: read llms-install.md. The server runs at (streamable HTTP). It has three tools: A free key is issued at fixragent.com/docs#key. Send it as the x-triage-key header, an Authorization: Bearer header, or ?k= on the server URL. A call with no key of its own (keyless assess_property_photo, and every get_triage_profile read) also counts against 20 requests an hour per address; an issued key is judged by the key (its own per-minute burst and daily limit), and try_sample has its own per-address counter.
README
fixragent-mcp
fixRAgent is maintenance triage for property managers, landlords and the companies that run buildings. Connect an AI assistant to the remote MCP server at https://fixragent.com/mcp (nothing to install) and give it one photo a resident sent: you get back a Triage Profile with what it is, how urgent it is (one of four words: EMERGENCY, TODAY, THIS WEEK, WHENEVER), which trade to call, and a line to say to the resident.
Made by AR Logic LLC, Saint Marys, Ohio. Website: fixragent.com. Setup for each assistant: fixragent.com/connect-ai. Agents installing this server: read llms-install.md.
Start here: the remote server (nothing to install)
The server runs at https://fixragent.com/mcp (streamable HTTP). It has three tools:
| tool | what it does | key needed? |
|---|---|---|
try_sample |
returns a stored assessment of a sample photo from fixRAgent’s test set, labelled SAMPLE | no; spends nothing |
assess_property_photo |
reads one photo (image_base64 with mime_type, or image: a file reference with an https download_url and a file_id) and returns the fixed triage |
no key: up to 3 real reads per connection in a rolling 24 hours, shared across keyless callers up to a daily ceiling; past that it returns the SAMPLE. With a free key: 60 a day |
get_triage_profile |
reads a saved assessment back by its diagnosis_id |
no (since 9 Oct 2026); calls no model and spends no daily allowance |
A free key is issued at fixragent.com/docs#key. Send it as the x-triage-key header, an Authorization: Bearer header, or ?k= on the server URL. A call with no key of its own (keyless assess_property_photo, and every get_triage_profile read) also counts against 20 requests an hour per address; an issued key is judged by the key (its own per-minute burst and daily limit), and try_sample has its own per-address counter.
Claude Code
claude mcp add --transport http fixragent https://fixragent.com/mcp
Claude, ChatGPT, Cursor, VS Code: add a custom connector (remote MCP server) with the URL https://fixragent.com/mcp. A step-by-step guide and one-click buttons for each are on fixragent.com/connect-ai.html.
Gemini CLI: see Gemini CLI below.
Smithery: listed as williamjohnson4102/fixragent. Smithery’s gateway passes requests through to https://fixragent.com/mcp; the optional key is the one setting (triageKey, sent as the x-triage-key header). The settings schema Smithery shows is smithery/config-schema.json.
Transport: streamable HTTP at https://fixragent.com/mcp. Discovery (initialize, tools/list, ping) needs no key. The local server.js below speaks stdio and lists the same three tools.
Usage
- Ask the assistant to call
try_sample. It returns a stored result for a sample photo, labelled SAMPLE, so you can see the shape of a Triage Profile before sending anything of your own. - Give it a photo a resident sent, with the problem in the resident’s own words: “Use fixRAgent to assess this photo. The resident says water is pooling under the water heater.” It calls
assess_property_photoand answers with what the asset is, the urgency word, the trade to call, the line to say to the resident and ashare_urlfor the Triage Profile. - Later,
get_triage_profilewith thediagnosis_id(or the last part of afixragent.com/c/link) reads the same profile back.
All three tools carry MCP annotations: try_sample and get_triage_profile are read-only and idempotent; assess_property_photo is not read-only (it stores the Triage Profile and its cleaned photo) and is not destructive.
It is also listed in the Official MCP Registry as io.github.Russ4102/fixragent. This repo’s server.json is a copy of that entry (the remote server, version 1.0.1); there is no npm package. The entry is published from registry/server.json in the plugin repo; change that copy first.
Anything happening right now that needs emergency services (fire, a gas smell, someone hurt) is a 911 call, not a tool call.
The local stdio server (optional)
server.js is a one-file stdio server that lists the same three tools as the remote server (try_sample, assess_property_photo, get_triage_profile) and forwards each call to https://fixragent.com/mcp. Use it if your client or directory runs local servers only (Glama builds and inspects this one); otherwise the remote server above needs nothing installed.
- One file,
server.js. No runtime dependencies. Node 20 or newer. Version 0.2.0. tools/listanswers offline and with no key: the three definitions (name, title, description, input and output schemas, annotations) are a static copy of the remote’stools/list, read 14 Oct 2026. The test compares the copy with the live server and fails when they drift.tools/callfor the three tools is a JSON-RPCtools/callPOSTed tohttps://fixragent.com/mcp; the remote’s result (or its JSON-RPC error) comes back unchanged. A JSON reply and an SSE reply are both read.- The key is optional. If
FIXRAGENT_API_KEYis set, it is sent as thex-triage-keyheader; it is never written to a file, stdout or stderr. With no key the call is made anyway and the remote decides:try_sampleandget_triage_profileneed none, and keylessassess_property_photohas the allowance described above, then returns the SAMPLE. - Speaks MCP over stdio in both eras: the
initializehandshake every shipping client uses today (2025-11-25 back to 2024-11-05) and the per-request_metaform of the 2026-07-28 revision (server/discover,resultType). FIXRAGENT_API_URL(optional) points it at another base; the MCP endpoint is/mcp. The default ishttps://fixragent.com.FIXRAGENT_IMAGE_DIR(optional) is the one folder from whichassess_property_photomay read a photo named byimage_path, an argument local to this server. Unset,image_pathis off and the photo goes asimage_base64(withmime_type) orimage, as on the remote. See Reading photo files.- The older tool
triage_photo(in front ofhttps://fixragent.com/api/triage, key required) is still callable by name for existing setups, but it is no longer listed. See The older tool.
Install
The install is one file: server.js.
mkdir -p ~/fixragent-mcp
curl -fsSL https://raw.githubusercontent.com/Russ4102/fixragent-mcp/main/server.js -o ~/fixragent-mcp/server.js
node ~/fixragent-mcp/server.js --version
The copy served at https://fixragent.com/mcp/server.js was still the older 0.1.0 (one tool) on 14 Oct 2026; --version says which one you have.
--check prints whether the key is set and whether fixragent.com answers. It never prints the key.
Claude Desktop
Edit claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/claude_desktop_config.json; Windows: %APPDATA%\Claude\claude_desktop_config.json), then restart Claude Desktop. The env block is optional.
{
"mcpServers": {
"fixragent": {
"command": "node",
"args": ["/absolute/path/to/fixragent-mcp/server.js"],
"env": { "FIXRAGENT_API_KEY": "your key" }
}
}
}
Claude Code
claude mcp add fixragent -- node /absolute/path/to/fixragent-mcp/server.js
# with a key:
claude mcp add fixragent -e FIXRAGENT_API_KEY=your-key -- node /absolute/path/to/fixragent-mcp/server.js
Cursor
Create .cursor/mcp.json in the project (or ~/.cursor/mcp.json for every project):
{
"mcpServers": {
"fixragent": {
"command": "node",
"args": ["/absolute/path/to/fixragent-mcp/server.js"],
"env": { "FIXRAGENT_API_KEY": "your key" }
}
}
}
Glama (and other directories that build the server)
The Dockerfile copies server.js and runs node server.js; nothing is installed. In Glama’s settings: build steps [], CMD arguments ["mcp-proxy", "--", "node", "server.js"]. No environment variable is needed for tools/list.
Gemini CLI
This repo is also a Gemini CLI extension. It connects to the remote server at https://fixragent.com/mcp (the same three tools as above), so there is nothing to run locally.
gemini extensions install https://github.com/Russ4102/fixragent-mcp
gemini extensions config fixragent # paste your key; it is stored in the system keychain
The key goes out as the x-triage-key header. Without it, Gemini lists the same three tools and the keyless allowance above applies: up to 3 real reads per connection in a rolling 24 hours, then the SAMPLE.
The tools (stdio)
The three tools are the remote’s, word for word; their full descriptions and schemas are in the remote’s tools/list and in REMOTE_TOOLS in server.js.
| tool | arguments | notes |
|---|---|---|
try_sample |
none | “Needs no key and no input, reads no photo and spends nothing.” The result is labelled SAMPLE |
assess_property_photo |
image_base64 + mime_type, or image (download_url, file_id); optional problem_text (at most 1,200 characters), reporter (resident · assessor · technician), depth (fast · deep) |
plus image_path, local to this server (below) |
get_triage_profile |
diagnosis_id |
“Read only: it never re-reads the photo, never calls the model and spends nothing from the daily allowance.” |
image_path (assess_property_photo only): a file inside the FIXRAGENT_IMAGE_DIR folder, read by this server and sent as image_base64 with its mime_type (taken from the file’s first bytes when not given); image_path itself is not sent. JPEG, PNG or WebP, 1 KB to 3 MB. Give one of image_path, image_base64 or image.
The key
Ask at fixragent.com/docs#key, or email [email protected] with one line on what you are building, and a person replies with your key.
The three tools work without a key (the remote decides what a keyless call gets). triage_photo, the older unlisted tool, needs one: without it, it answers, in words, that no key is set and that nothing was sent.
To wire up triage_photo before a key arrives, point the server at the mock, which replays captured replies (details). The mock reads no key, and the tool sends only when one is set, so give it a placeholder. In the client config above, the env block becomes:
"env": { "FIXRAGENT_API_URL": "https://b1442b0b-4a7a-4135-ab4d-9c4b9c1ab0d2.mock.pstmn.io", "FIXRAGENT_API_KEY": "placeholder" }
The mock takes a request body of up to 1 MB, which holds a photo of about 700 KB, so give the tool a small copy of your photo: sips -Z 1024 photo.jpg --out photo-small.jpg on macOS, or on Linux with Pillow:
python3 -c "from PIL import Image, ImageOps; im = ImageOps.exif_transpose(Image.open('photo.jpg')); im.thumbnail((1024, 1024)); im.convert('RGB').save('photo-small.jpg', quality=85)"
The reply is a captured triage, and its share_url points at that captured diagnosis_id. When your key arrives, remove FIXRAGENT_API_URL and put the key in FIXRAGENT_API_KEY.
The older tool: triage_photo (unlisted)
triage_photo is the one tool the 0.1.0 server listed. It is no longer in tools/list, but a client configured to call it by name still can. It calls https://fixragent.com/api/triage directly and needs a key. New setups should use assess_property_photo.
| input | type | notes |
|---|---|---|
image_path |
string | a file inside the FIXRAGENT_IMAGE_DIR folder (off when that is not set); a relative path is taken inside that folder; JPEG, PNG or WebP; 1 KB to 3 MB |
image_base64 |
string | the photo as base64 instead of a path; a data:image/...;base64, prefix is accepted, and line breaks are ignored; 1 KB to 3 MB decoded |
mime_type |
image/jpeg · image/png · image/webp |
required |
problem_text |
string, at most 1,200 characters | the problem in the reporter’s own words; treated as reported symptoms, never as ground truth |
role |
landlord (default) · fixer |
who is asking; changes the wording of the card, not the triage |
config |
fast (default) · deep |
fast reads the photo three times on production and deep five; both report how many reads agreed, and agreement.n is the count that ran — deep takes longer and spends more of the day’s demo budget |
Give image_path or image_base64, never both.
Reading photo files
This applies to image_path on both assess_property_photo and triage_photo. The tools’ arguments are written by the model, and a model can be steered by text it has read (a web page, an email, a file: a prompt injection). If image_path could name any file, a steered model could name a private key or a .env file and the server would send its bytes to the API. So image_path is off by default. To turn it on, name one folder:
"env": { "FIXRAGENT_API_KEY": "your demo key", "FIXRAGENT_IMAGE_DIR": "/Users/me/fixragent-photos" }
claude mcp add fixragent -e FIXRAGENT_API_KEY=your-demo-key -e FIXRAGENT_IMAGE_DIR=/Users/me/fixragent-photos -- node /absolute/path/to/fixragent-mcp/server.js
With the folder set, before anything is sent:
- the file’s real path (symlinks followed) must be inside that folder; a path outside it gets the same sentence whether or not a file is there;
- it must be a regular file of 1 KB to 3 MB;
- its first bytes must be a JPEG, PNG, WebP, GIF or HEIC image.
image_base64 goes through the same checks on the decoded bytes (1 KB to 3 MB, image first bytes), and the string must be base64 and nothing else (A–Z, a–z, 0–9, +, /, = padding; line breaks are ignored). Point FIXRAGENT_IMAGE_DIR at a folder that holds only photos, never at your home folder.
The result is the API’s reply, unchanged, plus two links built from diagnosis_id:
| field | what it is |
|---|---|
diagnosis_id |
the server-minted id; the join key for the outcome |
core |
the 20 fixed fields: asset_type, fault_detected, fault_summary, severity, tier (one of EMERGENCY, TODAY, THIS WEEK, WHENEVER), trade_required, resident_explanation, safety_hazard, hazard_detail, the two photo-borne facts visible_wiring and water_near_electrical, and the rest — see /docs#core |
agreement |
how many reads gave the same yes/no answer about a fault: n reads ran, k agreed, decided when the agreeing reads are at least half |
engine_version · rubric_version · config_id |
which engine, which rubric, which config produced this; a config_id beginning fallback- means a hardcoded config answered, not a measured candidate |
callback_url |
the /api/outcome path for this diagnosis_id |
share_url |
the card as a page: https://fixragent.com/c/ |
outcome_url |
two taps to tell us what happened: https://fixragent.com/o/ |
warnings |
anything you should not miss, such as a config fallback |
The tool returns the JSON both as structuredContent and as text, and declares an outputSchema a client can validate against.
A worked example (triage_photo)
The server runs with FIXRAGENT_IMAGE_DIR=/Users/me/fixragent-photos. A person says, in Claude Desktop: “Triage the photo toilet-tank.jpg.” The client calls:
{ "name": "triage_photo",
"arguments": { "image_path": "/Users/me/fixragent-photos/toilet-tank.jpg", "mime_type": "image/jpeg" } }
An abridged reply, from a production response captured 14 Sep 2026 (three reads, all three agreed). That reply came through the curl door; the tool returns the same JSON and adds share_url and outcome_url:
{
"diagnosis_id": "ae0deb3c-98c2-42ee-bad7-d4c0e62dd0a7",
"engine_version": "engine-v2-vote-1.0.0",
"rubric_version": "v1.1-2026-09-11",
"config_id": "fast-…",
"agreement": {
"mode": "fast",
"n": 3,
"k": 3,
"decided": true
},
"core": {
"is_building_asset": true,
"asset_type": "Toilet",
"asset_category": "PLUMBING",
"photo_subject": "Interior of an Eljer toilet tank showing fill valve, flapper, and flush handle arm.",
"fault_detected": false,
"fault_summary": "",
"severity": "P4",
"tier": "WHENEVER",
"trade_required": null,
"safety_hazard": false,
"hazard_detail": null,
"resident_explanation": "The toilet tank components appear to be in good working condition with no visible faults."
},
"callback_url": "/api/outcome?diagnosis_id=ae0deb3c-98c2-42ee-bad7-d4c0e62dd0a7",
"warnings": [],
"share_url": "https://fixragent.com/c/ae0deb3c-98c2-42ee-bad7-d4c0e62dd0a7",
"outcome_url": "https://fixragent.com/o/ae0deb3c-98c2-42ee-bad7-d4c0e62dd0a7"
}
The agent can then say what the card would say: WHENEVER — healthy, cosmetic, or not a building asset; no dispatch is owed; all three reads agreed — and hand over share_url.
Limits (triage_photo)
These are the guards of /api/triage (the older tool), in the order they are checked. The three remote tools have the limits described at the top of this README. When one stops you, the tool says which, in a sentence, with the seconds to wait taken from the Retry-After header.
- 20 requests an hour from one address.
- 60 requests a day on the shared demo key.
- A ceiling of twenty dollars a day on what the demo can spend on our side, counted per warm server. Past it the API answers 503 and spends nothing; the budget resets at 00:00 UTC.
- Photos: at least 1 KB and at most 3 MB decoded. The photo travels base64 inside a JSON body and the platform caps the body at 4.5 MB, so 3 MB of photo is the ceiling.
- A fast triage runs three reads; give it up to 60 seconds.
- A deep read is five reads; give it up to 90 seconds.
What triage_photo says when it fails
Each failure has its own sentence.
| what happened | what the tool says |
|---|---|
no key in FIXRAGENT_API_KEY |
“No key is set here, so nothing was sent…” — nothing was sent |
| the key holds a line break, a tab or a non-ASCII character | “The key in FIXRAGENT_API_KEY holds a character a request header cannot carry…” — nothing was sent |
| the API refused the key (401) | “fixragent.com refused the key (401 demo_key_required). Check the value in FIXRAGENT_API_KEY…” and the API’s own sentence |
| too many from this address (429) | “Too many requests from this address (429)… Try again in N seconds” |
| the key’s day is used up (429) | “This demo key has used its 60 requests for today (429). The count resets at 00:00 UTC. Try again in N seconds” |
image_path given but FIXRAGENT_IMAGE_DIR is not set |
“image_path is switched off on this server, so no file was read and nothing was sent…” |
image_path outside the folder, or a symlink leading out of it |
“image_path must name a file inside the folder in FIXRAGENT_IMAGE_DIR…” — nothing was read or sent |
| the bytes are not an image (by their first bytes) | “That is not a photo: its first bytes are not a JPEG, PNG, WebP, GIF or HEIC image. Nothing was sent.” |
image_base64 is not base64 |
“image_base64 is not base64 (only A-Z, a-z, 0-9, +, / and = padding, in groups of four). Nothing was sent.” |
| photo under 1 KB (refused locally) | “That photo is under 1 KB (N bytes)… Nothing was sent.” |
| photo over 3 MB (413, or refused locally before sending) | “That photo is larger than 3 MB…” |
| format that cannot be cleaned of metadata (415) | “That image format is refused (415)… Send a JPEG, PNG or WebP.” |
| the day’s budget is spent (503) | “The demo has used its compute budget for today (503). This is a limit on our side, not a problem with your photo…” |
| the mock refused a body over 1 MB (400) | “b1442b0b-….mock.pstmn.io could not use that request (400 badRequest). The API said: …exceeds the 1mb size limit.” |
| the engine gave no usable answer (502) | “fixragent.com could not produce a triage for this photo (502…). Try once more…” |
How it fits
- It routes: what the photo shows, one of four urgency words, which trade. Gas, water near electrics and exposed wiring go straight to a licensed trade, and the card says so.
- One fixed JSON shape every time, which your developers map to your own work-order fields.
- Use it today with a demo key.
- Agreement counts how often the reads matched each other; the outcome loop at
outcome_urlchecks each triage against what actually happened. - The one-file install above is the install: download
server.js, set the key, register it.
What happens to your photo
Every uploaded photo has its metadata stripped — EXIF, XMP, IPTC and the other carriers that hold GPS — before it is hashed, before it is sent to the engine, and before anything is stored. If the metadata cannot be stripped, the photo is refused (415 or 500) rather than processed. The endpoint keeps the cleaned photo with its Triage Profile (anyone with the profile’s link sees it) and the SHA-256 of the stripped version, and the triage row it writes carries role and variant (source:mcp). The engine provider keeps prompt, response and photo for 55 days. Full text: fixragent.com/docs#photos and fixragent.com/privacy.
Privacy Policy
This server sends the arguments of each call (the photo and the text you give it) to https://fixragent.com/mcp, or to https://fixragent.com/api/triage for triage_photo, and nothing else; it keeps no copy, writes no log, and reads no file at all unless FIXRAGENT_IMAGE_DIR is set, and then only the image file named in image_path inside that folder. What fixragent.com does with what it receives is in the privacy policy at https://fixragent.com/privacy. Contact: [email protected].
Test it
npm install # dev dependency only: the official SDK, used as the reference client
npm test # a local stub replays captured API and MCP replies; no key, no spend, no rows
npm run test:live -- --photo ./photo.jpg # adds a wrong-key 401 on production and one real triage
The harness has five canaries (MCP_TEST_CANARY=401-as-200, drop-core, drift, tool-drift, wrong-id) that make a case go red on purpose, so each check has been seen to fail.
The remote-copy case reads tools/list from https://fixragent.com/mcp (no key, no photo) and fails when any of the three static definitions differs from it. MCP_TEST_REMOTE= points it at another server.
The contract case compares the tool’s core fields with components.schemas.TriageCore in the published spec, https://fixragent.com/openapi.json (one GET, no photo, no key). MCP_TEST_OPENAPI= points it at another copy. If the spec or the remote cannot be read the case FAILS; MCP_TEST_OFFLINE=1 turns that into an explicit SKIP.
MCP_TEST_SERVER= runs the same cases against another copy, such as a copy with one guard removed, to show that guard’s cases go red.
The rest of the surface
- fixragent.com/docs — the reference, field by field
- fixragent.com/openapi.json · openapi.yaml
- Postman collection
- Agents quickstart
- llms.txt
llms-install.md— install steps written for an AI agent (Cline and others)smithery/config-schema.json— the optional key as Smithery’s connection settingCITATION.cff— how to cite this repository ·logo.png— 400×400 logo for directories
MIT licence. Made by AR Logic LLC.
安装
This server does not publish a one-line install command.
Open the repository installation guide配置
{
"mcpServers": {
"fixragent": {
"command": "node",
"args": ["/absolute/path/to/fixragent-mcp/server.js"],
"env": { "FIXRAGENT_API_KEY": "your key" }
}
}
}