{"name":"io.github.Kentucky-ai/opentakeoff","slug":"kentucky-ai-opentakeoff","title":"OpenTakeoff","description":"Construction takeoff for AI agents: load plans, set scale, measure, count, export with provenance.","url":"https://mcp.market/server/kentucky-ai-opentakeoff","rating":null,"grade":"A","score":91,"certified":false,"status":"active","category":"other","tags":[],"presence":{"score":54,"stars":132,"forks":54,"downloads_week":1651,"last_push_at":"2026-09-19T14:57:00.000Z","license":"Apache-2.0"},"uptime":null,"claimed":false,"transport":"npm","callable_via_gateway":false,"default_price_micros":0,"repository":"https://github.com/Kentucky-ai/opentakeoff","website":"https://opentakeoff.kentucky-ai.com","version":"0.9.87","remotes":[],"packages":[{"registryType":"npm","identifier":"opentakeoff-mcp","version":"0.9.87","transport":{"type":"stdio"}}],"tools":[{"name":"annotate","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"apply_rules","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"count_marks","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"create_rfi","description":"","write_action":true,"price_micros":0,"input_schema":null},{"name":"cut_out","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"delete_rfi","description":"Withdraw an RFI. A TOMBSTONE, never a renumber: the record stays with its number reserved, so the register and the marked set keep printing a gap where it was and the next RFI takes the next number \\u2014 an RFI number that went out and then meant something else would be a lie. Every markup linked to it keeps its note and loses the link (the canvas's own delete rule). Withdraw a question you raise","write_action":true,"price_micros":0,"input_schema":null},{"name":"delete_shape","description":"Remove a committed shape by the id returned when it was committed. …","write_action":true,"price_micros":0,"input_schema":null},{"name":"delete_verdict","description":"Lift an agent verdict mark by id (mark_verdict's reply, or list_annotations verdicts[]). Agent marks only: the estimator's APPROVED seal is human ink and is refused \\u2014 the same line edit_shape holds on reviewed shapes. Journaled like every mutation, so undo_last re-seats a lifted mark exactly where it was.","write_action":true,"price_micros":0,"input_schema":null},{"name":"derive_base","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"derive_transitions","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"detect_rooms","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"duplicate_condition","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"edit_annotation","description":"Shorten, replace or clear the text of an existing annotation. Get annotation_id from list_annotations (annotations, not verdicts). Changes only text: position, shape, dimension length, condition links, quantities and review records stay unchanged. Empty text clears the note; a dimension still prints its measured length. Refuses an RFI-linked note: review that question's context in the browser RFI ","write_action":false,"price_micros":0,"input_schema":null},{"name":"edit_condition","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"edit_materials","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"edit_shape","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"export_dxf","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"export_marked_pdf","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"export_report","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"export_takeoff","description":"The full \"opentakeoff.takeoff_canvas.v1\" annotations payload \\u2014 exactly what the app autosaves, importable by it. Returned inline; pass path to also write it to disk as JSON. …","write_action":false,"price_micros":0,"input_schema":null},{"name":"find_schedule","description":"Locate a schedule table in the set (#87): pass a kind (\"room finish\", \"material\"/\"finish\") and get every matching table's sheet, title, headers, TOTAL row count, and REGION \\u2014 sized for a view_sheet look or a read_sheet_text pull of exactly the table. A schedule continued across sheets is ONE match whose \"parts\" list every fragment (base first) with its own viewable region; tables read through","write_action":false,"price_micros":0,"input_schema":null},{"name":"find_text","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"get_sheet_vectors","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"import_takeoff","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"link_annotation","description":"Attach an existing annotation to a condition, or detach it by passing an empty condition \\u2014 the canvas's Attach/Detach control, reachable by an agent. Use it to tie up notes left unattached (list_annotations reports how many), or to move one to the finish it actually concerns. Attaching mints the tag on first use.","write_action":false,"price_micros":0,"input_schema":null},{"name":"list_annotations","description":"Every annotation on the takeoff, with condition_id RESOLVED to its finish tag so you can act on the reply without joining against conditions[]. Filter by sheet, by condition, or both. Coordinates come back in image px (the same frame you passed in), not the normalized form they're stored as. `unattached` counts the notes carrying no condition \\u2014 the candidates for link_annotation. `verdicts` i","write_action":false,"price_micros":0,"input_schema":null},{"name":"list_rfis","description":"Every RFI in the register with its status, sheet, who raised it (actor) and whether an agent-raised one is still pending the estimator's acceptance, its linked markup ids, and the finish tags those markups are attached to \\u2014 the scopes the question touches. withdrawn[] lists the numbers delete_rfi tombstoned, so a gap in the sequence is explained rather than silent. Read this before raising a ","write_action":false,"price_micros":0,"input_schema":null},{"name":"list_shapes","description":"The mid-session shape inventory (#149): every committed shape's id, sheet, condition tag, role, quantities, room label, vertex count, and review state in one compact read \\u2014 the ids edit_shape and delete_shape assume you have, without pulling the whole export_takeoff payload to find one shape. Filter by sheet, by condition, or both; filters narrow, an empty list is a result, not an error.","write_action":false,"price_micros":0,"input_schema":null},{"name":"load_plan","description":"Open a plan PDF from disk. Default: replace the whole session (previous documents, scales, conditions, and shapes are cleared). merge: true ADDS the document to the working set instead (#152) \\u2014 a bid set is plans + schedule + addenda, not one PDF \\u2014 keeping every scale, condition, and shape; sheet keys carry file names so documents never collide, the sheet graph spans the whole set (resol","write_action":false,"price_micros":0,"input_schema":null},{"name":"mark_verdict","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"measure_line","description":"Measure an open polyline (min 2 points, image px): length_lf at the sheet's scale. Requires the scale to be set. Pass condition to commit it as a linear shape (base, transitions, feature strips). A curved run (base along a radius wall, a curved feature strip) takes arc_through: one point on the bow, marked. …","write_action":false,"price_micros":0,"input_schema":null},{"name":"measure_polygon","description":"Measure a closed polygon you supply (min 3 vertices, image px): area_sf and perimeter_lf at the sheet's scale. Requires the scale to be set. Pass condition to commit it; role \"deduct\" subtracts. A room ring belongs on the innermost wall-face strokes from get_sheet_vectors, crossing each door opening on the wall centerline and wrapping columns and stubs; never on a hatch edge, casework or a door le","write_action":false,"price_micros":0,"input_schema":null},{"name":"measure_surface","description":"Surface Area \\u2014 wall SF (#146): trace an OPEN run along the wall in plan view (min 2 points, image px) and the quantity is traced LF \\xD7 height. This is how wall tile, wainscot, and wall systems are taken off \\u2014 the quantity family …measure_polygon cannot produce. Height lives on the CONDITION (the canvas's H knob): pass height_ft to set it on this call (journals as its own undo step, lik","write_action":false,"price_micros":0,"input_schema":null},{"name":"one_click","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"open_tool_stage","description":"Enable a stage of this server's tools. Tool exposure is staged to match the takeoff workflow: \"setup\" (orient: load, scale, read the set) is always enabled; \"measure\" (commit shapes: …), \"revise\" (edit, annotate, verdict-mark, undo), and \"handoff\" (summaries, exports, the marked set) start closed and open here on demand. Opening a stage is idempotent and never closes another \\u2014 the surface onl","write_action":false,"price_micros":0,"input_schema":null},{"name":"place_count","description":"Count markers \\u2014 EA (#146): one point, one each. Thresholds, stair nosings, floor boxes, entrance mats \\u2014 the scale-free quantity family. Commits one count shape per point (computed {count: 1}, exactly the canvas's Count tool), NO scale required, and the whole call is ONE undo step…. takeoff_summary reports them as ea; the marked set draws each marker. …","write_action":false,"price_micros":0,"input_schema":null},{"name":"propose_condition_edit","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"propose_takeoff","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"read_sheet_text","description":"The sheet's text with positions \\u2014 items [{str, x, y}] in image px plus the joined text. Optionally restrict to a region {x0, y0, x1, y1}. Use it to read title blocks, room labels, finish schedules, and scale notes. …","write_action":false,"price_micros":0,"input_schema":null},{"name":"resolve_rfi","description":"Answer an OPEN RFI: the answer lands as its response, status becomes answered (the register's own state for \"response in\"), and the response date stamps exactly as the panel's would, plus an ISO timestamp of the resolve. Only an open RFI resolves \\u2014 an answered, closed, or void one is refused rather than re-answered or quietly revived (undo_last reverses your own resolve if the answer was wron","write_action":false,"price_micros":0,"input_schema":null},{"name":"resolve_tag","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"revise_proposal","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"scope_duplicates","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"scope_merge","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"set_scale","description":"Set a sheet's scale \\u2014 exactly ONE of: label (a standard scale, e.g. '1/4\" = 1'-0\"'), upp (real feet per image px), calibrate (two points along a known dimension plus its real feet), or use_detected (adopt the drawn scale note read off the sheet). The detected scale is never applied automatically \\u2014 setting it is always this explicit call. Changing an existing scale recomputes measurements","write_action":false,"price_micros":0,"input_schema":null},{"name":"sheet_context","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"sheet_graph","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"sheet_info","description":"Sheet detail: dims (px and pt), vector segment count, whether the sheet has vector linework (…), scale status, the detected scale suggestion, and this sheet's committed shape count. …","write_action":false,"price_micros":0,"input_schema":null},{"name":"split_condition","description":"Cut a twin loose from its family: every following material row freezes at its current values and edits to the original stop reaching it. It keeps its finish tag and still groups with its siblings \\u2014 only the inheritance ends. Use when two variants have diverged far enough that following one another is wrong. A condition that already owns its materials returns split:false rather than erroring. ","write_action":false,"price_micros":0,"input_schema":null},{"name":"sweep_schedule_row","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"symbol_sweep","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"takeoff_summary","description":"Per-condition totals (floor/wall/border SF, LF, EA, SY, with and without waste) plus grand totals \\u2014 the Report's numbers, computed by the same rules. Numbers only: the deliverable that SHOWS the work on the drawings is export_marked_pdf. …","write_action":false,"price_micros":0,"input_schema":null},{"name":"undo_last","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"view_sheet","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"withdraw_condition_edit","description":"Drop a pending condition-edit proposal (#365) without touching the condition. undo_last re-seats it.","write_action":true,"price_micros":0,"input_schema":null},{"name":"withdraw_proposal","description":"Take a proposal back (#365): every still-pending shape in the batch is removed in ONE journal step, the record stays marked withdrawn (its label is history the estimator may still read), and new commits stop attaching to it. Shapes the estimator already accepted are ink and stay \\u2014 the reply counts them. This is the honest exit for \"that batch was wrong\" \\u2014 one call instead of N delete_sha","write_action":false,"price_micros":0,"input_schema":null}],"scan":{"score":91,"grade":"A","scanned_at":"2026-09-19T20:06:14.021Z","report":{"scannerVersion":"0.1.5","scannedAt":"2026-09-19T20:06:14.001Z","components":{"code":{"score":25,"max":25,"notes":["3 source files scanned"]},"reliability":{"score":-1,"max":20,"notes":["no gateway calls yet and no remote to probe"]},"poisoning":{"score":-1,"max":15,"notes":["tools not inspected (local package is not executed); not counted"]},"auth":{"score":12,"max":15,"notes":["local package, no credentials required"]},"maintenance":{"score":15,"max":15,"notes":["last push 0 days ago"]},"identity":{"score":7,"max":10,"notes":["registry namespace matches repository owner"]}},"findings":[],"inputs":{"packages":[{"registryType":"npm","identifier":"opentakeoff-mcp","version":"0.9.87","found":true,"license":"Apache-2.0","hasInstallScripts":false,"dependencyCount":9,"publishedAt":"2026-09-13T02:14:30.375Z","repositoryUrl":"git+https://github.com/Kentucky-ai/opentakeoff.git","weeklyDownloads":1651}],"repo":{"found":true,"owner":"Kentucky-ai","repo":"opentakeoff","archived":false,"pushedAt":"2026-09-19T14:57:00Z","stars":132,"forks":54,"openIssues":6,"ownerType":"Organization","ownerAvatarUrl":"https://avatars.githubusercontent.com/u/292791417?v=4","ownerCreatedAt":"2026-06-11T13:02:31Z","license":"Apache-2.0"},"icon":{"url":"https://opentakeoff.kentucky-ai.com/favicon.svg","source":"registry"},"presence":{"stars":132,"forks":54,"downloadsWeek":1651,"license":"Apache-2.0","lastPushAt":"2026-09-19T14:57:00.000Z","score":54}}}},"grade_history":[],"reviews":[]}