opentakeoff-mcp 0.9.38 → 0.9.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -1
- package/dist/server-core.js +24 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -107,7 +107,7 @@ includes document text, shape vertices, or result payload content.
|
|
|
107
107
|
|---|---|
|
|
108
108
|
| `load_plan` | Open a plan PDF from disk. Default replaces the whole session; **`merge: true` ADDS the document to the working set** (#152) — plans + schedule + addenda as one takeoff, sheet graph spanning the whole set, marked set covering every worked sheet. Returns per-sheet dims, title-block `sheet_number`, and the detected drawn scale where present. |
|
|
109
109
|
| `sheet_info` | One sheet's dims, vector segment count, scale status, detected suggestion, committed shape count. |
|
|
110
|
-
| `set_scale` | Set a sheet's scale — exactly one of `label`, `upp`, `calibrate {p1, p2, feet}`, `use_detected`. |
|
|
110
|
+
| `set_scale` | Set a sheet's scale — exactly one of `label`, `upp`, `calibrate {p1, p2, feet}`, `use_detected`. **Lands unconfirmed** (`confirmed: false`) until a human confirms in the canvas — see Scale rules. |
|
|
111
111
|
| `one_click` | One-Click Area at (x, y): the sealed flood engine bounded by the plan linework, traced, vertices snapped — the SAME feet-true arguments the canvas passes at a click (gap sealing up to a door width, door-swing wedge inclusion, the half-foot minimum-passage rule), so an MCP trace and a canvas click at one seed measure the same square footage (pinned against the bench corpus goldens in `test/parity.test.ts`). Every trace carries the engine's account of itself: `confidence` 0–1 with `confidence_factors` naming each deduction (`gap_sealed_px`, `door_wedges`, `min_pass_delta`, …) — a review prioritizer, never a verification; a low score is a `view_sheet {overlay: true}` audit prompt, not a fact. On a SCANNED sheet (no usable linework) the flood falls back automatically to the rendered pixels — same engine as the canvas — with `raster_traced` disclosed on the reply and on the shape's origin (#154). Pass `condition` to commit (the full account stamps `origin` centrally at the commit); `role: "deduct"` subtracts. |
|
|
112
112
|
| `detect_rooms` | Batch One-Click: reads every room-number label off the sheet's text layer and floods each — one call instead of `read_sheet_text` + reasoning + N `one_click` calls, through the SAME sealed engine per room (confidence + the engine account ride each room and its committed origin). Only cleanly-traced rooms come back; everything skipped is counted and reasoned in `withheld` (degenerate / duplicate / implausible / unresolved), never dropped silently. To commit: `assign_from_schedule: true` routes each room through its OWN room-finish schedule row and commits under the FLOOR finish that row states (rooms the schedule can't answer for return in `unresolved[]` with reasons and re-seedable coordinates); or pass `condition` to commit every room under one stated tag. |
|
|
113
113
|
| `measure_polygon` | Area + perimeter of a polygon you supply (min 3 verts). Requires scale. |
|
|
@@ -216,6 +216,14 @@ space, which makes them usable directly as click targets.
|
|
|
216
216
|
|
|
217
217
|
- A detected scale is a **suggestion** — it is never applied automatically.
|
|
218
218
|
Adopting it is always an explicit `set_scale { use_detected: true }`.
|
|
219
|
+
- **Agent proposes, human confirms.** `set_scale` is the agent surface, so a
|
|
220
|
+
scale set here lands **unconfirmed** (`confirmed: false` in the reply).
|
|
221
|
+
Quantities still flow — the gate is a flag, never a refusal — but
|
|
222
|
+
`takeoff_summary` names the affected sheets in
|
|
223
|
+
`scale_unconfirmed`, and the export/report carry `scale_confirmed` so the
|
|
224
|
+
canvas can ask the estimator to confirm (its scale menu grows a
|
|
225
|
+
**Confirm agent-set scale** row on import). Only a human act in the canvas
|
|
226
|
+
clears the flag.
|
|
219
227
|
- `measure_polygon` and `measure_line` refuse without a scale:
|
|
220
228
|
`Set the scale for <sheet> first — use set_scale (detected: <label>).`
|
|
221
229
|
- `one_click` without a scale returns a **px-only preview**
|
package/dist/server-core.js
CHANGED
|
@@ -4199,7 +4199,11 @@ function reportJson({ projectName = "", rows = [], bySheet = [], scaleInfo = [],
|
|
|
4199
4199
|
schema: "opentakeoff.report.v1",
|
|
4200
4200
|
project_name: projectName || null,
|
|
4201
4201
|
generated_with: "OpenTakeoff",
|
|
4202
|
-
|
|
4202
|
+
// scale_confirmed (scale gate): false = an agent set this sheet's scale and
|
|
4203
|
+
// no human confirmed it — the report's consumer should treat those sheets'
|
|
4204
|
+
// quantities as standing on an unverified number. Absent input = true
|
|
4205
|
+
// (human-era payloads predate the flag).
|
|
4206
|
+
sheets: scaleInfo.map((si) => ({ sheet_id: si.sheet_id, sheet: label(si.sheet_id), scale_source: si.scale_source ?? si.source ?? "unknown", scale_confirmed: si.scale_confirmed !== false })),
|
|
4203
4207
|
// custom-column values APPEND after materials (row key order otherwise
|
|
4204
4208
|
// untouched). Iterating the DEFINED columns — never raw attrs — naturally
|
|
4205
4209
|
// drops orphaned colIds; attrValue (the shared assigned-value rule) keeps
|
|
@@ -5399,11 +5403,13 @@ var Session = class _Session {
|
|
|
5399
5403
|
}
|
|
5400
5404
|
s.upp = upp;
|
|
5401
5405
|
s.scaleSource = source === "label" ? "standard" : source === "calibrate" ? "calibrated" : source;
|
|
5406
|
+
s.scaleConfirmed = false;
|
|
5402
5407
|
return {
|
|
5403
5408
|
sheet: s.key,
|
|
5404
5409
|
upp,
|
|
5405
5410
|
...label ? { label } : {},
|
|
5406
5411
|
source,
|
|
5412
|
+
confirmed: false,
|
|
5407
5413
|
// #153 — several DISTINCT scale notes on one sheet means enlarged plans
|
|
5408
5414
|
// or details are likely; region measurements will warn when a
|
|
5409
5415
|
// disagreeing note sits inside them, but say it up front too
|
|
@@ -6679,7 +6685,8 @@ var Session = class _Session {
|
|
|
6679
6685
|
summary() {
|
|
6680
6686
|
const rows = conditionTotals(this.conditions, this.shapes, this.seamCtx());
|
|
6681
6687
|
const lean = rows.map(({ color, fill, hatch, materials, ...rest }) => rest);
|
|
6682
|
-
|
|
6688
|
+
const unconfirmed = [...this.sheets.values()].filter((s) => s.upp != null && s.scaleConfirmed === false).map((s) => s.key);
|
|
6689
|
+
return { conditions: lean, totals: grandTotals(rows), ...unconfirmed.length ? { scale_unconfirmed: unconfirmed } : {} };
|
|
6683
6690
|
}
|
|
6684
6691
|
deleteShape(id) {
|
|
6685
6692
|
const i = this.shapes.findIndex((x) => x.id === id);
|
|
@@ -7458,7 +7465,15 @@ var Session = class _Session {
|
|
|
7458
7465
|
schema: ANN_SCHEMA,
|
|
7459
7466
|
project_name: "",
|
|
7460
7467
|
units: "imperial",
|
|
7461
|
-
sheets: [...this.sheets.values()].filter((s) => s.upp != null).map((s) => ({
|
|
7468
|
+
sheets: [...this.sheets.values()].filter((s) => s.upp != null).map((s) => ({
|
|
7469
|
+
sheet_id: s.key,
|
|
7470
|
+
units_per_px: s.upp,
|
|
7471
|
+
// provenance rides the payload (it used to be dropped here): the canvas
|
|
7472
|
+
// hydrates scale_source for its report and scale_confirmed for the
|
|
7473
|
+
// scale gate's confirm affordance — absent = confirmed (pre-flag docs)
|
|
7474
|
+
...s.scaleSource ? { scale_source: s.scaleSource } : {},
|
|
7475
|
+
...s.scaleConfirmed === false ? { scale_confirmed: false } : {}
|
|
7476
|
+
})),
|
|
7462
7477
|
conditions: this.conditions,
|
|
7463
7478
|
shapes: this.shapes,
|
|
7464
7479
|
markups: this.markups,
|
|
@@ -7488,7 +7503,7 @@ var Session = class _Session {
|
|
|
7488
7503
|
projectName,
|
|
7489
7504
|
rows,
|
|
7490
7505
|
bySheet: sheetTotals(this.conditions, this.shapes),
|
|
7491
|
-
scaleInfo: [...this.sheets.values()].filter((s) => s.upp != null).map((s) => ({ sheet_id: s.key, scale_source: s.scaleSource ?? "unknown" })),
|
|
7506
|
+
scaleInfo: [...this.sheets.values()].filter((s) => s.upp != null).map((s) => ({ sheet_id: s.key, scale_source: s.scaleSource ?? "unknown", scale_confirmed: s.scaleConfirmed !== false })),
|
|
7492
7507
|
markups: this.markups,
|
|
7493
7508
|
rfis: [],
|
|
7494
7509
|
rollGoods: rollReportRows(byCond, rows)
|
|
@@ -7722,6 +7737,7 @@ var setScaleOutput = {
|
|
|
7722
7737
|
upp: z.number().describe("Real feet per image px at render scale 2.0"),
|
|
7723
7738
|
label: z.string().optional().describe("The standard scale label, when set by label or detected note"),
|
|
7724
7739
|
source: z.enum(["label", "upp", "calibrate", "detected"]),
|
|
7740
|
+
confirmed: z.boolean().describe("Always false here: set_scale is the agent surface, and an agent-set scale stays UNCONFIRMED until a human confirms it in the canvas \u2014 quantities still flow, wearing the caveat"),
|
|
7725
7741
|
warning: z.string().optional().describe("Present when the sheet carries MULTIPLE distinct scale notes (#153) \u2014 enlarged plans/details likely; region measurements under a disagreeing note will warn")
|
|
7726
7742
|
};
|
|
7727
7743
|
var oneClickOutput = {
|
|
@@ -7888,7 +7904,8 @@ var takeoffSummaryOutput = {
|
|
|
7888
7904
|
lf_net: z.number(),
|
|
7889
7905
|
ea: z.number(),
|
|
7890
7906
|
sy_net: z.number()
|
|
7891
|
-
}).passthrough()
|
|
7907
|
+
}).passthrough(),
|
|
7908
|
+
scale_unconfirmed: z.array(z.string()).optional().describe("Sheets whose scale is agent-set and no human has confirmed \u2014 these totals stand on an unverified scale; verify against a stated dimension or confirm in the canvas")
|
|
7892
7909
|
};
|
|
7893
7910
|
var exportTakeoffOutput = {
|
|
7894
7911
|
schema: z.string(),
|
|
@@ -9792,6 +9809,7 @@ async function importTakeoff(session, filePath) {
|
|
|
9792
9809
|
if (s && s.upp == null && row.units_per_px > 0) {
|
|
9793
9810
|
s.upp = row.units_per_px;
|
|
9794
9811
|
s.scaleSource = row.scale_source ?? "upp";
|
|
9812
|
+
if (row.scale_confirmed === false) s.scaleConfirmed = false;
|
|
9795
9813
|
}
|
|
9796
9814
|
}
|
|
9797
9815
|
const tagKey2 = (t) => String(t || "").trim().toUpperCase();
|
|
@@ -10375,7 +10393,7 @@ function registerResources(server, session) {
|
|
|
10375
10393
|
// package.json
|
|
10376
10394
|
var package_default = {
|
|
10377
10395
|
name: "opentakeoff-mcp",
|
|
10378
|
-
version: "0.9.
|
|
10396
|
+
version: "0.9.39",
|
|
10379
10397
|
mcpName: "io.github.Kentucky-ai/opentakeoff",
|
|
10380
10398
|
type: "module",
|
|
10381
10399
|
description: "OpenTakeoff MCP server \u2014 drive the takeoff engine from your MCP client over stdio.",
|
package/package.json
CHANGED