Tools & prompts reference
conmcp exposes 8 tools and 1 prompt over MCP. All page numbers are 1-based — estimators think in sheet and page numbers, not indices. The server's suggested flow (as given to the client LLM):
get_plan_overview → search_plan_pages → extract_quantities → render_page (to visually read drawings) → extract_tables (schedules) → get_takeoff_instructions → build a takeoff → generate_takeoff_report
Every pdf_path (and any output path) must fall inside the CONMCP_ALLOWED_DIRS
sandbox, or the tool returns a sandbox error. Example responses below come from the
sample plan and are abridged.
get_plan_overview
Get plan set metadata and a per-page sheet index (sheet number, title, type like site_plan/details/schedule, and discipline). Call this first to orient yourself in a plan set before reading individual sheets.
| Parameter | Type | Required | Description |
|---|---|---|---|
pdf_path |
str |
yes | Path to the plan set PDF |
Returns — file metadata plus the detected sheet index:
{
"version": "0.1.0",
"file": "/Users/you/plans/sample_plan.pdf",
"file_name": "sample_plan.pdf",
"size_bytes": 24310,
"page_count": 4,
"title": "OAKRIDGE COMMERCE PARK — PHASE 2",
"author": "conmcp sample generator",
"producer": "PyMuPDF",
"disciplines": ["civil", "general"],
"sheets": [
{"page": 1, "sheet_number": "G-001", "title": "SHEET INDEX", "sheet_type": "cover", "discipline": "general"},
{"page": 2, "sheet_number": "C-101", "title": "SITE PLAN", "sheet_type": "site_plan", "discipline": "civil"},
{"page": 3, "sheet_number": "C-501", "title": "FENCE DETAILS", "sheet_type": "details", "discipline": "civil"},
{"page": 4, "sheet_number": "C-601", "title": "GATE SCHEDULE", "sheet_type": "schedule", "discipline": "civil"}
]
}
Sheet types come from title-block heuristics (cover, site_plan, grading,
utilities, details, schedule, notes, elevations, …); pages that can't be
classified fall back to unclassified rather than guessing. Disciplines follow the US
National CAD Standard prefixes (C = civil, A = architectural, L = landscape, E =
electrical, …).
search_plan_pages
Find which pages mention the given keywords, ranked by relevance, with a text snippet per hit. Use this to locate scope-relevant sheets in large plan sets instead of reading every page.
| Parameter | Type | Required | Description |
|---|---|---|---|
pdf_path |
str |
yes | Path to the plan set PDF |
query |
str |
yes | Keywords, e.g. 'chain link fence gate' |
max_results |
int (1–50) |
no (default 10) |
Max hits returned |
Returns:
{
"query": "chain link gate",
"hits": [
{
"page": 2,
"score": 6.0,
"snippet": "450 LF - 6' HIGH CHAIN LINK FENCE W/ 3-STRAND BARBED WIRE …",
"matched_terms": ["chain", "link", "gate"]
},
{
"page": 4,
"score": 3.0,
"snippet": "GATE SCHEDULE … G-3 CANTILEVER SLIDE 30'-0\" …",
"matched_terms": ["gate", "chain", "link"]
}
]
}
get_page_text
Get the extractable text of one page (notes, callouts, title block). Drawings carry most information graphically — pair this with render_page rather than relying on text alone.
| Parameter | Type | Required | Description |
|---|---|---|---|
pdf_path |
str |
yes | Path to the plan set PDF |
page |
int (≥1) |
yes | Page number (1-based) |
Returns — text is capped at 20,000 characters, with a flag if it was cut:
{
"page": 4,
"text": "GATE SCHEDULE\nGATE ID TYPE WIDTH HEIGHT HARDWARE\nG-1 DOUBLE SWING 24'-0\" …",
"truncated": false
}
render_page
Render a plan sheet (or a cropped region) as a PNG image so you can visually read the drawing: follow fence lines, count gate symbols, read the legend and graphic scale. Use clip + higher dpi to zoom into details.
| Parameter | Type | Required | Description |
|---|---|---|---|
pdf_path |
str |
yes | Path to the plan set PDF |
page |
int (≥1) |
yes | Page number (1-based) |
dpi |
int (36–300) |
no (default 150) |
Render resolution |
clip |
list[float] or null |
no | Optional crop [x0, y0, x1, y1] in PDF points (72/inch) |
Returns — a PNG image (an MCP image content block, not JSON). This is the core of the vision-first design: the client's vision model reads the returned image directly.
The effective DPI is capped by CONMCP_MAX_RENDER_DPI (default 300) so renders stay
inside model image limits. A clip that doesn't intersect the page is an error.
extract_tables
Extract ruled tables (gate schedules, hardware schedules, plant schedules) from a page as row grids. Cross-check schedule rows against plan-view callouts — mismatches are RFI candidates.
| Parameter | Type | Required | Description |
|---|---|---|---|
pdf_path |
str |
yes | Path to the plan set PDF |
page |
int (≥1) |
yes | Page number (1-based) |
Returns:
{
"page": 4,
"table_count": 1,
"tables": [
{
"row_count": 4,
"rows": [
["GATE ID", "TYPE", "WIDTH", "HEIGHT", "HARDWARE"],
["G-1", "DOUBLE SWING", "24'-0\"", "6'-0\"", "PANIC BAR, DROP ROD"],
["G-2", "SINGLE SWING", "6'-0\"", "6'-0\"", "SELF-CLOSING HINGES, LATCH"],
["G-3", "CANTILEVER SLIDE", "30'-0\"", "6'-0\"", "ELECTRIC OPERATOR, LOOPS"]
]
}
]
}
Detection is line-based (pdfplumber), so it finds ruled schedule grids; whitespace-only "tables" in notes won't be picked up.
extract_quantities
Scan page text for quantity callouts using construction-domain patterns: quantities with units (LF/SF/SY/CY/EA/TON), fence callouts (height, material), gate callouts (width, type), and post spacing (O.C.). These are deterministic leads to verify against the drawing — not a finished takeoff.
| Parameter | Type | Required | Description |
|---|---|---|---|
pdf_path |
str |
yes | Path to the plan set PDF |
pages |
list[int] or null |
no | Pages to scan (1-based); omit for the whole document |
Returns — one call scans at most CONMCP_MAX_PAGES_PER_CALL pages (default 20);
pages_capped is true if the request was trimmed:
{
"pages_scanned": [1, 2, 3, 4],
"pages_capped": false,
"summary": {
"match_counts": {"quantity": 3, "fence_callout": 4, "gate_callout": 5, "post_spacing": 1},
"unit_totals": {"LF": 578.0, "SF": 215.0}
},
"matches": [
{
"kind": "quantity",
"page": 2,
"raw_text": "450 LF - 6' HIGH CHAIN LINK FENCE W/ 3-STRAND BARBED WIRE",
"value": 450.0,
"unit": "LF",
"attributes": {}
},
{
"kind": "fence_callout",
"page": 2,
"raw_text": "450 LF - 6' HIGH CHAIN LINK FENCE W/ 3-STRAND BARBED WIRE",
"value": null,
"unit": null,
"attributes": {"material": "chain link", "height_ft": 6.0}
},
{
"kind": "gate_callout",
"page": 2,
"raw_text": "24' DOUBLE SWING GATE (G-1)",
"value": null,
"unit": null,
"attributes": {"gate_type": "double", "width_ft": 24.0}
},
{
"kind": "post_spacing",
"page": 3,
"raw_text": "LINE POSTS @ 8'-0\" O.C. MAX",
"value": 8.0,
"unit": "FT_OC",
"attributes": {"spacing_ft": 8.0}
}
]
}
Match kinds: quantity, fence_callout, gate_callout, post_spacing. These are
leads — the playbook tells the model to verify every one against the rendered drawing.
get_takeoff_instructions
Get the construction takeoff playbook for a trade: workflow steps, field-tested gotchas (scope traps, duplicate-sheet pitfalls, spec locations), UOM conventions, and the JSON schema your final TakeoffReport must match. Call this before building a takeoff.
| Parameter | Type | Required | Description |
|---|---|---|---|
focus |
str |
no (default 'fence') |
Trade focus: 'fence', 'sitework', or 'general' |
Returns (abridged — the real payload includes the full gotcha list and the complete
JSON Schema for TakeoffReport):
{
"focus": "fence",
"role": "You are performing a construction takeoff: extracting quantities, scope, and risks from plan documents accurately enough to bid real money on. Be conservative — a wrong quantity is worse than a flagged one. …",
"workflow": ["Call get_plan_overview to see the sheet index …", "…"],
"scope_profile": "fences-gates",
"search_terms": ["fence", "gate", "chain link", "ornamental", "post", "fabric", "barbed", "operator", "bollard"],
"gotchas": [
"Verify the drawing scale before trusting any measured length …",
"Silt fence and tree-protection fence are erosion control (Div 31), NOT permanent fencing (Div 32). Exclude them explicitly.",
"…"
],
"uom_conventions": {
"fence runs": "LF",
"gates": "EA",
"gate operators": "EA",
"hinges/hardware sets": "PAIR or EA",
"fabric area (when priced by area)": "SF",
"concrete footings (when broken out)": "CY"
},
"report_schema": { "…": "TakeoffReport JSON Schema" }
}
An unknown focus falls back to 'general'.
generate_takeoff_report
Validate a completed takeoff against the schema and render it as Markdown (review/Obsidian), CSV (spreadsheets), or JSON (contractor-bid compatible line_items). Validation errors come back as readable messages — fix and retry.
| Parameter | Type | Required | Description |
|---|---|---|---|
report |
dict |
yes | TakeoffReport JSON (see get_takeoff_instructions) |
fmt |
str |
no (default 'markdown') |
Output format: 'markdown', 'csv', or 'json' |
output_path |
str or null |
no | Optional file path to save the rendered report |
Example call:
{
"report": {
"project": "Oakridge Commerce Park — Phase 2",
"source_pdf": "/Users/you/plans/sample_plan.pdf",
"scope_profile": "fences-gates",
"items": [
{
"id": "fence-cl-6ft",
"section": "fences-gates",
"item": "CHAIN-LINK-6FT-BW",
"description": "6' high galvanized chain link fence w/ 3-strand barbed wire",
"qty": 450,
"uom": "LF",
"status": "Included",
"source": "C-101 p.2",
"location": "north property line",
"assumptions": ["Posts 8'-0\" O.C. per detail 1/C-501"],
"confidence": 0.9,
"needs_review": false,
"custom_fields": {"post_spacing": 8, "fabric_gauge": "9 GA"}
}
],
"scope_notes": ["Gate operator power and loops by electrical contractor (BY OTHERS)"],
"risks": ["Verify field dimensions prior to fabrication per general note 5"]
},
"fmt": "markdown",
"output_path": "/Users/you/plans/oakridge-takeoff.md"
}
Returns:
{
"format": "markdown",
"item_count": 1,
"needs_review_count": 0,
"flagged_uoms": [],
"saved_to": "/Users/you/plans/oakridge-takeoff.md",
"content": "# Takeoff — Oakridge Commerce Park — Phase 2\n…"
}
output_path goes through the same sandbox as inputs. flagged_uoms lists any units
outside the shared vocabulary — see the takeoff schema for the full
report contract and export shapes.
Prompt: fence_takeoff
Guided fence & gate takeoff for a plan set.
| Argument | Type | Required | Description |
|---|---|---|---|
pdf_path |
str |
yes | Path to the plan set PDF |
An MCP prompt (not a tool): invoking it inserts a ready-made instruction into the
conversation that tells the model to perform a fence and gate takeoff on pdf_path,
including the estimator role, the 7-step workflow, and the full fence gotcha list — then
finish by calling generate_takeoff_report. It's the same content as
get_takeoff_instructions(focus="fence"), packaged as a one-click starting point in
clients with a prompt picker (Claude Desktop) or slash commands (Claude Code).