Skip to content

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).