The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the QCDatabase.AI listing page.
WARNING! Our apologies, but some of the modifications we made to allow remote hosting of this MCP server have broken the stdio tools. We are working on a fix ASAP. Please check back here later.
A small program that lets an AI assistant (like Claude) do your everyday QC Database work for you — set the project you're working on, upload records, read what the AI pulled off a document, and find and close out the items still needed for a complete, traceable turnover package.
It runs on your own computer. Your AI assistant talks to it; it talks to QCDatabase.AI on your behalf, using your own login.
Built on the official Model Context Protocol
Python library and the QC Database MCP specification — published at
qcdatabase.ai/mcp_server_spec.md and
kept in this repo as mcp_server_spec.md.
Once it's connected, you can talk to it in plain language. For example:
You need Python 3.10 or newer on your computer.
brew install python or get it from
python.org/downloads.To check it's installed, open a terminal (PowerShell on Windows, Terminal on Mac) and run:
You should see Python 3.10 or higher. (On some Macs the command is python3.)
From a terminal, in this folder:
That's it. This installs the server and a command called qcdatabase-mcp.
If
pipisn't found, trypython -m pip install .(orpython3 -m pip install .).
Claude Desktop launches this server for you and shows its tools in your chats. Set it up once:
If you don't have it, download it from claude.ai/download and sign in.
In Claude Desktop:
This opens (and, if needed, creates) a file named claude_desktop_config.json.
You can also open it directly:
%APPDATA%\Claude\claude_desktop_config.json~/Library/Application Support/Claude/claude_desktop_config.jsonPut this in the file. If the file already has other servers, just add the
"qcdatabase" block inside the existing "mcpServers" section.
Windows:
Mac (Python is usually python3 there):
Save the file.
If Claude can't start it (you see "python not found" or the server fails), use the full path to Python instead of just
"python". Find it by runningwhere python(Windows) orwhich python3(Mac) in a terminal, then paste that path as the"command". For example on Windows:(Use double backslashes
\\in Windows paths inside JSON.)
Fully quit and reopen the app (on Windows, also close it from the system tray; on Mac, Quit with Cmd-Q — closing the window isn't enough). The new server only loads on a fresh start.
Start a new chat and look for the tools/connector indicator (a slider or tool icon near the message box). Click it and you should see qcdatabase with its tools listed. If you don't, see Troubleshooting.
Then ask Claude to "connect to QC Database" to sign in — see below.
Sign in. Ask the assistant to connect to QC Database (the login
tool). Your web browser opens; log in and pick which company workspace
you want to connect. That choice locks the connection to that one
organization. You only do this once — it's remembered.
Pick your project. Ask it to work on a project by name, or list your projects first. Almost every action happens "inside" a project, so the server asks you to choose one before it will do project work. You can switch projects any time.
Get to work. Upload documents and drawings, check what's missing for turnover, create and close reference requests, add notes and photos, and so on.
login, logout, auth_status, whoamilist_tenants, list_projects, set_project,
get_active_project, list_project_members, list_lists, list_list_items,
list_map_item_schemas, get_map_item_schema, list_document_folders,
list_form_schemascreate_list_item, update_list_item,
delete_list_item (a soft delete — the entry stops appearing in list reads but
stays on record)list_jobs, create_job, list_packages,
create_package, list_line_specs, create_line_specupload_document, upload_document_version,
download_document, upload_drawing, upload_large_format_drawing,
upload_drawing_to_package, upload_drawing_version,
upload_large_format_drawing_version, attach_photolist_drawings, get_drawing (incl. pixel width/height — the
HTML5-canvas coordinate space for map items), export_drawing,
export_large_format_drawinglist_fillable_templates, get_fillable_template,
download_fillable_template, submit_fillable_templateget_document, set_document_extracted_data,
list_documents, list_map_items, get_map_item, create_map_item
(place welds/flanges by pixel coordinates), bulk_create_map_items,
bulk_update_map_items (batch up to 500 items on one drawing/schema). Always
call list_map_item_schemas first — see the CAD/PCF/.weldb note belowinspect_zipmap,
upload_zipmap — see Zipmapslist_repair_codes, add_map_item_repairlist_form_submissions, create_form_submission,
get_form_submission, update_form_submission, complete_form_submission,
create_note, list_notes, resolve_notelist_photos, attach_photolist_itp_line_items, get_itp_line_item,
create_itp_line_item, update_itp_line_item, mark_map_item_complete,
mark_map_item_accepted, mark_itp_complete, mark_itp_acceptedlist_lock_types
(read only), list_locks, get_lock, add_lock (place a hold point — fit-up,
tack-up, weld-cleanliness, FME, final-closure, boiler-tube FME sponge-in/sponge-out,
etc. — on a map item or ITP line item so it can't be turned in until inspected),
unlock_lock, assign_lock, delete_lock (withdraws a hold placed in error — a
soft delete: kept in the audit trail, no longer holding the item; use unlock_lock
for a hold that was actually satisfied). A lock is a construction quality gate,
not a security control; place or clear one only at the explicit request of its
owner/author or an authorized inspector. Creating or editing lock types (the
named hold definitions, and who may place/clear each) is intentionally not
exposed here — that permission-sensitive setup is done in the web app's Project
Admin, and the assistant will point you therelist_reference_requests,
create_reference_request, list_references, create_reference,
turnover_reportlist_shippers, list_shipper_line_itemssemantic_search — natural-language
search across the project's documents, drawings, large-format drawings, jobs,
packages, list items, map items, form submissions, notes, and shipperssearch_user_manual (ask "how do I…?" questions and
read the product help articles), list_user_manual (browse the help contents)generate_qr_codeThis server is a natural partner to the systems that already describe your geometry — CAD/CAE exports, PCF piping files, and .weldb boilermaker replacement-panel files. Those sources carry most of what a good map item needs, so the assistant can turn them into accurate weld maps — if it follows two habits:
list_map_item_schemas before
create_map_item / bulk_create_map_items. That picks the right schema and
reveals its exact fields, so source values land where they belong instead of
being guessed or free-texted:
x_position_2/y_position_2) as the opposite corner so the extent is
preserved. Collapsing a rectangular weld to a bare point (or spreading a point
weld into a box) silently corrupts the map. Fetching the schema up front is
what makes this distinction obvious before any items are placed.A zipmap is a plain zip that packages one drawing (a PNG, optionally the source single-page PDF) together with every map item already placed on it and the JSON Schemas that describe those item types. It is the "bring your own AI" hand-off: your model, CAD export, or takeoff tool produces the map; QC Database ingests it.
upload_zipmap replaces the old three-step dance (upload the drawing → wait →
bulk-create items against it) with one transactional request. The drawing, all
of its map items across every schema, and its extracted data are created together
— or nothing is. No server-side AI runs on it; the map is yours.
Ask for it in plain language: "upload this zipmap into the Unit 2 hydro package." Behind that, the assistant does three things first:
inspect_zipmap — looks inside without uploading: the drawing size, how
many items of each type, the data fields those items use, and which types are
not yet bound to a QC Database map item schema.list_map_item_schemas / get_map_item_schema — a zipmap names its types
locally (weld, heat); QC Database identifies schemas by id. Any unbound type
must be paired with the schema it belongs on (schema_ids={"weld": "<id>"}),
and comparing the type's fields against the schema's fields is how that match is
confirmed. Producers can skip this by writing the id into the archive's
schemata/<type>.schema.json up front.list_packages — the new drawing has to be filed somewhere, and the API
requires a package id. Pick (or create_package) the scope package first.Coordinates travel as pixels of the zipmap's PNG (top-left origin, y down) — exactly the space QC Database maps in — so nothing is rescaled or flipped. If the project uses per-schema auto-numbering, its labels win over the zipmap's item ids and the result reports each new item against the id it came from.
Re-sending a corrected map? mode="replace" first soft-deletes any live
drawing in that package with the same drawing number (retained in the audit trail,
gone from drawing lists, and its map items go with it) instead of leaving a
duplicate. The default, mode="append", always creates a new drawing.
Everything that can be checked locally is checked before the upload — a missing PNG, a type with no schema id, a coordinate outside the drawing, PDF-space coordinates that would misplace every item — so a bad map fails in a second with a list of what to fix, not after a multi-megabyte round trip.
Zipmaps are read from your own computer, so this works with the local (stdio) server. A hosted server never touches your disk (see Filesystem safety).
Marking work complete or accepted is a buy-off — the system records it under your name and the current time. The assistant can do these for you, but every one of those tools tells you plainly that you are responsible for the action. The server never signs anything off silently. Treat those as your decisions, made with your go-ahead.
https://qcdatabase.ai.Your login is stored on your own computer in a per-user folder:
%LOCALAPPDATA%\qcdatabase-mcp\store.json~/Library/Application Support/qcdatabase-mcp/store.json~/.config/qcdatabase-mcp/store.jsonRun the logout tool (or delete that file) to forget the login.
This is a hard rule the server must always uphold — for its own safety and for anyone contributing to this open-source repo:
site-packages), the virtualenv, or (from a checkout) the repo root — whatever
path it is given, so the assistant can never edit the server's or a
dependency's code (no self-modification). Downloads also refuse to overwrite
an existing file; choose a new path.store.json is also .gitignored).These rules are enforced in code by _guard_local_path in server.py.
"command" to the full path to Python (see
the install note above).%APPDATA%\Claude\logs\, Mac ~/Library/Logs/Claude/.store.json
(see Privacy & safety for its location) and sign in again — that
re-registers the app with the current permissions.8765. If another program is using it, set a
different free port before launching the app by adding an environment variable
QCDB_CALLBACK_PORT (for example 8766), then sign in again.python. Reinstall Python with "Add to PATH" ticked
(Windows), or use python3 in the config.Layout:
Environment variables (stdio mode):
QCDB_CALLBACK_PORT — local OAuth callback port (default 8765).QCDB_CONFIG_DIR — override where tokens/settings are stored.mcp.qcdatabase.ai)By default the server speaks MCP over stdio — one local user, sign-in via the
login tool. Pass --http to run the multi-user hosted server instead, which
serves the Streamable HTTP transport at /mcp, plus a public home page
at / (connection instructions for humans) and a health check at /health
(for load balancers and uptime monitors).
Try it on your own machine (creates a .venv, installs, runs on
http://127.0.0.1:8000):
Then open http://127.0.0.1:8000/ in a browser.
Deploy it on a server so that git pull + restart is a complete deploy —
the full walkthrough (systemd unit, nginx/Caddy configs, verification
checklist, troubleshooting) is in deploy/README.md:
Behind nginx you MUST forward the original Host header (
proxy_set_header Host $host;) — the provideddeploy/nginx.confdoes. Without it, the DNS-rebinding protection rejects every authenticated request with421, which looks like "OAuth works but no tool call ever does". Caddy (deploy/Caddyfile) does the right thing by default.
In hosted mode the server is an OAuth 2.0 resource server (per the MCP authorization spec) — there are no shared secrets and nothing is stored on disk:
/.well-known/oauth-protected-resource document this server publishes) and
sends that user's access token as Authorization: Bearer … on every request./api/whoami/, identifies the user,
and acts as them. Unauthenticated requests get 401 with a WWW-Authenticate
header pointing clients at the authorization server, so sign-in is automatic.set_project.)Host/Origin of every request
(forged Host → 421, forged Origin → 403).The login / logout tools become no-ops in hosted mode (sign-in is the client's
job); everything else works identically to stdio.
Hosted-mode configuration (flags override env):
QCDB_MCP_HTTP=1 — same as --http.--host / QCDB_MCP_HOST, --port / QCDB_MCP_PORT — bind (default
127.0.0.1:8000).--resource-url / QCDB_MCP_RESOURCE_URL — this server's public URL and OAuth
resource id; required when binding a non-loopback host.--issuer-url / QCDB_MCP_ISSUER_URL — the OAuth authorization server (default
https://qcdatabase.ai).QCDB_MCP_ALLOWED_HOSTS / QCDB_MCP_ALLOWED_ORIGINS — extra allow-list entries
(comma-separated), e.g. when fronted by additional hostnames.QCDB_MCP_TOKEN_CACHE_TTL — seconds a verified token is trusted before
re-checking /api/whoami/ (default 60). This is also the window in which a
token revoked upstream keeps working here; lower it to shrink that window.QCDB_MCP_TOKEN_NEG_CACHE_TTL — seconds a failed verification is remembered
(default 5), which blunts garbage-token amplification against the API.QCDB_MCP_STATELESS — serve each request without a server-held MCP session
(default 1). Leave on: stateful sessions break on restarts, redeploys, and
multi-replica setups ("session not found"). Set 0 only if you need
server-initiated messages within a session.QCDB_MCP_JSON_RESPONSE — answer POSTs with plain JSON instead of an SSE
stream (default 1). Leave on: buffering proxies (nginx's default) stall SSE
and the client times out; none of this server's tools stream partial results.TLS. Terminate TLS at a reverse proxy in front of the server (never expose
plain HTTP publicly) and forward the real Host header.
Resource indicator (kept open). This server accepts any valid QCDatabase-
issued token and takes its resource id from QCDB_MCP_RESOURCE_URL, so anyone can
fork it and host on their own domain without special AS configuration. For strict
RFC 8707 audience binding, an operator may configure their authorization server
to issue tokens bound to their MCP URL — but it is not required.
Scaling across replicas. The only shared state is each user's pinned project —
small, non-secret, and disposable (lost pins just mean re-running set_project).
Pick whichever fits your deployment:
QCDB_MCP_REDIS_URL and install the extra
(pip install 'qcdatabase-mcp[redis]'). All replicas share state and it
survives restarts. Pins expire after QCDB_MCP_SESSION_TTL seconds idle
(default 8h). To plug in a different backend, implement the two-method store in
hosted.py (get_active_project / set_active_project) and return it from
build_session_store.(The token-verification cache is intentionally per-replica — each rebuilds it from
/api/whoami/, so it needs no sharing.)
Licensed under the MIT License (see LICENSE).