Endpoints: 28,729MCP servers: 18,413Payout addresses: 2,071Paid calls: 1,545Letters: 14Defects: 1,324counted 4 min ago
teppi

Server definition

Hash
sha256:aea99e730a961c54dbf53354bd8dd0e2bb270b5876fba48ba15ed60c67b9f899
What it is
What a remote MCP server returned when asked what it offers: 47 tools

The blob, as servednamed by its sha256

{ "instructions": "EasyTerritory MCP server for territory intelligence. ACCESS: discovery is open. Every tools/call and Tasks method needs Authorization: Bearer <EZT MCP API key> (ezt_...). Without one: HTTP 401, error.code=AUTH_REQUIRED — ask the user for their key; do not retry anonymously. Hosts that cannot set a header use OAuth; the user pastes that same ezt_ key once. GUIDANCE HANDLE: every territory/map tool requires guidance_handle from get_guidance (a short rotating code, e.g. bravo-7f3a). Pass result.guidance_handle on every later call. Re-call get_guidance when a tool returns blocked_by=guidance_required. Exempt: get_guidance, submit_feedback, discover_intent, workflow_advisor, and the ep_* knowledge tools. EXECUTION: finish the goal, list every failure, or name the blocker. Listing tools or opening the map is not done. Fetch each async task result once. FAMILIES: auto_build balances ZIP/part territories; account_build groups by a column; direct_build applies known assignments; cluster_points groups points with no territory layer; seed_build grows one territory from one location; calculate_route orders one day's stops; isochrone_build draws a drive-time area; schedule_visits picks which day a visit happens. If discover_intent returns tier1_tool null, ask — do not guess auto_build. ORDER: get_guidance, ezt://part-layers, get_map_visualization, ensure_map_viewer until connected (one map_session_id), ingest_accounts (request_account_upload for a large CSV; pass map_session_id), the family tool, analyze with analysis_panel=single. Never build before ingest. Never compute before the viewer is connected when a person is in the loop. SHARED RULES: workload is drive time plus confirmed dwell — never a metric column such as Revenue. When the user says workload or workload-balanced, use workload_bias=100 and ask dwell only. A named state or region ('TX ZIPs only') is part_scope=explicit and part_filter={state_abbr: TX}, not bbox_intersect. Poll only the task_id from the immediately preceding submission — never an older job. When map_session_id is set on a build, cluster, or schedule call, omit ts and ts_handle; the open map is the Territory Solution. Follow each status next_action (on the status document and under _meta): sleep_and_poll means sleep sleep_ms then one status call; consume_result means fetch the result once; stop_error means stop. Never sleep on estimated_remaining_ms. DETAIL: the tool description has the call rules. Routers and blocked_by errors inline the EMEP page. Index: ezt://guidance/emep. Call submit_feedback when blocked, partial, or a workaround was required. No secrets or PII. Current guidance_handle: sierra-e43c (expires in ~6446s; rotates every 10800s). Pass guidance_handle to gated tools. If blocked_by=guidance_required, call get_guidance for the new handle.", "tools": [ { "description": "[Tier 1 — Attribute Grouping Builder] When: territories should mirror an account attribute column from CRM exports (rep name, territory_name, territory code). Prerequisites: ingest_accounts with grouping column; part_layer chosen; viewer connected. Omit ts and ts_handle when the session id argument is already set. That session is the TS. Numeric codes are labels, not balance metrics; scoped builds use in-scope accounts only. Part scope defaults to bbox_intersect (bbox proximity of ingested accounts). When the user names a state/region ('TX ZIPs only'), pass part_scope=explicit and part_filter={state_abbr: TX} — not bbox_intersect. Scope fields are top-level part_filter/part_ids. Progress: linked tasks publish live subphases on Tasks status / MC overlay (grouping → radial seeds → inflate → empty-part assign → interlock polish) with cooperative cancel — same poll loop as auto_build (next_action / sleep_ms). VISIT FREQUENCY: when a cadence column is declared, pass visit_frequency_field to scale workload or ignore_visit_frequency=true for one visit per cycle — do not inherit the column silently. Modern form-capable clients (protocol >= 2026-07-28) may be prompted in-band for missing grouping_field / part_layer / tal_label; legacy hosts keep required-arg / INVALID_REQUEST errors. Next: analyze + load_analysis_panel. Scenarios: ACB-001..004, MC-011.", "inputSchema": { "properties": { "conflict_policy": { "default": "plurality_account_count", "title": "Conflict Policy", "type": "string" }, "expected_revision": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "default": null, "title": "Expected Revision" }, "grouping_field": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Grouping Field" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "ignore_visit_frequency": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ], "default": null, "title": "Ignore Visit Frequency" }, "map_session_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Map Session Id" }, "output_mode": { "default": "tal", "description": "tal is the default. assignments returns assignments_artifact plus an authenticated download_url; no ts_handle, tal_id, or map_refresh.", "enum": [ "tal", "assignments" ], "title": "Output Mode", "type": "string" }, "part_filter": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Part Filter" }, "part_ids": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Part Ids" }, "part_layer": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Part Layer" }, "part_scope": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Part Scope" }, "point_layer": { "title": "Point Layer", "type": "string" }, "repair_policy": { "default": "default", "title": "Repair Policy", "type": "string" }, "tal_label": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Tal Label" }, "ts": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Ts" }, "ts_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Ts Handle" }, "visit_frequency_field": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Visit Frequency Field" } }, "required": [ "point_layer" ], "title": "account_buildArguments", "type": "object" }, "name": "account_build", "outputSchema": { "additionalProperties": true, "title": "account_buildDictOutput", "type": "object" } }, { "description": "[Tier 1 — Analysis] When: balance diagnostics, cross-TAL comparison, or post-mutation facts. Accepts ts, ts_handle, or completed job_id (inline ts or result.ts_handle from prior compute jobs). Prerequisites: TAL exists; re-run after any TAL or point change (I-2)—never treat stale analysis as current. Returns JSON facts and presentation guidance URI; no prose. metrics: omit to analyze all declared point-layer columns, or pass explicit names. System dimensions (always valid, not point columns): workload (territory hours; alias total_workload_hours), account_count. Point columns must be fields declared at ingest (metric_fields/workload_fields, e.g. Revenue, Units Sold); undeclared columns are discarded at ingest and fail with UNDECLARED_FIELD — re-ingest with the column declared to analyze it. Response includes available_metrics. METRIC COLUMNS ALWAYS RENDER: the territory_metric_grids (and the MC dock) carry Total Count plus a Total <metric> column for EVERY declared metric_fields column of the point layer, in declaration order, even when metrics names only account_count or workload — a count-only panel is never the correct outcome when metrics are declared. Declared names may be bare strings (Designer pull) or {field,label,type} objects (ingest). Metric cells are parsed leniently: '14,651', '$1,200.50', and padded strings sum as numbers. DWELL / WORKLOAD (T-171): workload hours need onsite/dwell time — request dwell_time, TAL build_provenance.dwell_time (auto_build), or the point layer's dwell_time_field. When none resolves, Analyze STILL RUNS and reports counts, metrics, classification breakdowns, and balance on those dimensions; workload is OMITTED (no total_workload_hours column, workload_total null) — never drive-only hours. The result then carries workload_omitted {tal_ids, ask_user, retry}: report the statistics FIRST, then relay ask_user verbatim (it asks for an average onsite/dwell time). Re-run analyze with dwell_time only if the user answers; never invent a default (including 30 minutes). Do not ask for dwell before the first analyze just to avoid the note. Prefer analysis_panel=single with map_session_id so the MC dock fills on this call — that is the one-shot post-build path. The completed result then has analysis_panel.status=pushed, and session state has analysis_panel.loaded=true. phase_timings_ms.panel_push is only a duration, not proof the dock opened. Otherwise pass the full Analyze result (or its task_id) to load_analysis_panel. WORKLOAD UNITS: territories[].workload_total is minutes (workload_unit=minutes). Quote hours only from territory_metric_grids cell total_workload_hours.value. When metrics is omitted and dwell resolves, balance_scores includes workload plus declared metrics — not a location-match score. DOCK HIDE: minimizing the table leaves analysis_panel.loaded true; do not call load_analysis_panel again just to restore it. A new analysis_panel_loaded event opens the dock. Chat-only JSON is not completion when a map is open. Use ezt://guidance/analysis-presentation for narrative. Full atom: ezt://guidance/workflows/analyze-and-present. Scenarios: AN-001..007, MC-009, S002.", "inputSchema": { "properties": { "analysis_panel": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Analysis Panel" }, "compare_tals": { "default": false, "title": "Compare Tals", "type": "boolean" }, "dwell_time": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Dwell Time" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "hypothetical_moves": { "anyOf": [ { "items": { "additionalProperties": true, "type": "object" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Hypothetical Moves" }, "job_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Job Id" }, "map_session_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Map Session Id" }, "max_depth": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "default": null, "title": "Max Depth" }, "metrics": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Metrics" }, "part_layer": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Part Layer" }, "scope": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Scope" }, "tal_ids": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Tal Ids" }, "ts": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Ts" }, "ts_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Ts Handle" }, "visit_frequency_field": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Visit Frequency Field" } }, "title": "analyzeArguments", "type": "object" }, "name": "analyze", "outputSchema": { "additionalProperties": true, "title": "analyzeDictOutput", "type": "object" } }, { "description": "[Tier 2 — Route Facts] When: you need per-route numbers for routes calculate_route already drew — drive hours, dwell hours, route_workload_hours, stop coordinates, centroid, bbox (e.g. 'which of my Houston runs has room for one more stop', 'total hours for each route'). Prerequisites: at least one route in the session/TS (map_session_id preferred, else ts_handle); dwell confirmed by the user unless calculate_route already stored it. route_workload_hours = provider drive time + confirmed dwell, with NO visit-frequency multiplier. dwell_hours is one dwell per stop in stop_count. A circuit's stop_count includes the return to the start, so that account is charged dwell twice. Cluster and territory workload charge each account once. This is a DIFFERENT quantity from those hours — never sum, compare, or substitute one for the other. FACTS ONLY: no capacity, no headroom, no ranking, no overloaded flag. Apply constraints like 'nearest route under 7 hours' yourself from centroid + route_workload_hours, then add the stop by re-running calculate_route with that route_id and the revised stop list. Anti-patterns: do NOT call analyze for routes (analyze is TAL/part-grained and returns territory workload); do NOT feed these hours into auto_build, territory_rebalance, or a territory workload figure. Unresolved dwell returns CLARIFICATION_REQUIRED / needs_dwell — ask the user, never invent a default (including 30 minutes). Synchronous: no task_id. Scenarios: RT-011.", "inputSchema": { "properties": { "dwell_time": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Dwell Time" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "map_session_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Map Session Id" }, "route_ids": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Route Ids" }, "ts_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Ts Handle" } }, "title": "analyze_routesArguments", "type": "object" }, "name": "analyze_routes", "outputSchema": { "additionalProperties": true, "title": "analyze_routesDictOutput", "type": "object" } }, { "description": "[Tier 1 — Balanced Territory Builder] When: partition account points into N balanced territories over a part layer (for example, ten territories; Mode A count, Mode B workload target, Scoped Split). Execution gate: after sizing, balance, dwell, and scope are known, call THIS tool immediately — searching the catalog, get_guidance, or polling Tasks never starts a build. A successful response with a new task_id is the only proof of submit; do not claim started/restarting until then. Then follow do_this_next only with that new task_id. When workflow_advisor returns next_tool=auto_build, call auto_build next. Canonical example — 3 TX ZIP territories, workload-only, 30-min dwell: build_mode={mode: fixed_territory_count, territory_count: 3}, objective={workload_bias: 100}, dwell_time={type: scalar, value: 30, unit: minutes}, part_scope=explicit, part_filter={state_abbr: TX}, map_session_id=<open MC>. Omit ts and ts_handle when the session id argument is already set. That session is the TS. Do not repost inline ts. build_mode.mode must be exactly one of: fixed_territory_count, fixed_workload_target, scoped_split. For workload targets (e.g. 40-hour territories), use build_mode={mode: fixed_workload_target, target_workload: 40} (territories only approximate the target). ALWAYS ask for dwell/onsite time before calling — Auto Build always computes territory workload hours (drive + dwell), even when balancing on a metric or account count. Pass dwell_time only after they confirm a column ({type: field, field, unit}) or scalar ({type: scalar, value, unit}). Never invent default dwell such as 1 hour or 30 minutes per visit. Server rejects auto_build without resolved dwell_time (CLARIFICATION_REQUIRED). VISIT FREQUENCY: when the point layer has a visit-frequency column, ask whether to aggregate workload across a schedule period (pass visit_frequency_field) or ignore it (pass ignore_visit_frequency=true). Do not inherit the column silently. If no visit-frequency column exists, omit both. Modern clients that negotiate protocol >= 2026-07-28 and declare form elicitation may answer missing sizing/dwell (and optional balance) in-band via Resolve/Elicit; legacy/Cursor hosts without form elicitation keep CLARIFICATION_REQUIRED / ask_user (HITL-025 — not a server failure). build_mode may be omitted when elicitation can fill it. Workload is drive time plus dwell — not Revenue or any metric column. When the user says 'balanced on workload' or 'workload-balanced', use workload_bias=100 with no objective.metric; do not ask which metric column workload means — ask dwell (Q5) only. Confirm balance dimension (Q2) and bias (Q3) with the user when unclear — a workload-hour target is sizing, not permission to silently set workload_bias=100 unless workload is already named as the balance dimension. Declare metric_fields at ingest but ask which column (if any) to balance on before auto_build when the user did not already choose workload balance. After build completes: analyze with map_session_id and analysis_panel=single (or load_analysis_panel) so stats appear in the MC dock — not chat-only. Part scope defaults to bbox_intersect (ZIPs intersecting the account-point bbox) — geographic proximity, not state/attribute scope. When the user names a state or region ('TX ZIPs only', 'Texas only'), pass part_scope=explicit and part_filter={state_abbr: TX} immediately; do not default to bbox_intersect (can pull tens of thousands of national ZIPs) and do not query_parts first for an obvious state filter. Scope fields are top-level part_filter/part_ids — never under objective. A genuinely nationwide ZIP request remains supported: when bbox_intersect resolves at national scale, the job continues at full ZIP-level quality and returns a NATIONAL_PART_SCOPE warning rather than silently switching geography. Long partitions publish named 35–42% subphases (travel cache, power solve, border refinement, compactness, seam polish) and renew their worker lease independently; keep polling via next_action/sleep_ms and do not infer a stall from one long quality pass. Prerequisites: ingest_accounts done, part_layer chosen, viewer connected (human-in-loop). Use direct_build for known assignments; account_build to group by attribute. Appends a TAL; does not replace existing alignments. Dissolved leaves report min_solidity beside mean_polsby_popper and min_polsby_popper. Gate shape from those figures rather than recomputing them from a download. Next: analyze with analysis_panel=single. Full atom: ezt://guidance/workflows/build-from-accounts. Scenarios: S003, AB-001..017, MC-011.", "inputSchema": { "properties": { "build_mode": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Build Mode" }, "dwell_time": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Dwell Time" }, "expected_revision": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "default": null, "title": "Expected Revision" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "ignore_visit_frequency": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ], "default": null, "title": "Ignore Visit Frequency" }, "map_session_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Map Session Id" }, "objective": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Objective" }, "output_mode": { "default": "tal", "description": "tal is the default. assignments returns assignments_artifact plus an authenticated download_url; no ts_handle, tal_id, or map_refresh.", "enum": [ "tal", "assignments" ], "title": "Output Mode", "type": "string" }, "part_filter": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Part Filter" }, "part_ids": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Part Ids" }, "part_layer": { "title": "Part Layer", "type": "string" }, "part_scope": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Part Scope" }, "point_layer": { "title": "Point Layer", "type": "string" }, "repair_policy": { "default": "default", "title": "Repair Policy", "type": "string" }, "tal_label": { "title": "Tal Label", "type": "string" }, "ts": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Ts" }, "ts_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Ts Handle" }, "visit_frequency_field": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Visit Frequency Field" } }, "required": [ "point_layer", "part_layer", "tal_label" ], "title": "auto_buildArguments", "type": "object" }, "name": "auto_build", "outputSchema": { "additionalProperties": true, "title": "auto_buildDictOutput", "type": "object" } }, { "description": "[Tier 1 — Routing] When: drive a list of stops in the best sequence — a windshield itinerary, a service run, a delivery sheet (RT-001..RT-008). This tool routes and sequences existing stops; it never creates or rebalances territories. When a visit-frequency column is on the point layer, the word route is ambiguous — confirm the user wants a driving itinerary of those stops, not schedule_visits, before calling this tool. Prerequisites: a TomTom or Azure Maps key on the server; stops referenced by point_layer must already be ingested, or pass inline lat/lon. TERRITORY STOPS: after a TAL is built or analyze linkage runs, account points carry territory_id. Route one territory with stop_sets source.filter.territory_id set to the leaf id from the last build (leaf_territories[].territory_id), not a display label. start is exactly one stop (home, depot, or one point_id). The territory filter belongs on stop_sets, never on start. ORDERED STOP SETS: stop_sets are visited in the order supplied and optimization NEVER moves a stop between sets, which keeps 'start at home, hit the depot, then the day's calls' in the right sequence. Use order='optimize' to let the solver sequence a set, 'as_given' to keep it fixed. group_by_field splits one set into ordered bands on a column value (e.g. route_priority: every 1 precedes every 2, optimized inside each band). ROUTE TYPES: 'circuit' returns to the start; 'tour' finishes at a declared end stop (end is required); 'open_tour' is a one-way run that ends at the last stop the solver picks, never returning to the start. A circuit's stop_count includes that return. analyze_routes charges dwell once per stop, so the start account is charged dwell twice. Cluster and territory workload charge each account once — those hour figures are not this route's hours. Sequencing is solved server-side against straight-line distance, then one provider call returns road distances, durations, and geometry, so reported numbers are always road-accurate. Drive time here is not the territory workload model — auto_build keeps its own. MANY ROUTES ON ONE MAP: each route is stored in the TS under a route_id, so calling this tool again with a NEW route_id ADDS a route (10 Houston routes coexist, each with its own legend row and color). Reusing an EXISTING route_id REPLACES that route in place, which is the way to add or drop a stop: re-route the same route_id with the revised stop list. Never delete and re-add for a stop change, and never omit route_id when you meant to add a second route (an auto-minted id is fine for a one-off). Use delete_route to drop a whole route; per-route workload facts come from analyze_routes, never from analyze. Pass dwell_time when you want route workload recomputable later; it is stored as provenance and never invented. QUOTA: billable provider calls are capped per API key per calendar month, and a long stop list chunked to the vendor waypoint cap costs one call per chunk. A route is all-or-nothing: past the cap it returns PROVIDER_QUOTA_EXCEEDED with limit, used, remaining, and period_resets_at rather than a partial path. An operator must raise the cap, so report those numbers instead of retrying with fewer stops. Async: returns task_id — follow _meta.next_action (sleep_and_poll → consume_result). Pass map_session_id to draw the path and numbered stops on an open map. Full atom: ezt://guidance/workflows/routes-layer. Scenarios: RT-001..RT-011.", "inputSchema": { "properties": { "depart_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Depart At" }, "dwell_time": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Dwell Time" }, "end": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "End" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "include_geometry": { "default": true, "title": "Include Geometry", "type": "boolean" }, "map_session_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Map Session Id" }, "route_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Route Id" }, "route_label": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Route Label" }, "route_type": { "title": "Route Type", "type": "string" }, "start": { "additionalProperties": true, "title": "Start", "type": "object" }, "stop_sets": { "items": { "additionalProperties": true, "type": "object" }, "title": "Stop Sets", "type": "array" }, "travel_mode": { "default": "car", "title": "Travel Mode", "type": "string" }, "ts_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Ts Handle" } }, "required": [ "route_type", "start", "stop_sets" ], "title": "calculate_routeArguments", "type": "object" }, "name": "calculate_route", "outputSchema": { "additionalProperties": true, "title": "calculate_routeDictOutput", "type": "object" } }, { "description": "[Tier 1 — Balanced Point Grouping] Partition one ingested point layer directly into balanced point groups with CCPD, without using ZIPs/counties or creating a TAL. The tool adds group_field (default group_id) to every point and color-classifies that field in the linked Map Component. That write replaces the layer's existing color classification (including a leftover color_classification ramp). Size and shape channels stay. Keep a prior metric as a second encoding with configure_map channel=size or channel=shape after this job — never a second color classification on the same layer. Example — five TX point groups balanced on workload with confirmed 30-minute dwell: point_layer=accounts, build_mode={mode: fixed_territory_count, territory_count: 5}, objective={workload_bias: 100}, dwell_time={type: scalar, value: 30, unit: minutes}. Prerequisite: ingest_accounts completed for point_layer. Omit ts and ts_handle when the session id argument is already set. That session is the TS. Ask the same sizing, balance, bias, visit-frequency, and dwell questions; workload means in-group drive time plus dwell, never a metric column. Never invent dwell. build_mode supports fixed_territory_count and fixed_workload_target only. SUBSET: a named state or region on an already-ingested layer — Florida schools, TX accounts only — uses point_filter on the first call, e.g. point_filter={STATE: FL}. Keys are point properties (one value or a list). Do not re-ingest a filtered extract. Do not pass part_filter or part_scope (those belong to ZIP/part tools). Do not cluster the national layer. Unmatched points stay on the layer with group_field cleared and appear as an Ungrouped legend class. The 10,000-point cap applies after the filter. Empty match is EMPTY_POINT_FILTER; unknown property is UNKNOWN_POINT_PROPERTY. SEEDING BIAS (start locations): when the groups should line up with a set of start locations — technician homes, depots, branch offices — pass seed_point_layer=<that layer>. It must be a SECOND ingested point layer, not point_layer. Each seed anchors exactly ONE group, so the seed count must match the group count (fixed_workload_target must derive that same count) or the call fails INVALID_REQUEST with blocked_by=seed_count_mismatch. seed_attraction (0-100, default 70) controls the hold on each group centroid: 100 pins it at its seed, 0 lets the seeds pick only the starting points and the solver drift to the workload centroid. Groups stay workload/metric balanced either way — seeds carry NO workload and are never members of the group they anchor. Both layers receive the same group_field value and the same per-group color, so a technician home paints in its group's color; read result.seed_summary.groups for each pairing plus seed_to_centroid_km. Example — 10 technician homes anchor 10 workload-balanced account groups: point_layer=accounts, seed_point_layer=technician_homes, seed_attraction=100, build_mode={mode: fixed_territory_count, territory_count: 10}. This tool does not spatially join points to parts and does not produce a TAL. Use auto_build for territories. Follow the returned task. The result lists groups (group_id, point_count, workload_hours, point_ids). Full atom: ezt://guidance/workflows/cluster-points. Scenarios: CP-001..005.", "inputSchema": { "properties": { "build_mode": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Build Mode" }, "dwell_time": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Dwell Time" }, "expected_revision": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "default": null, "title": "Expected Revision" }, "group_field": { "default": "group_id", "title": "Group Field", "type": "string" }, "group_label_prefix": { "default": "Group", "title": "Group Label Prefix", "type": "string" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "map_session_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Map Session Id" }, "metric_fields": { "anyOf": [ { "items": { "additionalProperties": true, "type": "object" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Metric Fields" }, "objective": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Objective" }, "point_filter": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Point Filter" }, "point_layer": { "title": "Point Layer", "type": "string" }, "seed_attraction": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "default": null, "title": "Seed Attraction" }, "seed_point_layer": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Seed Point Layer" }, "ts": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Ts" }, "ts_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Ts Handle" }, "visit_frequency_field": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Visit Frequency Field" } }, "required": [ "point_layer" ], "title": "cluster_pointsArguments", "type": "object" }, "name": "cluster_points", "outputSchema": { "additionalProperties": true, "title": "cluster_pointsDictOutput", "type": "object" } }, { "description": "[Tier 2 — Durable Map Config] When: set or update project_name (the durable TS short name used as the Map Component heading), loaded_part_layers, active_part_layer, active_tal_id, point_layer_classifications, classification, presentation, center, or zoom on the TS. project_name is first-class: persists to ts.properties.map_config.project_name and emits config_changed so a linked session refreshes the heading in place. POINT SYMBOLOGY: point_layer_classifications is the way to recolor/resize/reshape points on an open map — never export GeoJSON, compute breaks client-side, and repost a TS. Each entry is {point_layer, field, method: quantile|equal_interval|categorical|manual, class_count (2-12), channel: color|size|shape, optional colors/sizes/shapes, optional style:{color,size,opacity,shape} for the layer base symbol}. Supported shapes: circle|square|triangle|diamond|star|cross|house|pin|flag|hexagon|pentagon|shield|arrow_up|building (aliases home→house, marker/map_pin→pin, hex→hexagon, arrow/up→arrow_up, warehouse/depot→building, plus/x→cross). The server computes breaks from the in-session points, writes point_layers[].<channel>_classification, pushes config_changed, and returns the applied classes with per-class counts. Categorical missing values become an Ungrouped class. One classification per channel per layer: a second color entry replaces the first. Two encodings on one layer mix channels (color+size or color+shape). clear:true with point_layer and channel drops that channel classification, including a same-channel classification object. Other channels and the base style stay. active_channels reports the channels still set. Method defaults to quantile for numeric fields and categorical otherwise; pass one entry per layer to give two point layers distinct colors or shapes. classification (without a point_layer) stays a project-level map_config patch and does NOT paint points; a point-layer-targeted classification is routed to symbology with a warning. Optional center ([longitude, latitude]) and zoom (0-24) persist as the TS default camera and jump an open MC when included on this call; Monica's pan is not captured. Prefer map_session_id for an open MC — the live session TS is the patch base; a stale pre-ingest ts_handle must not strip points/part layers. Not the entry tool for browsing a TS — that is get_map_visualization (I-1). Prerequisites: map_session_id, ts_handle, or inline ts; points already ingested before point_layer_classifications; part_layer from ezt://part-layers before builds. Next: build or analyze; if active TAL changes, re-run analyze then load_analysis_panel (I-2). Scenarios: MC-010, MC-012, DS-001, baseline workflow step 4.", "inputSchema": { "properties": { "active_part_layer": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Active Part Layer" }, "active_tal_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Active Tal Id" }, "center": { "anyOf": [ { "items": { "type": "number" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Center" }, "classification": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Classification" }, "expected_content_hash": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Expected Content Hash" }, "expected_revision": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "default": null, "title": "Expected Revision" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "loaded_part_layers": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Loaded Part Layers" }, "map_session_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Map Session Id" }, "merge_strategy": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Merge Strategy" }, "point_layer_classifications": { "anyOf": [ { "items": { "additionalProperties": true, "type": "object" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Point Layer Classifications" }, "presentation": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Presentation" }, "project_name": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Project Name" }, "ts": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Ts" }, "ts_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Ts Handle" }, "zoom": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "default": null, "title": "Zoom" } }, "title": "configure_mapArguments", "type": "object" }, "name": "configure_map", "outputSchema": { "additionalProperties": true, "title": "configure_mapDictOutput", "type": "object" } }, { "description": "[Tier 2 — Territory From Parts] When: create or update one leaf territory from committed part IDs (manual MC-005 build or RL-011/012). Prerequisites: part_ids from selection or agent list; part_layer; viewer connected. Pass map_session_id for the open MC — the server loads that session's TS, appends the new TAL, and rebinds the same session before map_refresh (do not call get_map_visualization just to show the new territory). If result.map_refresh.notified is false or status is rebind_required, call get_map_visualization(job_id=<this job>). Completed result includes created_territory.territory_id, leaf_territories, and selection_summary (requested/unique counts plus duplicate_part_ids) — territory_name is a display label only (e.g. T1 → territory_id like tal-t1-t1). Later realign into this territory MUST use created_territory.territory_id (preferred) or the exact leaf display name when unique; never invent shorthand (T3 ≠ Territory 3) — if unclear, ask which leaf from leaf_territories. DWELL: this tool creates a NEW TAL and does not inherit dwell_time from a prior auto_build. Dwell is NOT required to create the territory (same as direct_build). Optional dwell_time={type:scalar,value,unit} stamps build_provenance for later Analyze. Without dwell, Analyze still runs and reports every other statistic but OMITS workload (result.workload_omitted) — present the stats, then relay its ask_user sentence; never invent a default (including 30 minutes). Modern form-capable clients may be prompted in-band for dwell when a hydrated TS shows points without dwell provenance; declining still allows create (HITL-038). Next: repeat for additional territories, realign with created_territory.territory_id, or analyze with confirmed dwell_time. Scenarios: MC-005, RL-011, RL-012.", "inputSchema": { "properties": { "conflict_policy": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Conflict Policy" }, "dwell_time": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Dwell Time" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "map_session_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Map Session Id" }, "part_ids": { "items": { "type": "string" }, "title": "Part Ids", "type": "array" }, "part_layer": { "title": "Part Layer", "type": "string" }, "tal_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Tal Id" }, "territory_name": { "title": "Territory Name", "type": "string" }, "territory_path": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Territory Path" }, "ts": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Ts" } }, "required": [ "part_layer", "part_ids", "territory_name" ], "title": "create_territory_from_partsArguments", "type": "object" }, "name": "create_territory_from_parts", "outputSchema": { "additionalProperties": true, "title": "create_territory_from_partsDictOutput", "type": "object" } }, { "description": "[Tier 2 — Delete Route] When: drop a whole route from the map and the TS (e.g. 'delete the Tuesday A route', 'remove route houston-tue-a'). Prerequisites: map_session_id for an open MC (preferred), else ts_handle; plus route_id (or an exact unique route label). NOT for removing a single stop — for that, call calculate_route again with the SAME route_id and the remaining stops, which replaces the route in place. Already-absent routes return ok with already_absent=true and no error. An ambiguous label returns AMBIGUOUS_ROUTE with matching_route_ids — pass an explicit route_id rather than guessing. Synchronous: no task_id. Verify the open map legend no longer lists the route. Scenarios: RT-010.", "inputSchema": { "properties": { "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "map_session_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Map Session Id" }, "route_id": { "title": "Route Id", "type": "string" }, "ts_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Ts Handle" } }, "required": [ "route_id" ], "title": "delete_routeArguments", "type": "object" }, "name": "delete_route", "outputSchema": { "additionalProperties": true, "title": "delete_routeDictOutput", "type": "object" } }, { "description": "[Tier 1 — Delete TAL] When: wipe one whole Territory Alignment Layer (user language: territory layer, alignment, all territories, active alignment — not only 'TAL') from the TS and open map (e.g. 'remove the territory layer', 'wipe all territories', 'remove the alignment', 'clear tal-tx-10t before rebuild'). Prerequisites: map_session_id for an open MC (preferred), else ts_handle; plus tal_id (or an exact unique TAL label). NOT for deleting one leaf territory — that is delete_territory. Do NOT N× delete_territory to wipe an alignment. Synchronous: no task_id / no Realign progress overlay. Points, part-layer overlays, and routes stay; active_tal_id becomes a remaining TAL, __points__, or __empty__. Already-absent returns ok with already_absent=true. Ambiguous label returns AMBIGUOUS_TAL. Next: verify the legend dropped the alignment, then auto_build / account_build / direct_build when rebuilding. Scenarios: HITL-057, T-134.", "inputSchema": { "properties": { "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "map_session_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Map Session Id" }, "tal_id": { "title": "Tal Id", "type": "string" }, "ts_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Ts Handle" } }, "required": [ "tal_id" ], "title": "delete_talArguments", "type": "object" }, "name": "delete_tal", "outputSchema": { "additionalProperties": true, "title": "delete_talDictOutput", "type": "object" } }, { "description": "[Tier 1 — Delete Territory] When: delete one leaf territory from an existing TAL (e.g. 'delete T1', 'remove territory West'). Prerequisites: ONE current TS reference — prefer map_session_id for an open MC, else ts_handle or ts; tal_id; territory_id (stable leaf id from created_territory.territory_id / leaf_territories preferred, or an exact unique display name — do not invent shorthand like T3 for Territory 3; ask if unclear). Do NOT invent part_ids or call auto_build. This tool collects the leaf's part_ids and runs Realign remove_parts + remove_empty_territories (same engine as RL-013). To wipe an entire TAL / alignment before rebuild, call delete_tal instead — never N× this tool. Already-absent territories return ok with already_absent=true (no job). Next: follow the returned Tasks status operation until completed, call its result operation once, then verify the open map legend no longer lists the territory AND the territory fill/outline is gone from the canvas (not just the legend) before claiming success. Geography-only delete does not require Analyze. Scenarios: feedback delete-territory, RL-013 wrapper, T-087 last-leaf paint clear.", "inputSchema": { "properties": { "expected_revision": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "default": null, "title": "Expected Revision" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "map_session_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Map Session Id" }, "part_layer": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Part Layer" }, "repair_policy": { "default": "default", "title": "Repair Policy", "type": "string" }, "tal_id": { "title": "Tal Id", "type": "string" }, "territory_id": { "title": "Territory Id", "type": "string" }, "ts": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Ts" }, "ts_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Ts Handle" } }, "required": [ "tal_id", "territory_id" ], "title": "delete_territoryArguments", "type": "object" }, "name": "delete_territory", "outputSchema": { "additionalProperties": true, "title": "delete_territoryDictOutput", "type": "object" } }, { "description": "[Tier 1 — Known Assignments Builder] When: user has explicit part-to-territory assignments (spreadsheet, legacy file, hierarchical territory_path). assignments_handle must be a server upload handle (aup_...) from POST /assignments/upload or request_assignment_upload — after parsing rows or passing csv_text/csv_file. Legacy spreadsheets (Postal Code + Territory/Region/Division) are auto-mapped when staged. Conflicting duplicate part_ids default to duplicate_part_policy=keep_first (first wins). Prerequisites: part_layer chosen; viewer connected for MC-first; ts/ts_handle optional. Not for account point locations—use ingest_accounts. Not for balanced partitioning—use auto_build. Not for attribute grouping—use account_build. Next: verify TAL in MC (MC-011), analyze + load_analysis_panel. Scenarios: DB-001..005, MC-011.", "inputSchema": { "properties": { "assignments": { "anyOf": [ { "items": { "additionalProperties": true, "type": "object" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Assignments" }, "assignments_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Assignments Handle" }, "duplicate_part_policy": { "default": "keep_first", "title": "Duplicate Part Policy", "type": "string" }, "expected_revision": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "default": null, "title": "Expected Revision" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "map_session_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Map Session Id" }, "missing_part_policy": { "default": "fail", "title": "Missing Part Policy", "type": "string" }, "part_layer": { "title": "Part Layer", "type": "string" }, "repair_policy": { "default": "default", "title": "Repair Policy", "type": "string" }, "tal_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Tal Id" }, "tal_label": { "title": "Tal Label", "type": "string" }, "ts": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Ts" }, "ts_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Ts Handle" } }, "required": [ "part_layer", "tal_label" ], "title": "direct_buildArguments", "type": "object" }, "name": "direct_build", "outputSchema": { "additionalProperties": true, "title": "direct_buildDictOutput", "type": "object" } }, { "description": "[Tier 1 — Router] Deterministic entry point for ambiguous or open-ended territory requests. When: the user's goal or build type is unclear and you want the correct workflow before acting. Prerequisites: none (static keyword routing; no compute). Returns an intent_category, the recommended tool order, the Tier 1 tool + Tier 2 alternatives, required_inputs, clarifying_questions to ask when inputs are missing, a guidance_uri (ezt://guidance/workflows/{name}) for the full atom, and `guidance` — the same EMEP atom inlined as {uri, title, excerpt, truncated}, so you do NOT need a second resources/read to get the workflow text. Use it to disambiguate auto_build vs account_build vs direct_build and to route realign / restructure / analyze / delegation / geocode-ingest / load-part-layer-on-map (add zips, show zip codes) / route-stops (drive a list of stops in the best order) / reachable-area (a drive-time or drive-distance area around origins — service area, catchment, coverage, isochrone, which routes to isochrone_build) / periodic-scheduling (a recurring cadence — 'every 30 days', 'twice a month' — and which day each visit lands on, which routes to schedule_visits, never auto_build or cluster_points) / push-to-designer (an EasyTerritory Designer rolodex project from a TS: export_geojson then POST FromTerritorySolution to create or PUT .../Projects/{projectId}/... to update; territories, points, and routes import; ezt_pat_; there is no MCP push tool) / pull-from-designer (a Designer rolodex project into the MCP: GET REST/Agent/Projects then GET .../Projects/{projectId}/TerritorySolution with ezt_pat_, then import_geojson geojson_gzip; there is no MCP pull tool). Scenarios: AB-016, ACB-003, wrong-build-tool.", "inputSchema": { "properties": { "context": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Context" }, "user_request": { "title": "User Request", "type": "string" } }, "required": [ "user_request" ], "title": "discover_intentArguments", "type": "object" }, "name": "discover_intent", "outputSchema": { "additionalProperties": true, "title": "discover_intentDictOutput", "type": "object" } }, { "description": "[Tier 1 — MC-First Gate] When: after get_map_visualization returns map_url and before any compute, build, analyze, or selection work with a human in the loop. Prerequisites: map_session_id from get_map_visualization. Next: ingest, configure_map, build, realign, or analyze once viewer_status.connected. Surface-agnostic: the in-chat MCP App shell connects the same session over the same SSE channel, so this gate works unchanged whether the human is looking at the in-chat map or the map_url tab. Do not skip it on an Apps host. Scenarios: MV-001, I-1.", "inputSchema": { "properties": { "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "map_session_id": { "title": "Map Session Id", "type": "string" }, "poll_interval_ms": { "default": 500, "title": "Poll Interval Ms", "type": "integer" }, "wait_seconds": { "default": 0, "title": "Wait Seconds", "type": "number" } }, "required": [ "map_session_id" ], "title": "ensure_map_viewerArguments", "type": "object" }, "name": "ensure_map_viewer", "outputSchema": { "additionalProperties": true, "title": "ensure_map_viewerDictOutput", "type": "object" } }, { "description": "[Knowledge Retrieval] Traverse the EMEP knowledge graph from a topic file (e.g. 'workflows/realign-by-selection.md') to find related guidance, up to `depth` hops. Returns no edges unless the pack ships a _graph.yaml.", "inputSchema": { "properties": { "depth": { "default": 1, "title": "Depth", "type": "integer" }, "edge_kinds": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Edge Kinds" }, "file_path": { "title": "File Path", "type": "string" } }, "required": [ "file_path" ], "title": "ep_graph_traverseArguments", "type": "object" }, "name": "ep_graph_traverse", "outputSchema": { "additionalProperties": true, "title": "ep_graph_traverseDictOutput", "type": "object" } }, { "description": "[Knowledge Retrieval] List EMEP topics grouped by type (concept, workflow, interface, troubleshooting, decision). Use to discover what guidance exists before ep_search, or to browse the pack. Optional `type` filter.", "inputSchema": { "properties": { "type": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Type" } }, "title": "ep_list_topicsArguments", "type": "object" }, "name": "ep_list_topics", "outputSchema": { "additionalProperties": true, "title": "ep_list_topicsDictOutput", "type": "object" } }, { "description": "[Knowledge Retrieval] Semantic search over the EZT MCP Expert Pack (EMEP) for targeted workflow guidance, concepts, interfaces, and common mistakes. When: you are unsure which tool or order to use, hit an error, or need product-specific domain context before acting. Returns ranked markdown chunks with source files; atoms pulled in via `requires` are flagged requires_expanded. Degrades to the ezt://guidance/... resources if retrieval is unavailable. Examples: 'build balanced territories from accounts', 'viewer not connected before compute', 'auto_build vs account_build'.", "inputSchema": { "properties": { "max_results": { "default": 6, "title": "Max Results", "type": "integer" }, "query": { "title": "Query", "type": "string" }, "tags": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Tags" }, "type": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Type" } }, "required": [ "query" ], "title": "ep_searchArguments", "type": "object" }, "name": "ep_search", "outputSchema": { "additionalProperties": true, "title": "ep_searchDictOutput", "type": "object" } }, { "description": "[Tier 2 — Project save] The only Territory Solution export. Materialize the working GeoJSON FeatureCollection (territory polygons with part_ids, point features, and route paths + numbered stops). Omit tal_ids / point_layers / route_layers for the full project. Async: returns task_id; follow its Tasks next_action/sleep_ms until completed, then fetch the result once. The result carries geojson_artifact {artifact_id, bytes}, zip (default true = gzip download), and download_url (GET with Bearer API key) — never an inline multi-MB body. Set zip=false for uncompressed GeoJSON. Prerequisites: ts_handle, completed build job_id, or map_session_id. Set include_points=false or include_routes=false to drop a family. Reopen with import_geojson, then get_map_visualization(ts_handle=...). Paint travels with the export: every feature and point_layers[] / route_layers[] entry carries style {color, opacity, size, shape, weight} exactly as shown, so reopen and Designer import need no restyling. An EasyTerritory Designer rolodex project is this download POSTed to Designer REST/Agent/Projects/FromTerritorySolution (new) or PUT to .../Projects/{projectId}/FromTerritorySolution (update). Territories, points, and routes import. There is no MCP push tool (ezt://guidance/workflows/push-to-designer).", "inputSchema": { "properties": { "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "include_points": { "default": true, "title": "Include Points", "type": "boolean" }, "include_routes": { "default": true, "title": "Include Routes", "type": "boolean" }, "job_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Job Id" }, "map_session_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Map Session Id" }, "point_layers": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Point Layers" }, "route_layers": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Route Layers" }, "tal_ids": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Tal Ids" }, "ts_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Ts Handle" }, "zip": { "default": true, "title": "Zip", "type": "boolean" } }, "title": "export_geojsonArguments", "type": "object" }, "name": "export_geojson", "outputSchema": { "additionalProperties": true, "title": "export_geojsonDictOutput", "type": "object" } }, { "description": "[Tier 1 — Delegation Extract] When: senior planner sends a regional subtree to a delegate for bounded editing. Prerequisites: master TS with hierarchical TAL; branch rollup or path identified. Next: store branch extract + branch_metadata; delegate opens MC on extract only. Scenarios: DL-001, DL-002.", "inputSchema": { "properties": { "branch_rollup_territory_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Branch Rollup Territory Id" }, "branch_territory_path": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Branch Territory Path" }, "delegate_ref": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Delegate Ref" }, "exclude_locked": { "default": true, "title": "Exclude Locked", "type": "boolean" }, "expected_content_hash": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Expected Content Hash" }, "expected_revision": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "default": null, "title": "Expected Revision" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "map_session_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Map Session Id" }, "tal_id": { "title": "Tal Id", "type": "string" }, "ts": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Ts" }, "ts_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Ts Handle" } }, "required": [ "tal_id" ], "title": "extract_tal_branchArguments", "type": "object" }, "name": "extract_tal_branch", "outputSchema": { "additionalProperties": true, "title": "extract_tal_branchDictOutput", "type": "object" } }, { "description": "[Tier 1 — Plain-language orchestrator] Use when the exact granular tool is unclear or when a greedy/file-holding client wants to complete an action in one step. Prefer granular tools directly when you know the step. Pass request (your goal) plus optional csv_file / accounts_handle / assignments_handle / map_session_id / ts_handle. For an uploaded CSV, pass csv_file as the ChatGPT/OpenAI file parameter; ezt parses/stages it server-side and can run ingest_accounts with map_session_id so points appear on the open map. For fixed-workload builds, args.target_hours / args.workload_target are normalized to auto_build build_mode.mode=fixed_workload_target. Modern form-capable clients (protocol >= 2026-07-28) may answer missing part_layer, tal_label, sizing, dwell, and optional balance in-band (aligned with auto_build); legacy hosts keep CLARIFICATION_REQUIRED / ask_user (HITL-025).", "inputSchema": { "properties": { "accounts_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Accounts Handle" }, "args": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Args" }, "assignments_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Assignments Handle" }, "csv_file": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "string" }, { "type": "null" } ], "default": null, "title": "Csv File" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "map_session_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Map Session Id" }, "request": { "title": "Request", "type": "string" }, "state_facts": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "State Facts" }, "ts_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Ts Handle" } }, "required": [ "request" ], "title": "eztArguments", "type": "object" }, "name": "ezt", "outputSchema": { "additionalProperties": true, "title": "eztDictOutput", "type": "object" } }, { "description": "[Tier 1 — Geocode Only] When: geocode addresses without full account ingest, or headless bulk geocode (GC-001). Prerequisites: none for headless; ts_handle + MC-first when human verifies on map. Prefer ingest_accounts for territory builds (it geocodes inline with accept_suboptimal_geocodes default true). Use this tool when the user must verify geocode quality first; set accept_suboptimal_geocodes=true to accept ZIP-centroid/suboptimal matches. Address columns: address/address_line1/street, city, state, postal (CRM headers auto-mapped). QUOTA: billable provider calls are capped per API key per calendar month. Cache hits cost nothing and are never counted, so never set use_cache=false or force_regeocode=true to work around a cap — that turns free hits into paid calls. Past the cap the tool returns PROVIDER_QUOTA_EXCEEDED carrying limit, used, remaining, and period_resets_at; an operator must raise the cap, so report those numbers instead of retrying or splitting the batch. Next: pass result.geocoded_rows to ingest_accounts or export point layer. Scenarios: GC-001..006.", "inputSchema": { "properties": { "accept_suboptimal_geocodes": { "default": false, "title": "Accept Suboptimal Geocodes", "type": "boolean" }, "country_hint": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Country Hint" }, "force_regeocode": { "default": false, "title": "Force Regeocode", "type": "boolean" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "map_session_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Map Session Id" }, "min_confidence": { "default": 0, "title": "Min Confidence", "type": "number" }, "region_hint": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Region Hint" }, "rows": { "items": { "additionalProperties": true, "type": "object" }, "title": "Rows", "type": "array" }, "use_cache": { "default": true, "title": "Use Cache", "type": "boolean" } }, "required": [ "rows" ], "title": "geocode_addressArguments", "type": "object" }, "name": "geocode_address", "outputSchema": { "additionalProperties": true, "title": "geocode_addressDictOutput", "type": "object" } }, { "description": "[Tier 1 — Startup Guide + Handle] CALL THIS FIRST. Read-only. When: session start, after blocked_by=guidance_required, or when the shared rules are unclear. Returns result.text (the short cross-tool brief, same text as server instructions) and result.guidance_handle (the rotating code every territory/map tool requires). Per-tool rules are on that tool's description. Workflow essays are inlined as guidance on discover_intent, workflow_advisor, and blocked_by. Exempt: get_guidance, submit_feedback, discover_intent, workflow_advisor, ep_* tools.", "inputSchema": { "properties": {}, "title": "get_guidanceArguments", "type": "object" }, "name": "get_guidance", "outputSchema": { "additionalProperties": true, "title": "get_guidanceDictOutput", "type": "object" } }, { "description": "[Tier 2 — MC Selection Poll] When: read the latest committed MC session selection — especially when Monica started selection from the legend finger icon and then tells the agent what to do with 'my selection' (no prior request_part_selection in this turn). Prerequisites: map_session_id of the open MC. Returns part_layer + part_ids (+ selection_task_id when a first-class task was created). Prefer get_part_selection when you already have selection_task_id and it is still available. Next for 'add these to <territory>': call realign with tal_id, the same map_session_id, and moves=[{part_id, to_territory_id}] derived from every returned part_id using the stable leaf territory_id (created_territory.territory_id / leaf catalog). Display-name aliases need an exact unique name match — do not invent shorthand (T3 ≠ Territory 3). If unclear, ask which leaf. Do not resend the TS or request a new selection. Scenarios: MC-004 variants, user-initiated select.", "inputSchema": { "properties": { "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "map_session_id": { "title": "Map Session Id", "type": "string" } }, "required": [ "map_session_id" ], "title": "get_map_selectionArguments", "type": "object" }, "name": "get_map_selection", "outputSchema": { "additionalProperties": true, "title": "get_map_selectionDictOutput", "type": "object" } }, { "description": "[Tier 1 — MC-First Entry] FIRST step of non-headless territory work (invariant I-1). When: open a blank map for planning, or open/reuse the user's Map Component for an existing Territory Solution (TS), ts_handle, or completed job. Pass new_project=true to replace the live workspace with an empty project; omitted empty+view reuses the existing project. Optional presentation.view_name: empty | points_only | review | selection (defaults from mode/TS content). Diagnostics: presentation.debug_panel=true. Prerequisites: none (idempotent per user_id). Next: hand user map_url, then ensure_map_viewer until viewer_status.connected. Dual surface (same map_session_id, one map): Apps-capable hosts render this map in chat from ui://easyterritory/map-viewer (_meta.ui.resourceUri); every other client — including Cursor — opens map_url in a browser tab. map_url is always returned. Do not open a second surface or call this tool again to 'fix' a map. Scenarios: MC-000, MC-001, MV-001, S004.", "inputSchema": { "properties": { "active_tal_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Active Tal Id" }, "expiry_seconds": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "default": null, "title": "Expiry Seconds" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "interaction_flags": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Interaction Flags" }, "job_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Job Id" }, "mode": { "default": "view", "title": "Mode", "type": "string" }, "new_project": { "default": false, "title": "New Project", "type": "boolean" }, "presentation": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Presentation" }, "ts": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Ts" }, "ts_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Ts Handle" }, "user_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "User Id" } }, "title": "get_map_visualizationArguments", "type": "object" }, "name": "get_map_visualization", "outputSchema": { "additionalProperties": true, "title": "get_map_visualizationDictOutput", "type": "object" } }, { "description": "[Tier 2 — Selection Poll] When: poll or retrieve committed part IDs after request_part_selection OR after Monica starts selection from the MC legend finger icon. Prerequisites: selection_task_id from request_part_selection or from map session state (active_selection_task_id on ezt://map-sessions/{id}/state). Poll loop: while status=awaiting_user_selection, sleep recommended_poll_delay_ms (fallback poll_interval_ms, min 250ms) and poll again until status=committed, expired, or cancelled. Response includes do_this_next and poll_loop while awaiting. If the user already committed and says 'add my selection to …', call once for the committed task (or use get_map_selection) and proceed — do not start a new selection. Next: realign, create_territory_from_parts, or analyze scoped to selection.part_ids. Scenarios: AN-007, RL-008.", "inputSchema": { "properties": { "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "selection_task_id": { "title": "Selection Task Id", "type": "string" } }, "required": [ "selection_task_id" ], "title": "get_part_selectionArguments", "type": "object" }, "name": "get_part_selection", "outputSchema": { "additionalProperties": true, "title": "get_part_selectionDictOutput", "type": "object" } }, { "description": "[Tier 2 — Project reopen] Import a GeoJSON FeatureCollection (standard points or an export_geojson artifact, including gzip/zip). Returns ts_handle + summary — pass ts_handle to get_map_visualization / analyze / export_geojson next; do not repost the GeoJSON body. Arbitrary territory polygons WITHOUT part_ids are rejected (TERRITORY_POLYGONS_WITHOUT_PART_IDS) unless you explicitly pass part_layer plus overlay_policy=\"centroid_within\" to assign parts whose centroids fall inside each polygon. Optional: point_layer (name for imported points), tal_label. Supply geojson (object), geojson_text (JSON or sniffed gzip/zip), or geojson_gzip (base64 gzip/zip). An EasyTerritory Designer rolodex project arrives here too: GET Designer REST/Agent/Projects (list) then GET .../Projects/{projectId}/TerritorySolution with the user's ezt_pat_ Bearer, and pass the (gzipped) file as geojson_gzip. There is no MCP pull tool (ezt://guidance/workflows/pull-from-designer).", "inputSchema": { "properties": { "geojson": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Geojson" }, "geojson_gzip": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Geojson Gzip" }, "geojson_text": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Geojson Text" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "overlay_policy": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Overlay Policy" }, "part_layer": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Part Layer" }, "point_layer": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Point Layer" }, "tal_label": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Tal Label" } }, "title": "import_geojsonArguments", "type": "object" }, "name": "import_geojson", "outputSchema": { "additionalProperties": true, "title": "import_geojsonDictOutput", "type": "object" } }, { "description": "[Tier 1 — Data Intake] When: load/add account/location rows or account CSV point data into a TS point layer. ALWAYS before account-derived builds (auto_build, account_build). Prerequisites: MC-first + viewer connected when human verifies points; ts/ts_handle optional. Geocodes rows lacking valid coordinates (accept_suboptimal_geocodes defaults true for bulk address-only CSVs). Recognized address columns: address/address_line1/street, city, state, postal columns, plus common CRM headers (e.g. Address 1: Street 1). Coordinate passthrough accepts case-insensitive GIS headers latitude/lat/LAT and longitude/lon/lng/long/LON (no rename required); pass explicit latitude_field/longitude_field for other headers (e.g. Address 1: Latitude). If unused numeric lat/lon-like columns exist and ingest would otherwise bulk-geocode, the job fails fast with CLARIFICATION_REQUIRED / blocked_by=needs_coordinate_fields — set latitude_field/longitude_field or force_regeocode=true; never wait on a national geocode crawl. After submit, if the first Tasks status shows coordinate_passthrough_count=0 with a large geocode_query_count on a file that had coord-like columns, call tasks/cancel or tasks_cancel and fix headers/fields. id_field must be a UNIQUE business key (default row_id). Never use label/name columns — duplicates fail with blocked_by=needs_unique_row_id. If no unique column exists, omit id_field (server synthesizes row-1, row-2, … when row_id is absent) or synthesize a unique row_id client-side before staging. Before this call, inspect headers/samples for metric, dwell-time, and visit-frequency columns. Pass confirmed business metrics as metric_fields and confirmed workload roles as workload_fields plus visit_frequency_field/dwell_time_field so downstream tools can reuse point-layer metadata. If no visit-frequency column exists, omit visit_frequency_field; do not ask for an average/default frequency. DECLARED FIELDS ARE THE RETENTION CONTRACT: the TS keeps only id, coordinates, label, and declared fields. Also declare grouping_fields (columns for account_build grouping), display_fields (columns shown in map callouts), and search_fields (columns searched in the MC) — undeclared columns are discarded at ingest and later operations on them fail with UNDECLARED_FIELD. NEVER pass a client file path in rows or accounts_handle — the server cannot read /mnt/data/... or other sandbox paths. LARGE FILES: parse the CSV client-side, then call request_account_upload(rows=<chunk> OR csv_text=<raw CSV>) (append via upload_handle), then call this tool with accounts_handle instead of rows. TO SHOW THE POINTS ON AN OPEN MAP: pass map_session_id (the open MC's session id from get_map_visualization / the map_url) — the server pushes the new point layer to that map on completion. WITHOUT map_session_id the points land in a DETACHED TS: read the job result (result_resource_uri) for result.ts_handle and result.map_binding, then bind via show_map_overlay / configure_map / get_map_visualization with that ts_handle. ASYNC COMPLETION IS MANDATORY: the submission/geocoding phase is not workflow completion. Follow the returned Tasks do_this_next and sleep_ms until status=completed, then call the indicated result operation once and inspect the terminal one-liner field, viewer_outcome, failed_rows, map_binding, and map_refresh. viewer_outcome.status painted (or sent_unconfirmed with notified=true) means the linked MC got the push; queued_no_viewer means map_refresh.status=viewer_disconnected (or no viewer yet) — follow recovery.args (ensure_map_viewer); needs_show_map_overlay / rebind_required / detached include literal recovery.args. Do not stop until the binding matches the requested map_session_id, map_refresh.notified=true, and the point layer is visible in the already-open MC (or the detached ts_handle is bound). A linked points-only ingest lands on the surrogate __points__ TAL — that is success (map_refresh.status is sent_unconfirmed or applied); do NOT rebind via get_map_visualization(job_id=...) when notified=true and active_tal_id=__points__. MULTI-OVERLAY COMPOSE: linked ingest is non-destructive for prior part-layer overlays — previously configured loaded_part_layers remain on the session with the new points. Verify both the point layer AND any expected part overlays are still present before claiming compose success; points-only adoption does not mean those overlays disappeared. GEOCODE QUOTA: billable provider calls are capped per API key per calendar month; cached addresses and coordinate passthrough are free and never counted. When the cap runs out mid-file the ingest is NOT refused — rows covered by the remaining budget are geocoded and ingested, and the rest arrive in failed_rows with reason_code=PROVIDER_QUOTA_EXCEEDED plus a result quota block naming limit, used, remaining, unserved_row_count, and period_resets_at. Report that instead of re-running the ingest; the blocked rows need a raised cap, not a retry. Next: configure_map if needed, then choose the build tool. Full atom: ezt://guidance/workflows/geocode-and-ingest. Scenarios: IA-001..005, GC-002, GC-003, S003.", "inputSchema": { "properties": { "accept_suboptimal_geocodes": { "default": true, "title": "Accept Suboptimal Geocodes", "type": "boolean" }, "accounts_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Accounts Handle" }, "country_hint": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Country Hint" }, "display_fields": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Display Fields" }, "dwell_time_field": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Dwell Time Field" }, "force_regeocode": { "default": false, "title": "Force Regeocode", "type": "boolean" }, "grouping_fields": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Grouping Fields" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "id_field": { "default": "row_id", "title": "Id Field", "type": "string" }, "label": { "default": "Accounts", "title": "Label", "type": "string" }, "label_field": { "default": "label", "title": "Label Field", "type": "string" }, "latitude_field": { "default": "latitude", "title": "Latitude Field", "type": "string" }, "longitude_field": { "default": "longitude", "title": "Longitude Field", "type": "string" }, "map_session_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Map Session Id" }, "metric_fields": { "anyOf": [ { "items": { "additionalProperties": true, "type": "object" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Metric Fields" }, "min_confidence": { "default": 0, "title": "Min Confidence", "type": "number" }, "point_layer": { "default": "accounts", "title": "Point Layer", "type": "string" }, "region_hint": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Region Hint" }, "rows": { "anyOf": [ { "items": { "additionalProperties": true, "type": "object" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Rows" }, "search_fields": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Search Fields" }, "ts": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Ts" }, "ts_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Ts Handle" }, "use_cache": { "default": true, "title": "Use Cache", "type": "boolean" }, "visit_frequency_field": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Visit Frequency Field" }, "workload_fields": { "anyOf": [ { "items": { "additionalProperties": true, "type": "object" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Workload Fields" } }, "title": "ingest_accountsArguments", "type": "object" }, "name": "ingest_accounts", "outputSchema": { "additionalProperties": true, "title": "ingest_accountsDictOutput", "type": "object" } }, { "description": "[Tier 1 — Isochrone Build] When: draw the area reachable by DRIVING from one or more origins within a time or distance budget — '20-minute drive-time area around each branch', service area, catchment, coverage ring, reachable range, isochrone, isodistance (IS-001..IS-006). It draws reachable area. It has no objective and does not balance workload. Prerequisites: a TomTom or Azure Maps key on the server (no key → ISOCHRONE_NOT_CONFIGURED, no degraded mode — never substitute a straight-line radius); a part_layer (read ezt://part-layers); and, for point-layer origins, ingested points plus ts_handle or map_session_id. Inline origins need no TS. ORIGINS x BANDS: origins are inline {latitude, longitude, label?} or a point-layer reference {point_layer, point_ids?, filter?, label_field?} — '40 minutes around each of my 6 branches' is ONE call. bands are [{time_budget_seconds} | {distance_budget_meters}], one budget per band, and every band in a call must use the SAME unit (mixing returns MIXED_BUDGET_TYPES). One band → one leaf per origin; several bands → a rollup per origin with one leaf per band. Cost is origins x bands provider calls, capped by TOO_MANY_ORIGINS (default 25) / TOO_MANY_BANDS (default 5). QUOTA: those billable calls also draw on a per-API-key monthly cap. The whole fan-out is claimed before any call goes out, so a build that does not fit returns PROVIDER_QUOTA_EXCEEDED with limit, used, remaining, and period_resets_at having spent nothing. An operator must raise the cap; report those numbers instead of retrying with fewer bands. LIMITS: travel_mode is 'car' or 'truck' ONLY — neither provider offers a walking or cycling reachable range, unlike calculate_route. time_budget_seconds <= 21600, distance_budget_meters <= 500000 (BUDGET_OUT_OF_RANGE). depart_at is supported; arrive_at is not. avoid accepts toll_roads, motorways, ferries, unpaved_roads, carpools, border_crossings, tunnels, car_trains, low_emission_zones. GEOMETRY AND PARTS: each territory stores the provider's exact polygon AND part_ids assigned by centroid_within overlay, so analyze and territory stats work normally while the map shows the true drive-time edge. Bands nest and their polygons overlap on purpose (tightest paints on top); part assignment is disjoint — a part joins the tightest band containing it, then the nearer origin. realign can move parts between bands but NEVER moves a polygon, so a realigned isochrone's assignment intentionally diverges from its drawn shape. A band budget is NOT territory workload and NOT route drive time — never report '20-minute band' as 20 minutes of workload or feed it to auto_build. Async: returns task_id — follow _meta.next_action (sleep_and_poll → consume_result) and sleep the authoritative sleep_ms, never estimated_remaining_ms. Partial provider failure still builds the TAL and reports failed_requests[]. Then: analyze(tal_ids=[<tal_id>], map_session_id, analysis_panel='single') for per-band accounts and metrics. Pass map_session_id here to paint the bands on an open map. Full atom: ezt://guidance/workflows/isochrone-territories. Scenarios: IS-001..IS-006.", "inputSchema": { "properties": { "avoid": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Avoid" }, "bands": { "items": { "additionalProperties": true, "type": "object" }, "title": "Bands", "type": "array" }, "depart_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Depart At" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "map_session_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Map Session Id" }, "origins": { "items": { "additionalProperties": true, "type": "object" }, "title": "Origins", "type": "array" }, "part_layer": { "title": "Part Layer", "type": "string" }, "part_overlay": { "default": "centroid_within", "title": "Part Overlay", "type": "string" }, "route_type": { "default": "fastest", "title": "Route Type", "type": "string" }, "tal_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Tal Id" }, "tal_label": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Tal Label" }, "traffic": { "default": true, "title": "Traffic", "type": "boolean" }, "travel_mode": { "default": "car", "title": "Travel Mode", "type": "string" }, "ts_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Ts Handle" } }, "required": [ "origins", "bands", "part_layer" ], "title": "isochrone_buildArguments", "type": "object" }, "name": "isochrone_build", "outputSchema": { "additionalProperties": true, "title": "isochrone_buildDictOutput", "type": "object" } }, { "description": "[Tier 2 — Analysis Panel Display] When: show Analyze JSON in the MC bottom dock (single or comparison) after a completed analyze that omitted analysis_panel. Prefer analyze(analysis_panel=single, map_session_id=...) so the dock fills without this second call. Prerequisites: map_session_id; a full Analyze result — pass task_id/job_id of the completed analyze job, or analyses=[<full Analyze result>]. The result must include tal_analyses and ts_identity (territory_metric_grids when metrics exist). A thin {tal_analyses} object is INVALID_REQUEST, not an empty dock. presentation_mode is single|compare|executive|diagnostic — territory_metric_grid is default_presentation.view inside the Analyze payload, not a presentation_mode. Does not mutate TS. Re-run analyze after any TAL or point change (I-2). dismiss=true hides the whole dock, header bar included, and keeps the stats on the session. analysis_panel.dismissed is then true. A later analyze with analysis_panel=single shows the dock again. Omit analyses on a dismiss call. Dual surface: the dock renders in the in-chat map on Apps-capable hosts and in the map_url tab everywhere else — same map_session_id, no extra call. Scenarios: MC-009, AN-006, MC-007.", "inputSchema": { "properties": { "analyses": { "anyOf": [ { "items": { "additionalProperties": true, "type": "object" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Analyses" }, "dismiss": { "default": false, "title": "Dismiss", "type": "boolean" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "job_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Job Id" }, "labels": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Labels" }, "map_session_id": { "title": "Map Session Id", "type": "string" }, "presentation_mode": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Presentation Mode" }, "roles": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Roles" }, "task_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Task Id" } }, "required": [ "map_session_id" ], "title": "load_analysis_panelArguments", "type": "object" }, "name": "load_analysis_panel", "outputSchema": { "additionalProperties": true, "title": "load_analysis_panelDictOutput", "type": "object" } }, { "description": "[Tier 2 — Part Metadata] When: enrich or filter parts before build, or inspect attributes without geometry. Prerequisites: part_layer from ezt://part-layers. Next: direct_build, configure_map, or agent-side join before build. Scenarios: DS-002. Returns part_id and attributes only; no geometry.", "inputSchema": { "properties": { "filter": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Filter" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "max_results": { "default": 100, "title": "Max Results", "type": "integer" }, "page_token": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Page Token" }, "part_ids": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Part Ids" }, "part_layer": { "title": "Part Layer", "type": "string" } }, "required": [ "part_layer" ], "title": "query_partsArguments", "type": "object" }, "name": "query_parts", "outputSchema": { "additionalProperties": true, "title": "query_partsDictOutput", "type": "object" } }, { "description": "[Tier 1 — Realign] When: move parts between leaf territories within ONE TAL. Prerequisites: existing TAL plus ONE current TS reference: map_session_id (preferred for an open MC), ts_handle, or ts; expected_revision is optional optimistic concurrency (STALE_TS_REVISION → reload and retry). For a committed ad-hoc map selection, call get_map_selection(map_session_id), then pass moves=[{part_id, to_territory_id}] for each returned selection.part_ids plus tal_id and the same map_session_id. Do not repost the full TS and do not start another selection. to_territory_id must be a leaf territory_id (prefer created_territory.territory_id / leaf_territories / legend catalog). A display-name alias is accepted only as an exact unique match to the leaf's actual name (e.g. 'Territory 3' or 'T1' when that is the name) — do NOT invent shorthand expansions (T3 ≠ Territory 3; no T→Territory mapping). It is NOT tal_id. On UNKNOWN_TERRITORY_ID / AMBIGUOUS_TERRITORY read error.details.available_leaf_territories and stop guessing; if the spoken destination is still unclear, ask the user which leaf (id or exact name). Visual moves with a destination known in advance: request_part_selection with purpose=realign. Structural rebalance: territory_split/merge/rebalance. Next: verify the live map refresh. Run analyze + load_analysis_panel only when the TS has a point layer (I-2). For a geography-only ZIP/part edit, skip analyze unless the user requested AN-004; do not create a NO_POINT_LAYER failure after a successful edit. Full atom: ezt://guidance/workflows/realign-by-selection. Scenarios: RL-001..013, S001, MC-004, EV-001.", "inputSchema": { "properties": { "expected_revision": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "default": null, "title": "Expected Revision" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "map_session_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Map Session Id" }, "moves": { "anyOf": [ { "items": { "additionalProperties": true, "type": "object" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Moves" }, "part_ids": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Part Ids" }, "part_layer": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Part Layer" }, "realign_operation": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Realign Operation" }, "remove_empty_territories": { "default": false, "title": "Remove Empty Territories", "type": "boolean" }, "repair_policy": { "default": "default", "title": "Repair Policy", "type": "string" }, "tal_id": { "title": "Tal Id", "type": "string" }, "ts": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Ts" }, "ts_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Ts Handle" } }, "required": [ "tal_id" ], "title": "realignArguments", "type": "object" }, "name": "realign", "outputSchema": { "additionalProperties": true, "title": "realignDictOutput", "type": "object" } }, { "description": "[Tier 1 — Delegation Reintegrate] When: merge an approved proposal TS into master. Prerequisites: branch_metadata, expected_master_revision; proposal TS at current lineage. STALE_TS_REVISION if master moved—request fresh extract. Sequential merges only. Next: refresh master Analysis panel (I-2). Scenarios: DL-003, DL-004, DL-006.", "inputSchema": { "properties": { "branch_metadata": { "additionalProperties": true, "title": "Branch Metadata", "type": "object" }, "expected_master_content_hash": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Expected Master Content Hash" }, "expected_master_revision": { "title": "Expected Master Revision", "type": "integer" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "map_session_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Map Session Id" }, "master_ts": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Master Ts" }, "master_ts_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Master Ts Handle" }, "proposal_ts": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Proposal Ts" }, "proposal_ts_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Proposal Ts Handle" } }, "required": [ "expected_master_revision", "branch_metadata" ], "title": "reintegrate_branchArguments", "type": "object" }, "name": "reintegrate_branch", "outputSchema": { "additionalProperties": true, "title": "reintegrate_branchDictOutput", "type": "object" } }, { "description": "[Tier 2 — Data Intake helper] When: you have account/location rows or an account CSV that you cannot inline in a single ingest_accounts call (e.g. a large CSV, hundreds/thousands of rows). Stage the rows here in one or more chunks, then call ingest_accounts(accounts_handle=...). If the file includes metric, workload, visit-frequency, or dwell-time fields, stage the file here first, inspect returned csv_headers / row keys, confirm role candidates, then pass metric_fields/workload_fields/visit_frequency_field/dwell_time_field to ingest_accounts. Continue only after ingest_accounts has loaded the points. FOUR WAYS TO STAGE (all return an upload_handle): (A) MCP csv_file: pass the uploaded CSV as a ChatGPT/OpenAI file parameter when available. (B) MCP csv_text: pass csv_text=<raw CSV string> when file params are unavailable. Preserve multiline newlines — do NOT JSON-encode with PowerShell ConvertTo-Json (that corrupts rows into a single line). Use Python json.dumps or MCP csv_file instead. (C) MCP rows: call with rows=<chunk>; append more with upload_handle=<prior handle>. (D) HTTP: call with no rows/csv_text to get result.upload_url, then POST rows or csv_text/csv_file. Then: ingest_accounts(accounts_handle=<upload_handle>, map_session_id=<MC session id>, label_field=<label field>). GIS coordinate headers latitude/lat/LAT and longitude/lon/lng/long/LON passthrough case-insensitively. CRM headers such as Address 1: Latitude and Address 1: Longitude are coordinates too — the sheet stays accounts when Region and Territory are also present. suggested_ingest.args sets latitude_field and longitude_field for those headers; pass them through and do not rename them. If first Tasks status shows coordinate_passthrough_count=0 with a large geocode_query_count on a file that had coord-like columns, call tasks/cancel or tasks_cancel and fix — never wait on national geocode. id_field only when a unique business key exists — never label/name; omit otherwise (server synthesizes row-N). On success the result includes suggested_ingest ({tool, args, confidence, notes, geocode_plan}) — prefer those args for the next ingest_accounts call (fill map_session_id from the open MC). geocode_plan states coordinate_fields_detected, will_geocode, eta_class, and passthrough_likely before you submit. Handle is single-use (claimed by the ingest job) and expires (default 1h). Scenarios: IA-001..005, S003.", "inputSchema": { "properties": { "csv_file": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "string" }, { "type": "null" } ], "default": null, "title": "Csv File" }, "csv_text": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Csv Text" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "rows": { "anyOf": [ { "items": { "additionalProperties": true, "type": "object" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Rows" }, "ttl_seconds": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "default": null, "title": "Ttl Seconds" }, "upload_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Upload Handle" } }, "title": "request_account_uploadArguments", "type": "object" }, "name": "request_account_upload", "outputSchema": { "additionalProperties": true, "title": "request_account_uploadDictOutput", "type": "object" } }, { "description": "[Tier 2 — Direct Build intake] When: you have part-to-territory assignment rows or a legacy spreadsheet (e.g. Zip2Terr.csv with Postal Code + Territory/Region/Division) that you cannot inline in direct_build. Stage rows here, then call direct_build(assignments_handle=<upload_handle>). FOUR WAYS TO STAGE (all return upload_handle): (A) MCP csv_file: pass the uploaded CSV as a ChatGPT/OpenAI file parameter. (B) MCP csv_text: pass csv_text=<raw multiline CSV> — preserve newlines; do NOT JSON-encode with PowerShell ConvertTo-Json (corrupts rows). (C) MCP assignments: pass assignments=<chunk of row objects>; append with upload_handle. (D) HTTP POST /assignments/upload with assignments, csv_text, or csv_file. Legacy columns (Postal Code, Territory, Region, Division) auto-map to part_id + territory_path. Then: direct_build(assignments_handle=<handle>, part_layer=..., tal_label=..., map_session_id=...). Handle is single-use and expires (default 1h).", "inputSchema": { "properties": { "assignments": { "anyOf": [ { "items": { "additionalProperties": true, "type": "object" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Assignments" }, "csv_file": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "string" }, { "type": "null" } ], "default": null, "title": "Csv File" }, "csv_text": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Csv Text" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "ttl_seconds": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "default": null, "title": "Ttl Seconds" }, "upload_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Upload Handle" } }, "title": "request_assignment_uploadArguments", "type": "object" }, "name": "request_assignment_upload", "outputSchema": { "additionalProperties": true, "title": "request_assignment_uploadDictOutput", "type": "object" } }, { "description": "[Tier 2 — Human Spatial Input] When: Monica selects parts on the map for realign, manual territory build, or return_list. Prerequisites: MC-first + viewer connected; part_layer; active TAL when realigning. Poll loop: after submit, poll get_part_selection(selection_task_id) using recommended_poll_delay_ms from the response (or scripts/wait_part_selection.py) until status=committed — never ask the human to type committed/done/go. User-initiated path: Monica may also start select mode from the MC legend finger icon on a part-layer row (no prior request_part_selection). When the user refers to 'my selection' / 'the parts I selected', read the open map session's latest committed selection via get_map_selection(map_session_id) or get_part_selection using active_selection_task_id from ezt://map-sessions/{id}/state — do not ask them to select again. Next: realign, create_territory_from_parts, or analyze with selection.part_ids. Dual surface: in an Apps-capable host the human selects on the in-chat map (ui://easyterritory/map-viewer); otherwise they select in the map_url tab. The commit poll loop above is identical on both surfaces. Scenarios: S001, MC-004..006, RL-006..013.", "inputSchema": { "properties": { "active_tal_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Active Tal Id" }, "destination_territory_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Destination Territory Id" }, "expiry_seconds": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "default": null, "title": "Expiry Seconds" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "new_territory": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "New Territory" }, "part_layer": { "title": "Part Layer", "type": "string" }, "prompt": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Prompt" }, "purpose": { "default": "generic", "title": "Purpose", "type": "string" }, "realign_operation": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Realign Operation" }, "remove_empty_territories": { "default": false, "title": "Remove Empty Territories", "type": "boolean" }, "ts": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Ts" }, "user_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "User Id" } }, "required": [ "part_layer" ], "title": "request_part_selectionArguments", "type": "object" }, "name": "request_part_selection", "outputSchema": { "additionalProperties": true, "title": "request_part_selectionDictOutput", "type": "object" } }, { "description": "[Tier 1 — Periodic Scheduling] When: accounts carry a recurring cadence (every 7 days, twice a month) and the user asks which day each visit happens. Expands demand across a repeating horizon and packs it into daily work clusters (buckets) under one technician-day workload cap. This tool decides the day. Neighbors: auto_build, cluster_points, calculate_route. When the user said route and a visit-frequency column exists, confirm they want a schedule, not calculate_route, before calling. It creates no TAL and assigns no technician. Prerequisite: ingest_accounts completed for point_layer with the cadence column declared. Omit ts and ts_handle when the session id argument is already set. That session is the TS. An undeclared column fails with UNDECLARED_FIELD. Required: point_layer, visit_frequency_field, dwell_time, daily_capacity. Ask the user for dwell and the daily cap; never invent them. frequency_unit=interval_days means the value is the maximum days between visits (so a 3-day cadence in a 14-day horizon is 5 visits, not 4); visits_per_horizon means the count across the horizon. Values in never_visit_values (default 0, null, empty) are excluded and reported in excluded_accounts, never silently dropped. Example — weekly and biweekly accounts across 8-hour technician days: point_layer=accounts, visit_frequency_field=service_interval_days, dwell_time={type: scalar, value: 45, unit: minutes}, daily_capacity={mode: not_to_exceed, hours: 8}, max_buckets_per_day=3. bucket_workload_hours is in-bucket drive plus dwell with NO visit-frequency multiplier: it is neither territory workload nor route_workload_hours — never sum or compare them. Re-running with the same visit_layer_name replaces that schedule in place; a different name adds a second one for comparison. After submission, follow do_this_next with the returned task_id: sleep exactly sleep_ms while next_action=sleep_and_poll, fetch the result once on consume_result, and stop on stop_error. Next: calculate_route over the visits layer filtered to one bucket_id to drive that day. Full atom: ezt://guidance/workflows/periodic-visit-schedule. Scenarios: PS-001..PS-008.", "inputSchema": { "properties": { "cadence_flexibility_pct": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "default": null, "title": "Cadence Flexibility Pct" }, "daily_capacity": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Daily Capacity" }, "dwell_time": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Dwell Time" }, "emit_visit_layer": { "default": true, "title": "Emit Visit Layer", "type": "boolean" }, "expected_revision": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "default": null, "title": "Expected Revision" }, "frequency_unit": { "default": "interval_days", "title": "Frequency Unit", "type": "string" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "horizon": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Horizon" }, "map_session_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Map Session Id" }, "max_buckets_per_day": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "default": null, "title": "Max Buckets Per Day" }, "never_visit_values": { "anyOf": [ { "items": {}, "type": "array" }, { "type": "null" } ], "default": null, "title": "Never Visit Values" }, "objective": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Objective" }, "point_layer": { "title": "Point Layer", "type": "string" }, "schedule_label": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Schedule Label" }, "ts": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Ts" }, "ts_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Ts Handle" }, "visit_frequency_field": { "title": "Visit Frequency Field", "type": "string" }, "visit_layer_name": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Visit Layer Name" } }, "required": [ "point_layer", "visit_frequency_field" ], "title": "schedule_visitsArguments", "type": "object" }, "name": "schedule_visits", "outputSchema": { "additionalProperties": true, "title": "schedule_visitsDictOutput", "type": "object" } }, { "description": "[Tier 1 — Seed Build] When: grow ONE territory outward from a seed location until it holds a target number of locations or a target metric sum — 'a franchise territory around this address with 40 stores', 'grow from this point until it reaches $2M revenue', 'the ZIPs around our new branch that cover 300 stores'. The result is an ordinary part-based territory (ZIPs, counties) appended as a new leaf on an existing layer (tal_id) or as a new layer when tal_id is omitted. It grows one territory from one seed. It does not partition, balance, or route. Prerequisites: an ingested point layer (ingest_accounts) — its locations are the values counted or summed; a part_layer (ezt://part-layers); and the seed as {longitude, latitude}. Resolve an address or POI to coordinates first with the address geocoding tool; a map click already gives coordinates. TARGET: target={type: 'location_count', value: N} or {type: 'metric_sum', field: <column declared in metric_fields at ingest>, value: X}. An undeclared field returns UNDECLARED_FIELD — re-ingest with metric_fields. Fit is CLOSEST: growth adds the nearest adjacent part that still fits, then takes the smallest remaining neighbour only when overshooting lands nearer the target than stopping short. target_status reports reached | closest_under | closest_over | frontier_exhausted | max_parts_reached. NO OVERLAP, EVER: with tal_id, parts already in any territory of that layer are never taken — the new territory drifts away from them instead (blocked_part_count, seed_offset_km). A seed inside an existing territory fails SEED_PART_ASSIGNED; pick another seed or reassign parts with realign. There is no allow_overlap flag. SCOPE: growth reads only the seed's neighbourhood — the point layer is indexed once and parts are materialised ring by ring outward from the seed part; it never joins every location to every part, so a national point layer costs the same as a local one. part_filter / part_ids are an ALLOWLIST (parts outside it do not exist for the walk), not a performance prerequisite: pass part_scope='explicit' + part_filter={state_abbr: 'TX'} only when the user wants the territory confined to that state. bbox_intersect / point_matched are accepted and ignored here; the result reports the materialised part count and ring depth the walk touched. ONE SEED PER CALL: for several franchisees call seed_build once per seed with the same tal_id; earlier territories become blocked for later seeds. Async: returns task_id — follow _meta.next_action (sleep_and_poll → consume_result) and sleep the authoritative sleep_ms. The result is handle-only (ts_handle, no inline ts) with created_territory, the grown part ids, target.{requested, achieved, delta}, and do_this_next. Then: analyze(tal_ids=[<tal_id>], map_session_id, analysis_panel='single') so the map dock shows the territory; without dwell Analyze omits workload and returns workload_omitted.ask_user to relay after the stats. Pass map_session_id here to paint the territory on the open map. Full atom: ezt://guidance/workflows/seed-grown-territory. Scenarios: SB-001..SB-004.", "inputSchema": { "properties": { "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "map_session_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Map Session Id" }, "max_parts": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "default": null, "title": "Max Parts" }, "part_filter": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Part Filter" }, "part_ids": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Part Ids" }, "part_layer": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Part Layer" }, "part_scope": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Part Scope" }, "point_layer": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Point Layer" }, "seed": { "additionalProperties": true, "title": "Seed", "type": "object" }, "tal_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Tal Id" }, "tal_label": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Tal Label" }, "target": { "additionalProperties": true, "title": "Target", "type": "object" }, "territory_name": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Territory Name" }, "ts_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Ts Handle" }, "user_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "User Id" } }, "required": [ "seed", "target" ], "title": "seed_buildArguments", "type": "object" }, "name": "seed_build", "outputSchema": { "additionalProperties": true, "title": "seed_buildDictOutput", "type": "object" } }, { "description": "[Tier 2 — Low-Level MC State] When: switch MC mode, active TAL, or pending job ref, or jump the open MC camera with center ([longitude, latitude]) and/or zoom (0-24). Camera here is session-only and does not write the TS — use configure_map to persist a default view. Prerequisites: map_session_id. Prefer configure_map for durable TS map_config. Prefer request_part_selection for selection workflows. Scenarios: residual backlog (no dedicated scenario by design).", "inputSchema": { "properties": { "active_tal_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Active Tal Id" }, "center": { "anyOf": [ { "items": { "type": "number" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Center" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "map_session_id": { "title": "Map Session Id", "type": "string" }, "mode": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Mode" }, "pending_job_reference": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Pending Job Reference" }, "zoom": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "default": null, "title": "Zoom" } }, "required": [ "map_session_id" ], "title": "set_map_stateArguments", "type": "object" }, "name": "set_map_state", "outputSchema": { "additionalProperties": true, "title": "set_map_stateDictOutput", "type": "object" } }, { "description": "[Tier 1 — Map Overlay] When: customer wants something on the map — US ZIP codes, counties, accounts/points, or a territory alignment — in any phrasing ('add US zip codes to the map', 'show zip codes', 'add zips'). Prerequisites: ts_handle (or inline ts) from get_map_visualization; viewer connected for live MC refresh. Pass user_request alone (e.g. 'add US zip codes to the map') or overlay_kind (part_layer | point_layer | tal | route) with optional overlay_id. Resolves the four MC overlay families and calls the correct underlying step (configure_map for part layers and active TAL; point layers must already be in the TS from ingest_accounts). Source the TS via ts_handle, inline ts, OR map_session_id (preferred for points: reads the LIVE session TS so points pushed by ingest_accounts(map_session_id=...) are found without threading a new ts_handle). Returns overlay_kind, overlay_id, viewer_hint, and for part layers a visibility block (state, min_zoom, camera_action). An open map_session_id zooms the MC to min_zoom (camera_action=fit_to_visible). When the user already named a center and zoom, do_this_next.tool is configure_map: call it once with that center and zoom before ingest or a point classification, so the fit does not replace it. Skip that call when no center was named, and do not invent one. Do not call configure_map again unless a later result also reports camera_action=fit_to_visible. Do not use ezt_test/focusAt. POINT LAYERS with map_session_id: status=already_on_map is verified against the live session render payload; when the layer is in the TS but missing from the session, the tool pushes a refresh and returns status=refreshed with a map_refresh block — check its render_ack before claiming points are visible. ROUTE overlay (overlay_kind=route) needs map_session_id and only re-shows or hides routes calculate_route already drew — 'hide/remove/clear the route' clears them; with no route yet it returns blocked_by=needs_route pointing at calculate_route. Pass route_id to target ONE of several routes; omit it to act on all of them. To delete a route for good use delete_route; to change a route's stops re-run calculate_route with the same route_id. calculate_route already draws its own result, so this is recovery, not the normal path. Prefer this over raw configure_map for show/add-on-map asks. Part-layer overlays are written onto the live session even when map_push_status=viewer_not_connected (session durability); SSE paint still needs a connected viewer. A later linked ingest_accounts keeps those overlays (non-destructive compose with points). Dual surface: the overlay lands on the in-chat map on Apps-capable hosts and in the map_url tab everywhere else — same map_session_id, no extra call. Scenarios: MC-010, ChatGPT/external MCP.", "inputSchema": { "properties": { "expected_content_hash": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Expected Content Hash" }, "expected_revision": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "default": null, "title": "Expected Revision" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "map_session_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Map Session Id" }, "merge_strategy": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Merge Strategy" }, "overlay_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Overlay Id" }, "overlay_kind": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Overlay Kind" }, "route_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Route Id" }, "ts": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Ts" }, "ts_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Ts Handle" }, "user_request": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "User Request" } }, "title": "show_map_overlayArguments", "type": "object" }, "name": "show_map_overlay", "outputSchema": { "additionalProperties": true, "title": "show_map_overlayDictOutput", "type": "object" } }, { "description": "[Tier 2 — Quality Feedback] When: workflow blocked, partial, workaround required, confusing tool response, or missing capability/docs. Prerequisites: none. Next: continue or end session after acknowledgement. Scenarios: GC-006. No secrets, credentials, raw customer data, or PII. Do not paste Territory Solution JSON, GeoJSON FeatureCollections, or account/CSV tables — those blocks are stripped.", "inputSchema": { "$defs": { "FeedbackCategoryParam": { "enum": [ "product_gap", "mcp_tool_gap", "documentation_gap", "data_issue", "workflow_confusion", "bug", "suggestion", "other" ], "type": "string" }, "FeedbackConfidenceParam": { "enum": [ "low", "medium", "high" ], "type": "string" }, "FeedbackProductAreaParam": { "enum": [ "territory_planning", "alignment", "mapping", "routing", "powerbi", "d365", "data_import", "geocoding", "map_component", "mcp", "other" ], "type": "string" }, "FeedbackSeverityParam": { "enum": [ "low", "medium", "high", "blocking" ], "type": "string" } }, "properties": { "category": { "$ref": "#/$defs/FeedbackCategoryParam", "default": "other" }, "client_name": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Client Name" }, "client_version": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Client Version" }, "confidence": { "$ref": "#/$defs/FeedbackConfidenceParam", "default": "medium" }, "context_summary": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Context Summary" }, "correlation_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Correlation Id" }, "expected_capability": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Expected Capability" }, "include_context": { "default": false, "title": "Include Context", "type": "boolean" }, "related_product_area": { "anyOf": [ { "$ref": "#/$defs/FeedbackProductAreaParam" }, { "type": "null" } ], "default": null }, "related_tool": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Related Tool" }, "session_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Session Id" }, "severity": { "$ref": "#/$defs/FeedbackSeverityParam", "default": "medium" }, "suggested_improvement": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Suggested Improvement" }, "user_goal": { "title": "User Goal", "type": "string" }, "what_happened": { "title": "What Happened", "type": "string" } }, "required": [ "user_goal", "what_happened" ], "title": "submit_feedbackArguments", "type": "object" }, "name": "submit_feedback", "outputSchema": { "additionalProperties": true, "title": "submit_feedbackDictOutput", "type": "object" } }, { "description": "[Tier 2 — MCP Tasks mirror] Tool-only client equivalent of native tasks/cancel. Use only when this mirror appears in tools/list; Tasks-capable clients use tasks/cancel instead. Prerequisite: task_id from the immediately preceding async submission. Returns the same Task status fields and EZT poll metadata as the native method. Cancellation is cooperative; confirm TS unchanged or partial per the originating tool contract. Scenarios: OP-001.", "inputSchema": { "properties": { "task_id": { "title": "Task Id", "type": "string" } }, "required": [ "task_id" ], "title": "tasks_cancelArguments", "type": "object" }, "name": "tasks_cancel", "outputSchema": { "additionalProperties": true, "title": "tasks_cancelDictOutput", "type": "object" } }, { "description": "[Tier 2 — MCP Tasks mirror] Tool-only client equivalent of native tasks/get. Use only when this mirror appears in tools/list; Tasks-capable clients use tasks/get. Prerequisite: task_id from the immediately preceding async submission. Polling never starts work. next_action and sleep_ms sit on this document (the same fields as the submit result) and are repeated under _meta. Follow next_action exactly: sleep_and_poll means sleep the single authoritative sleep_ms value, call tasks_get once, then re-read both fields; consume_result means call tasks_result once now; stop_error means stop. status=cancelled is a user stop — do not resubmit the same tool unless asked. Never sleep on estimated_remaining_ms and never poll HTTP /result for status. While a task is still queued, _meta also carries queue_position (1-based within its worker lane), queue_depth, queue_lane (io|compute), and estimated_start_ms — report these to explain a wait; estimated_start_ms is advisory and never a sleep duration. The result is the same Task status document returned by native tasks/get. Scenarios: OP-002, tool-only MCP clients.", "inputSchema": { "properties": { "task_id": { "title": "Task Id", "type": "string" } }, "required": [ "task_id" ], "title": "tasks_getArguments", "type": "object" }, "name": "tasks_get", "outputSchema": { "additionalProperties": true, "title": "tasks_getDictOutput", "type": "object" } }, { "description": "[Tier 2 — MCP Tasks mirror] Tool-only client equivalent of native tasks/result. Call exactly once after tasks_get returns status=completed and _meta.next_action=consume_result. Prerequisite: task_id from that completed Task. Returns the originating tool's terminal handle-only result (ts_handle, counts, timings, map binding/shortcut); it never returns inline TS when a handle exists. Do not poll this operation while working. After build results, pass ts_handle or the same task_id as job_id to analyze with explicit tal_ids; never resubmit a build to recover from an Analyze lookup error.", "inputSchema": { "properties": { "task_id": { "title": "Task Id", "type": "string" } }, "required": [ "task_id" ], "title": "tasks_resultArguments", "type": "object" }, "name": "tasks_result", "outputSchema": { "additionalProperties": true, "title": "tasks_resultDictOutput", "type": "object" } }, { "description": "[Tier 1 — Minimal-Disruption Merge] When: merge two adjacent territories and rebalance with minimal disruption. Prerequisites: adjacent territory_id_a and territory_id_b in source TAL. Open MC preferred args: map_session_id + source_tal_id + territory_id_a/b (live session TS is authoritative — do not repost GeoJSON). Headless: ts_handle or Compact/GeoJSON ts. Modern form-capable clients may be prompted in-band for missing ids / workload dwell; legacy hosts keep CLARIFICATION_REQUIRED. Next: verify merged TAL in MC, analyze. Scenarios: MG-001..004, EV-001.", "inputSchema": { "properties": { "balance_tolerance": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "default": null, "title": "Balance Tolerance" }, "disruption_weight": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "default": null, "title": "Disruption Weight" }, "dwell_time": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Dwell Time" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "map_session_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Map Session Id" }, "new_tal_label": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "New Tal Label" }, "objective": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Objective" }, "part_layer": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Part Layer" }, "point_layer": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Point Layer" }, "repair_policy": { "default": "default", "title": "Repair Policy", "type": "string" }, "source_tal_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Source Tal Id" }, "territory_id_a": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Territory Id A" }, "territory_id_b": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Territory Id B" }, "ts": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Ts" }, "ts_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Ts Handle" }, "visit_frequency_field": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Visit Frequency Field" } }, "title": "territory_mergeArguments", "type": "object" }, "name": "territory_merge", "outputSchema": { "additionalProperties": true, "title": "territory_mergeDictOutput", "type": "object" } }, { "description": "[Tier 1 — Minimal-Disruption Rebalance] When: underlying account data changed but alignment should stay close to current (MDR). Open MC preferred args: map_session_id + source_tal_id (+ objective/dwell) — the live session TS is authoritative; do not invent a ts_handle or repost multi-MB GeoJSON when a map session is open. Headless: ts_handle or Compact TS v2 / GeoJSON ts (both valid). Prerequisites: source TAL, refreshed point_layer; viewer connected for MC-first. The source assignment is the non-regression baseline: if no generated candidate improves the requested objective, the derived TAL stays unchanged and returns NO_IMPROVING_REBALANCE_FOUND. Read solver_diagnostics for source/generated/accepted objectives and convergence. Modern form-capable clients may be prompted in-band for missing source_tal_id / uninferable part_layer / unresolved workload dwell; legacy hosts keep CLARIFICATION_REQUIRED (HITL-025). Next: report retention_pct and diagnostics, then analyze before/after with analysis_panel=single. Scenarios: RB-001..005, EV-003.", "inputSchema": { "properties": { "balance_tolerance": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "default": null, "title": "Balance Tolerance" }, "disruption_weight": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "default": null, "title": "Disruption Weight" }, "dwell_time": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Dwell Time" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "map_session_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Map Session Id" }, "new_tal_label": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "New Tal Label" }, "objective": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Objective" }, "part_layer": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Part Layer" }, "point_layer": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Point Layer" }, "repair_policy": { "default": "default", "title": "Repair Policy", "type": "string" }, "source_tal_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Source Tal Id" }, "ts": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Ts" }, "ts_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Ts Handle" }, "visit_frequency_field": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Visit Frequency Field" } }, "title": "territory_rebalanceArguments", "type": "object" }, "name": "territory_rebalance", "outputSchema": { "additionalProperties": true, "title": "territory_rebalanceDictOutput", "type": "object" } }, { "description": "[Tier 1 — Minimal-Disruption Split] When: split one oversized territory with minimal disruption to the rest of the alignment. Prerequisites: existing TAL with target territory; viewer connected for MC-first. Open MC preferred args: map_session_id + source_tal_id + target_territory_id (live session TS is authoritative — do not repost GeoJSON). Headless: ts_handle or Compact/GeoJSON ts. Distinct from auto_build Scoped Split (balanced-from-scratch). Modern form-capable clients may be prompted in-band for missing source/target ids (and unresolved workload dwell); legacy hosts keep CLARIFICATION_REQUIRED. Next: compare derived TAL in MC, analyze disruption_summary. Scenarios: SP-001..004.", "inputSchema": { "properties": { "balance_tolerance": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "default": null, "title": "Balance Tolerance" }, "disruption_weight": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "default": null, "title": "Disruption Weight" }, "dwell_time": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Dwell Time" }, "guidance_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Guidance Handle" }, "map_session_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Map Session Id" }, "new_tal_label": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "New Tal Label" }, "new_territory_label": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "New Territory Label" }, "objective": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Objective" }, "part_layer": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Part Layer" }, "point_layer": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Point Layer" }, "repair_policy": { "default": "default", "title": "Repair Policy", "type": "string" }, "source_tal_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Source Tal Id" }, "target_territory_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Target Territory Id" }, "ts": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Ts" }, "ts_handle": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Ts Handle" }, "visit_frequency_field": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Visit Frequency Field" } }, "title": "territory_splitArguments", "type": "object" }, "name": "territory_split", "outputSchema": { "additionalProperties": true, "title": "territory_splitDictOutput", "type": "object" } }, { "description": "[Tier 1 — Workflow Advisor] Deterministic read-only next-action advisor for multi-step workflows. When: you know the intent (use discover_intent first if not) and want the validated next tool call instead of guessing the order. Prerequisites: none (static state machine; executes nothing). Pass intent_category (builds: build_from_accounts | account_grouping | known_assignments; edits: realign_existing | restructure; map overlay: load_part_layer for add/show ZIP or part layer; routing: route_stops for driving a known list of stops; reachable areas: reachable_area for a drive-time or drive-distance area around origins, with isochrone_build accepted as an alias; scheduling: periodic_scheduling for a recurring cadence, with schedule_visits accepted as an alias), agent-supplied state (viewer_connected, point_layer, part_layer, tal_present, analysis_fresh; realign adds selection_requested/selection_committed/realign_applied; restructure adds source_tal_id), and optional inputs (builds: part_layer, territory_count, grouping_field, assignments_handle, tal_label; realign: tal_id, moves or realign_operation+part_ids, to_territory_id, analysis_requested; restructure: operation=split|merge|rebalance, source_tal_id, target_territory_id, territory_id_a/territory_id_b; load_part_layer: user_request for ZIP inference; route_stops: route_type=circuit|tour|open_tour, start, stop_sets, end for tour, stops_need_ingest when the stops are not in the TS yet; reachable_area: origins, bands, part_layer, travel_mode=car|truck, origins_need_ingest when the origins are not in the TS yet — the advisor never invents a budget; periodic_scheduling: visit_frequency_field, frequency_unit, dwell_time or dwell_minutes/dwell_hours, daily_capacity or daily_capacity_hours, horizon_days, max_buckets_per_day — the advisor reports cadence/dwell/capacity as missing rather than inventing them). Returns next_tool + drafted next_args, missing_inputs, blocked_by, remaining_steps, guidance_uri, and `guidance` (the EMEP atom inlined as {uri, title, excerpt, truncated} — no resources/read needed); enforces mc_first, viewer_before_compute, build_after_ingest, and conditional analysis_freshness (realign only queues Analyze when a point layer exists or analysis_requested=true). Call it again after each completed step with updated state until done. Other categories (delegate, manual_selection, analyze_present, ...) return a discover_intent/guidance fallback. Scenarios: AB-016, ACB-003, DB-001, WI-001, WI-002, wrong-build-tool, build-before-ingest.", "inputSchema": { "properties": { "inputs": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Inputs" }, "intent_category": { "title": "Intent Category", "type": "string" }, "state": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "State" } }, "required": [ "intent_category" ], "title": "workflow_advisorArguments", "type": "object" }, "name": "workflow_advisor", "outputSchema": { "additionalProperties": true, "title": "workflow_advisorDictOutput", "type": "object" } } ] }
Verify it yourselfcurl -s https://api.teppi.xyz/v1/evidence/sha256:aea99e730a961c54dbf53354bd8dd0e2bb270b5876fba48ba15ed60c67b9f899 | sha256sum