Serveur MCP

EasyTerritory MCP

ai.easyterritory/ezt-mcp
Entreprise et opérations Cartes et localisation Public et accessible MCP 2025-11-25

Ce que fait ce MCP

Builds, balances, analyzes, and edits sales or service territories, with geocoding, routing, scheduling, map visualization, and GeoJSON import/export.

account_build
[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.
Schéma d’entrée
{'type': 'object', 'title': 'account_buildArguments', 'required': ['point_layer'], 'properties': {'ts': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Ts', 'default': None}, 'part_ids': {'anyOf': [{'type': 'array', 'items': {'type': 'string'}}, {'type': 'null'}], 'title': 'Part Ids', 'default': None}, 'tal_label': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Tal Label', 'default': None}, 'ts_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Ts Handle', 'default': None}, 'part_layer': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Part Layer', 'default': None}, 'part_scope': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Part Scope', 'default': None}, 'output_mode': {'enum': ['tal', 'assignments'], 'type': 'string', 'title': '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.'}, 'part_filter': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Part Filter', 'default': None}, 'point_layer': {'type': 'string', 'title': 'Point Layer'}, 'repair_policy': {'type': 'string', 'title': 'Repair Policy', 'default': 'default'}, 'grouping_field': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Grouping Field', 'default': None}, 'map_session_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Map Session Id', 'default': None}, 'conflict_policy': {'type': 'string', 'title': 'Conflict Policy', 'default': 'plurality_account_count'}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}, 'expected_revision': {'anyOf': [{'type': 'integer'}, {'type': 'null'}], 'title': 'Expected Revision', 'default': None}, 'visit_frequency_field': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Visit Frequency Field', 'default': None}, 'ignore_visit_frequency': {'anyOf': [{'type': 'boolean'}, {'type': 'null'}], 'title': 'Ignore Visit Frequency', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'account_buildDictOutput', 'additionalProperties': True}
analyze
[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.
Schéma d’entrée
{'type': 'object', 'title': 'analyzeArguments', 'properties': {'ts': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Ts', 'default': None}, 'scope': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Scope', 'default': None}, 'job_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Job Id', 'default': None}, 'metrics': {'anyOf': [{'type': 'array', 'items': {'type': 'string'}}, {'type': 'null'}], 'title': 'Metrics', 'default': None}, 'tal_ids': {'anyOf': [{'type': 'array', 'items': {'type': 'string'}}, {'type': 'null'}], 'title': 'Tal Ids', 'default': None}, 'max_depth': {'anyOf': [{'type': 'integer'}, {'type': 'null'}], 'title': 'Max Depth', 'default': None}, 'ts_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Ts Handle', 'default': None}, 'dwell_time': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Dwell Time', 'default': None}, 'part_layer': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Part Layer', 'default': None}, 'compare_tals': {'type': 'boolean', 'title': 'Compare Tals', 'default': False}, 'analysis_panel': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Analysis Panel', 'default': None}, 'map_session_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Map Session Id', 'default': None}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}, 'hypothetical_moves': {'anyOf': [{'type': 'array', 'items': {'type': 'object', 'additionalProperties': True}}, {'type': 'null'}], 'title': 'Hypothetical Moves', 'default': None}, 'visit_frequency_field': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Visit Frequency Field', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'analyzeDictOutput', 'additionalProperties': True}
analyze_routes
[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.
Schéma d’entrée
{'type': 'object', 'title': 'analyze_routesArguments', 'properties': {'route_ids': {'anyOf': [{'type': 'array', 'items': {'type': 'string'}}, {'type': 'null'}], 'title': 'Route Ids', 'default': None}, 'ts_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Ts Handle', 'default': None}, 'dwell_time': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Dwell Time', 'default': None}, 'map_session_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Map Session Id', 'default': None}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'analyze_routesDictOutput', 'additionalProperties': True}
auto_build
[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.
Schéma d’entrée
{'type': 'object', 'title': 'auto_buildArguments', 'required': ['point_layer', 'part_layer', 'tal_label'], 'properties': {'ts': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Ts', 'default': None}, 'part_ids': {'anyOf': [{'type': 'array', 'items': {'type': 'string'}}, {'type': 'null'}], 'title': 'Part Ids', 'default': None}, 'objective': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Objective', 'default': None}, 'tal_label': {'type': 'string', 'title': 'Tal Label'}, 'ts_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Ts Handle', 'default': None}, 'build_mode': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Build Mode', 'default': None}, 'dwell_time': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Dwell Time', 'default': None}, 'part_layer': {'type': 'string', 'title': 'Part Layer'}, 'part_scope': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Part Scope', 'default': None}, 'output_mode': {'enum': ['tal', 'assignments'], 'type': 'string', 'title': '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.'}, 'part_filter': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Part Filter', 'default': None}, 'point_layer': {'type': 'string', 'title': 'Point Layer'}, 'repair_policy': {'type': 'string', 'title': 'Repair Policy', 'default': 'default'}, 'map_session_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Map Session Id', 'default': None}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}, 'expected_revision': {'anyOf': [{'type': 'integer'}, {'type': 'null'}], 'title': 'Expected Revision', 'default': None}, 'visit_frequency_field': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Visit Frequency Field', 'default': None}, 'ignore_visit_frequency': {'anyOf': [{'type': 'boolean'}, {'type': 'null'}], 'title': 'Ignore Visit Frequency', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'auto_buildDictOutput', 'additionalProperties': True}
calculate_route
[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.
Schéma d’entrée
{'type': 'object', 'title': 'calculate_routeArguments', 'required': ['route_type', 'start', 'stop_sets'], 'properties': {'end': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'End', 'default': None}, 'start': {'type': 'object', 'title': 'Start', 'additionalProperties': True}, 'route_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Route Id', 'default': None}, 'depart_at': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Depart At', 'default': None}, 'stop_sets': {'type': 'array', 'items': {'type': 'object', 'additionalProperties': True}, 'title': 'Stop Sets'}, 'ts_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Ts Handle', 'default': None}, 'dwell_time': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Dwell Time', 'default': None}, 'route_type': {'type': 'string', 'title': 'Route Type'}, 'route_label': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Route Label', 'default': None}, 'travel_mode': {'type': 'string', 'title': 'Travel Mode', 'default': 'car'}, 'map_session_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Map Session Id', 'default': None}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}, 'include_geometry': {'type': 'boolean', 'title': 'Include Geometry', 'default': True}}}
Schéma de sortie
{'type': 'object', 'title': 'calculate_routeDictOutput', 'additionalProperties': True}
cluster_points
[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.
Schéma d’entrée
{'type': 'object', 'title': 'cluster_pointsArguments', 'required': ['point_layer'], 'properties': {'ts': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Ts', 'default': None}, 'objective': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Objective', 'default': None}, 'ts_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Ts Handle', 'default': None}, 'build_mode': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Build Mode', 'default': None}, 'dwell_time': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Dwell Time', 'default': None}, 'group_field': {'type': 'string', 'title': 'Group Field', 'default': 'group_id'}, 'point_layer': {'type': 'string', 'title': 'Point Layer'}, 'point_filter': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Point Filter', 'default': None}, 'metric_fields': {'anyOf': [{'type': 'array', 'items': {'type': 'object', 'additionalProperties': True}}, {'type': 'null'}], 'title': 'Metric Fields', 'default': None}, 'map_session_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Map Session Id', 'default': None}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}, 'seed_attraction': {'anyOf': [{'type': 'integer'}, {'type': 'null'}], 'title': 'Seed Attraction', 'default': None}, 'seed_point_layer': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Seed Point Layer', 'default': None}, 'expected_revision': {'anyOf': [{'type': 'integer'}, {'type': 'null'}], 'title': 'Expected Revision', 'default': None}, 'group_label_prefix': {'type': 'string', 'title': 'Group Label Prefix', 'default': 'Group'}, 'visit_frequency_field': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Visit Frequency Field', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'cluster_pointsDictOutput', 'additionalProperties': True}
configure_map
[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.
Schéma d’entrée
{'type': 'object', 'title': 'configure_mapArguments', 'properties': {'ts': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Ts', 'default': None}, 'zoom': {'anyOf': [{'type': 'number'}, {'type': 'null'}], 'title': 'Zoom', 'default': None}, 'center': {'anyOf': [{'type': 'array', 'items': {'type': 'number'}}, {'type': 'null'}], 'title': 'Center', 'default': None}, 'ts_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Ts Handle', 'default': None}, 'presentation': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Presentation', 'default': None}, 'project_name': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Project Name', 'default': None}, 'active_tal_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Active Tal Id', 'default': None}, 'classification': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Classification', 'default': None}, 'map_session_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Map Session Id', 'default': None}, 'merge_strategy': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Merge Strategy', 'default': None}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}, 'active_part_layer': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Active Part Layer', 'default': None}, 'expected_revision': {'anyOf': [{'type': 'integer'}, {'type': 'null'}], 'title': 'Expected Revision', 'default': None}, 'loaded_part_layers': {'anyOf': [{'type': 'array', 'items': {'type': 'string'}}, {'type': 'null'}], 'title': 'Loaded Part Layers', 'default': None}, 'expected_content_hash': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Expected Content Hash', 'default': None}, 'point_layer_classifications': {'anyOf': [{'type': 'array', 'items': {'type': 'object', 'additionalProperties': True}}, {'type': 'null'}], 'title': 'Point Layer Classifications', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'configure_mapDictOutput', 'additionalProperties': True}
create_territory_from_parts
[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.
Schéma d’entrée
{'type': 'object', 'title': 'create_territory_from_partsArguments', 'required': ['part_layer', 'part_ids', 'territory_name'], 'properties': {'ts': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Ts', 'default': None}, 'tal_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Tal Id', 'default': None}, 'part_ids': {'type': 'array', 'items': {'type': 'string'}, 'title': 'Part Ids'}, 'dwell_time': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Dwell Time', 'default': None}, 'part_layer': {'type': 'string', 'title': 'Part Layer'}, 'map_session_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Map Session Id', 'default': None}, 'territory_name': {'type': 'string', 'title': 'Territory Name'}, 'territory_path': {'anyOf': [{'type': 'array', 'items': {'type': 'string'}}, {'type': 'null'}], 'title': 'Territory Path', 'default': None}, 'conflict_policy': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Conflict Policy', 'default': None}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'create_territory_from_partsDictOutput', 'additionalProperties': True}
delete_route
[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.
Schéma d’entrée
{'type': 'object', 'title': 'delete_routeArguments', 'required': ['route_id'], 'properties': {'route_id': {'type': 'string', 'title': 'Route Id'}, 'ts_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Ts Handle', 'default': None}, 'map_session_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Map Session Id', 'default': None}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'delete_routeDictOutput', 'additionalProperties': True}
delete_tal
[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.
Schéma d’entrée
{'type': 'object', 'title': 'delete_talArguments', 'required': ['tal_id'], 'properties': {'tal_id': {'type': 'string', 'title': 'Tal Id'}, 'ts_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Ts Handle', 'default': None}, 'map_session_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Map Session Id', 'default': None}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'delete_talDictOutput', 'additionalProperties': True}
delete_territory
[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.
Schéma d’entrée
{'type': 'object', 'title': 'delete_territoryArguments', 'required': ['tal_id', 'territory_id'], 'properties': {'ts': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Ts', 'default': None}, 'tal_id': {'type': 'string', 'title': 'Tal Id'}, 'ts_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Ts Handle', 'default': None}, 'part_layer': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Part Layer', 'default': None}, 'territory_id': {'type': 'string', 'title': 'Territory Id'}, 'repair_policy': {'type': 'string', 'title': 'Repair Policy', 'default': 'default'}, 'map_session_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Map Session Id', 'default': None}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}, 'expected_revision': {'anyOf': [{'type': 'integer'}, {'type': 'null'}], 'title': 'Expected Revision', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'delete_territoryDictOutput', 'additionalProperties': True}
direct_build
[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.
Schéma d’entrée
{'type': 'object', 'title': 'direct_buildArguments', 'required': ['part_layer', 'tal_label'], 'properties': {'ts': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Ts', 'default': None}, 'tal_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Tal Id', 'default': None}, 'tal_label': {'type': 'string', 'title': 'Tal Label'}, 'ts_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Ts Handle', 'default': None}, 'part_layer': {'type': 'string', 'title': 'Part Layer'}, 'assignments': {'anyOf': [{'type': 'array', 'items': {'type': 'object', 'additionalProperties': True}}, {'type': 'null'}], 'title': 'Assignments', 'default': None}, 'repair_policy': {'type': 'string', 'title': 'Repair Policy', 'default': 'default'}, 'map_session_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Map Session Id', 'default': None}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}, 'expected_revision': {'anyOf': [{'type': 'integer'}, {'type': 'null'}], 'title': 'Expected Revision', 'default': None}, 'assignments_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Assignments Handle', 'default': None}, 'missing_part_policy': {'type': 'string', 'title': 'Missing Part Policy', 'default': 'fail'}, 'duplicate_part_policy': {'type': 'string', 'title': 'Duplicate Part Policy', 'default': 'keep_first'}}}
Schéma de sortie
{'type': 'object', 'title': 'direct_buildDictOutput', 'additionalProperties': True}
discover_intent
[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.
Schéma d’entrée
{'type': 'object', 'title': 'discover_intentArguments', 'required': ['user_request'], 'properties': {'context': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Context', 'default': None}, 'user_request': {'type': 'string', 'title': 'User Request'}}}
Schéma de sortie
{'type': 'object', 'title': 'discover_intentDictOutput', 'additionalProperties': True}
ensure_map_viewer
[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.
Schéma d’entrée
{'type': 'object', 'title': 'ensure_map_viewerArguments', 'required': ['map_session_id'], 'properties': {'wait_seconds': {'type': 'number', 'title': 'Wait Seconds', 'default': 0.0}, 'map_session_id': {'type': 'string', 'title': 'Map Session Id'}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}, 'poll_interval_ms': {'type': 'integer', 'title': 'Poll Interval Ms', 'default': 500}}}
Schéma de sortie
{'type': 'object', 'title': 'ensure_map_viewerDictOutput', 'additionalProperties': True}
ep_graph_traverse
[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.
Schéma d’entrée
{'type': 'object', 'title': 'ep_graph_traverseArguments', 'required': ['file_path'], 'properties': {'depth': {'type': 'integer', 'title': 'Depth', 'default': 1}, 'file_path': {'type': 'string', 'title': 'File Path'}, 'edge_kinds': {'anyOf': [{'type': 'array', 'items': {'type': 'string'}}, {'type': 'null'}], 'title': 'Edge Kinds', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'ep_graph_traverseDictOutput', 'additionalProperties': True}
ep_list_topics
[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.
Schéma d’entrée
{'type': 'object', 'title': 'ep_list_topicsArguments', 'properties': {'type': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Type', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'ep_list_topicsDictOutput', 'additionalProperties': True}
ep_search
[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'.
Schéma d’entrée
{'type': 'object', 'title': 'ep_searchArguments', 'required': ['query'], 'properties': {'tags': {'anyOf': [{'type': 'array', 'items': {'type': 'string'}}, {'type': 'null'}], 'title': 'Tags', 'default': None}, 'type': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Type', 'default': None}, 'query': {'type': 'string', 'title': 'Query'}, 'max_results': {'type': 'integer', 'title': 'Max Results', 'default': 6}}}
Schéma de sortie
{'type': 'object', 'title': 'ep_searchDictOutput', 'additionalProperties': True}
export_geojson
[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).
Schéma d’entrée
{'type': 'object', 'title': 'export_geojsonArguments', 'properties': {'zip': {'type': 'boolean', 'title': 'Zip', 'default': True}, 'job_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Job Id', 'default': None}, 'tal_ids': {'anyOf': [{'type': 'array', 'items': {'type': 'string'}}, {'type': 'null'}], 'title': 'Tal Ids', 'default': None}, 'ts_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Ts Handle', 'default': None}, 'point_layers': {'anyOf': [{'type': 'array', 'items': {'type': 'string'}}, {'type': 'null'}], 'title': 'Point Layers', 'default': None}, 'route_layers': {'anyOf': [{'type': 'array', 'items': {'type': 'string'}}, {'type': 'null'}], 'title': 'Route Layers', 'default': None}, 'include_points': {'type': 'boolean', 'title': 'Include Points', 'default': True}, 'include_routes': {'type': 'boolean', 'title': 'Include Routes', 'default': True}, 'map_session_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Map Session Id', 'default': None}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'export_geojsonDictOutput', 'additionalProperties': True}
extract_tal_branch
[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.
Schéma d’entrée
{'type': 'object', 'title': 'extract_tal_branchArguments', 'required': ['tal_id'], 'properties': {'ts': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Ts', 'default': None}, 'tal_id': {'type': 'string', 'title': 'Tal Id'}, 'ts_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Ts Handle', 'default': None}, 'delegate_ref': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Delegate Ref', 'default': None}, 'exclude_locked': {'type': 'boolean', 'title': 'Exclude Locked', 'default': True}, 'map_session_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Map Session Id', 'default': None}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}, 'expected_revision': {'anyOf': [{'type': 'integer'}, {'type': 'null'}], 'title': 'Expected Revision', 'default': None}, 'branch_territory_path': {'anyOf': [{'type': 'array', 'items': {'type': 'string'}}, {'type': 'null'}], 'title': 'Branch Territory Path', 'default': None}, 'expected_content_hash': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Expected Content Hash', 'default': None}, 'branch_rollup_territory_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Branch Rollup Territory Id', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'extract_tal_branchDictOutput', 'additionalProperties': True}
ezt
[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).
Schéma d’entrée
{'type': 'object', 'title': 'eztArguments', 'required': ['request'], 'properties': {'args': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Args', 'default': None}, 'request': {'type': 'string', 'title': 'Request'}, 'csv_file': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'string'}, {'type': 'null'}], 'title': 'Csv File', 'default': None}, 'ts_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Ts Handle', 'default': None}, 'state_facts': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'State Facts', 'default': None}, 'map_session_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Map Session Id', 'default': None}, 'accounts_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Accounts Handle', 'default': None}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}, 'assignments_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Assignments Handle', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'eztDictOutput', 'additionalProperties': True}
geocode_address
[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.
Schéma d’entrée
{'type': 'object', 'title': 'geocode_addressArguments', 'required': ['rows'], 'properties': {'rows': {'type': 'array', 'items': {'type': 'object', 'additionalProperties': True}, 'title': 'Rows'}, 'use_cache': {'type': 'boolean', 'title': 'Use Cache', 'default': True}, 'region_hint': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Region Hint', 'default': None}, 'country_hint': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Country Hint', 'default': None}, 'map_session_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Map Session Id', 'default': None}, 'min_confidence': {'type': 'number', 'title': 'Min Confidence', 'default': 0.0}, 'force_regeocode': {'type': 'boolean', 'title': 'Force Regeocode', 'default': False}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}, 'accept_suboptimal_geocodes': {'type': 'boolean', 'title': 'Accept Suboptimal Geocodes', 'default': False}}}
Schéma de sortie
{'type': 'object', 'title': 'geocode_addressDictOutput', 'additionalProperties': True}
get_guidance
[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.
Schéma d’entrée
{'type': 'object', 'title': 'get_guidanceArguments', 'properties': {}}
Schéma de sortie
{'type': 'object', 'title': 'get_guidanceDictOutput', 'additionalProperties': True}
get_map_selection
[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.
Schéma d’entrée
{'type': 'object', 'title': 'get_map_selectionArguments', 'required': ['map_session_id'], 'properties': {'map_session_id': {'type': 'string', 'title': 'Map Session Id'}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'get_map_selectionDictOutput', 'additionalProperties': True}
get_map_visualization
[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.
Schéma d’entrée
{'type': 'object', 'title': 'get_map_visualizationArguments', 'properties': {'ts': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Ts', 'default': None}, 'mode': {'type': 'string', 'title': 'Mode', 'default': 'view'}, 'job_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Job Id', 'default': None}, 'user_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'User Id', 'default': None}, 'ts_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Ts Handle', 'default': None}, 'new_project': {'type': 'boolean', 'title': 'New Project', 'default': False}, 'presentation': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Presentation', 'default': None}, 'active_tal_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Active Tal Id', 'default': None}, 'expiry_seconds': {'anyOf': [{'type': 'integer'}, {'type': 'null'}], 'title': 'Expiry Seconds', 'default': None}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}, 'interaction_flags': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Interaction Flags', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'get_map_visualizationDictOutput', 'additionalProperties': True}
get_part_selection
[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.
Schéma d’entrée
{'type': 'object', 'title': 'get_part_selectionArguments', 'required': ['selection_task_id'], 'properties': {'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}, 'selection_task_id': {'type': 'string', 'title': 'Selection Task Id'}}}
Schéma de sortie
{'type': 'object', 'title': 'get_part_selectionDictOutput', 'additionalProperties': True}
import_geojson
[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).
Schéma d’entrée
{'type': 'object', 'title': 'import_geojsonArguments', 'properties': {'geojson': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Geojson', 'default': None}, 'tal_label': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Tal Label', 'default': None}, 'part_layer': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Part Layer', 'default': None}, 'point_layer': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Point Layer', 'default': None}, 'geojson_gzip': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Geojson Gzip', 'default': None}, 'geojson_text': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Geojson Text', 'default': None}, 'overlay_policy': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Overlay Policy', 'default': None}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'import_geojsonDictOutput', 'additionalProperties': True}
ingest_accounts
[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.
Schéma d’entrée
{'type': 'object', 'title': 'ingest_accountsArguments', 'properties': {'ts': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Ts', 'default': None}, 'rows': {'anyOf': [{'type': 'array', 'items': {'type': 'object', 'additionalProperties': True}}, {'type': 'null'}], 'title': 'Rows', 'default': None}, 'label': {'type': 'string', 'title': 'Label', 'default': 'Accounts'}, 'id_field': {'type': 'string', 'title': 'Id Field', 'default': 'row_id'}, 'ts_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Ts Handle', 'default': None}, 'use_cache': {'type': 'boolean', 'title': 'Use Cache', 'default': True}, 'label_field': {'type': 'string', 'title': 'Label Field', 'default': 'label'}, 'point_layer': {'type': 'string', 'title': 'Point Layer', 'default': 'accounts'}, 'region_hint': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Region Hint', 'default': None}, 'country_hint': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Country Hint', 'default': None}, 'metric_fields': {'anyOf': [{'type': 'array', 'items': {'type': 'object', 'additionalProperties': True}}, {'type': 'null'}], 'title': 'Metric Fields', 'default': None}, 'search_fields': {'anyOf': [{'type': 'array', 'items': {'type': 'string'}}, {'type': 'null'}], 'title': 'Search Fields', 'default': None}, 'display_fields': {'anyOf': [{'type': 'array', 'items': {'type': 'string'}}, {'type': 'null'}], 'title': 'Display Fields', 'default': None}, 'latitude_field': {'type': 'string', 'title': 'Latitude Field', 'default': 'latitude'}, 'map_session_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Map Session Id', 'default': None}, 'min_confidence': {'type': 'number', 'title': 'Min Confidence', 'default': 0.0}, 'accounts_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Accounts Handle', 'default': None}, 'force_regeocode': {'type': 'boolean', 'title': 'Force Regeocode', 'default': False}, 'grouping_fields': {'anyOf': [{'type': 'array', 'items': {'type': 'string'}}, {'type': 'null'}], 'title': 'Grouping Fields', 'default': None}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}, 'longitude_field': {'type': 'string', 'title': 'Longitude Field', 'default': 'longitude'}, 'workload_fields': {'anyOf': [{'type': 'array', 'items': {'type': 'object', 'additionalProperties': True}}, {'type': 'null'}], 'title': 'Workload Fields', 'default': None}, 'dwell_time_field': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Dwell Time Field', 'default': None}, 'visit_frequency_field': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Visit Frequency Field', 'default': None}, 'accept_suboptimal_geocodes': {'type': 'boolean', 'title': 'Accept Suboptimal Geocodes', 'default': True}}}
Schéma de sortie
{'type': 'object', 'title': 'ingest_accountsDictOutput', 'additionalProperties': True}
isochrone_build
[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.
Schéma d’entrée
{'type': 'object', 'title': 'isochrone_buildArguments', 'required': ['origins', 'bands', 'part_layer'], 'properties': {'avoid': {'anyOf': [{'type': 'array', 'items': {'type': 'string'}}, {'type': 'null'}], 'title': 'Avoid', 'default': None}, 'bands': {'type': 'array', 'items': {'type': 'object', 'additionalProperties': True}, 'title': 'Bands'}, 'tal_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Tal Id', 'default': None}, 'origins': {'type': 'array', 'items': {'type': 'object', 'additionalProperties': True}, 'title': 'Origins'}, 'traffic': {'type': 'boolean', 'title': 'Traffic', 'default': True}, 'depart_at': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Depart At', 'default': None}, 'tal_label': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Tal Label', 'default': None}, 'ts_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Ts Handle', 'default': None}, 'part_layer': {'type': 'string', 'title': 'Part Layer'}, 'route_type': {'type': 'string', 'title': 'Route Type', 'default': 'fastest'}, 'travel_mode': {'type': 'string', 'title': 'Travel Mode', 'default': 'car'}, 'part_overlay': {'type': 'string', 'title': 'Part Overlay', 'default': 'centroid_within'}, 'map_session_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Map Session Id', 'default': None}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'isochrone_buildDictOutput', 'additionalProperties': True}
load_analysis_panel
[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.
Schéma d’entrée
{'type': 'object', 'title': 'load_analysis_panelArguments', 'required': ['map_session_id'], 'properties': {'roles': {'anyOf': [{'type': 'array', 'items': {'type': 'string'}}, {'type': 'null'}], 'title': 'Roles', 'default': None}, 'job_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Job Id', 'default': None}, 'labels': {'anyOf': [{'type': 'array', 'items': {'type': 'string'}}, {'type': 'null'}], 'title': 'Labels', 'default': None}, 'dismiss': {'type': 'boolean', 'title': 'Dismiss', 'default': False}, 'task_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Task Id', 'default': None}, 'analyses': {'anyOf': [{'type': 'array', 'items': {'type': 'object', 'additionalProperties': True}}, {'type': 'null'}], 'title': 'Analyses', 'default': None}, 'map_session_id': {'type': 'string', 'title': 'Map Session Id'}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}, 'presentation_mode': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Presentation Mode', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'load_analysis_panelDictOutput', 'additionalProperties': True}
query_parts
[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.
Schéma d’entrée
{'type': 'object', 'title': 'query_partsArguments', 'required': ['part_layer'], 'properties': {'filter': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Filter', 'default': None}, 'part_ids': {'anyOf': [{'type': 'array', 'items': {'type': 'string'}}, {'type': 'null'}], 'title': 'Part Ids', 'default': None}, 'page_token': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Page Token', 'default': None}, 'part_layer': {'type': 'string', 'title': 'Part Layer'}, 'max_results': {'type': 'integer', 'title': 'Max Results', 'default': 100}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'query_partsDictOutput', 'additionalProperties': True}
realign
[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.
Schéma d’entrée
{'type': 'object', 'title': 'realignArguments', 'required': ['tal_id'], 'properties': {'ts': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Ts', 'default': None}, 'moves': {'anyOf': [{'type': 'array', 'items': {'type': 'object', 'additionalProperties': True}}, {'type': 'null'}], 'title': 'Moves', 'default': None}, 'tal_id': {'type': 'string', 'title': 'Tal Id'}, 'part_ids': {'anyOf': [{'type': 'array', 'items': {'type': 'string'}}, {'type': 'null'}], 'title': 'Part Ids', 'default': None}, 'ts_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Ts Handle', 'default': None}, 'part_layer': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Part Layer', 'default': None}, 'repair_policy': {'type': 'string', 'title': 'Repair Policy', 'default': 'default'}, 'map_session_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Map Session Id', 'default': None}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}, 'expected_revision': {'anyOf': [{'type': 'integer'}, {'type': 'null'}], 'title': 'Expected Revision', 'default': None}, 'realign_operation': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Realign Operation', 'default': None}, 'remove_empty_territories': {'type': 'boolean', 'title': 'Remove Empty Territories', 'default': False}}}
Schéma de sortie
{'type': 'object', 'title': 'realignDictOutput', 'additionalProperties': True}
reintegrate_branch
[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.
Schéma d’entrée
{'type': 'object', 'title': 'reintegrate_branchArguments', 'required': ['expected_master_revision', 'branch_metadata'], 'properties': {'master_ts': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Master Ts', 'default': None}, 'proposal_ts': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Proposal Ts', 'default': None}, 'map_session_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Map Session Id', 'default': None}, 'branch_metadata': {'type': 'object', 'title': 'Branch Metadata', 'additionalProperties': True}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}, 'master_ts_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Master Ts Handle', 'default': None}, 'proposal_ts_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Proposal Ts Handle', 'default': None}, 'expected_master_revision': {'type': 'integer', 'title': 'Expected Master Revision'}, 'expected_master_content_hash': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Expected Master Content Hash', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'reintegrate_branchDictOutput', 'additionalProperties': True}
request_account_upload
[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.
Schéma d’entrée
{'type': 'object', 'title': 'request_account_uploadArguments', 'properties': {'rows': {'anyOf': [{'type': 'array', 'items': {'type': 'object', 'additionalProperties': True}}, {'type': 'null'}], 'title': 'Rows', 'default': None}, 'csv_file': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'string'}, {'type': 'null'}], 'title': 'Csv File', 'default': None}, 'csv_text': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Csv Text', 'default': None}, 'ttl_seconds': {'anyOf': [{'type': 'integer'}, {'type': 'null'}], 'title': 'Ttl Seconds', 'default': None}, 'upload_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Upload Handle', 'default': None}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'request_account_uploadDictOutput', 'additionalProperties': True}
request_assignment_upload
[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).
Schéma d’entrée
{'type': 'object', 'title': 'request_assignment_uploadArguments', 'properties': {'csv_file': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'string'}, {'type': 'null'}], 'title': 'Csv File', 'default': None}, 'csv_text': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Csv Text', 'default': None}, 'assignments': {'anyOf': [{'type': 'array', 'items': {'type': 'object', 'additionalProperties': True}}, {'type': 'null'}], 'title': 'Assignments', 'default': None}, 'ttl_seconds': {'anyOf': [{'type': 'integer'}, {'type': 'null'}], 'title': 'Ttl Seconds', 'default': None}, 'upload_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Upload Handle', 'default': None}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'request_assignment_uploadDictOutput', 'additionalProperties': True}
request_part_selection
[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.
Schéma d’entrée
{'type': 'object', 'title': 'request_part_selectionArguments', 'required': ['part_layer'], 'properties': {'ts': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Ts', 'default': None}, 'prompt': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Prompt', 'default': None}, 'purpose': {'type': 'string', 'title': 'Purpose', 'default': 'generic'}, 'user_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'User Id', 'default': None}, 'part_layer': {'type': 'string', 'title': 'Part Layer'}, 'active_tal_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Active Tal Id', 'default': None}, 'new_territory': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'New Territory', 'default': None}, 'expiry_seconds': {'anyOf': [{'type': 'integer'}, {'type': 'null'}], 'title': 'Expiry Seconds', 'default': None}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}, 'realign_operation': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Realign Operation', 'default': None}, 'destination_territory_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Destination Territory Id', 'default': None}, 'remove_empty_territories': {'type': 'boolean', 'title': 'Remove Empty Territories', 'default': False}}}
Schéma de sortie
{'type': 'object', 'title': 'request_part_selectionDictOutput', 'additionalProperties': True}
schedule_visits
[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.
Schéma d’entrée
{'type': 'object', 'title': 'schedule_visitsArguments', 'required': ['point_layer', 'visit_frequency_field'], 'properties': {'ts': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Ts', 'default': None}, 'horizon': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Horizon', 'default': None}, 'objective': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Objective', 'default': None}, 'ts_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Ts Handle', 'default': None}, 'dwell_time': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Dwell Time', 'default': None}, 'point_layer': {'type': 'string', 'title': 'Point Layer'}, 'daily_capacity': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Daily Capacity', 'default': None}, 'frequency_unit': {'type': 'string', 'title': 'Frequency Unit', 'default': 'interval_days'}, 'map_session_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Map Session Id', 'default': None}, 'schedule_label': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Schedule Label', 'default': None}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}, 'emit_visit_layer': {'type': 'boolean', 'title': 'Emit Visit Layer', 'default': True}, 'visit_layer_name': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Visit Layer Name', 'default': None}, 'expected_revision': {'anyOf': [{'type': 'integer'}, {'type': 'null'}], 'title': 'Expected Revision', 'default': None}, 'never_visit_values': {'anyOf': [{'type': 'array', 'items': {}}, {'type': 'null'}], 'title': 'Never Visit Values', 'default': None}, 'max_buckets_per_day': {'anyOf': [{'type': 'integer'}, {'type': 'null'}], 'title': 'Max Buckets Per Day', 'default': None}, 'visit_frequency_field': {'type': 'string', 'title': 'Visit Frequency Field'}, 'cadence_flexibility_pct': {'anyOf': [{'type': 'number'}, {'type': 'null'}], 'title': 'Cadence Flexibility Pct', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'schedule_visitsDictOutput', 'additionalProperties': True}
seed_build
[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.
Schéma d’entrée
{'type': 'object', 'title': 'seed_buildArguments', 'required': ['seed', 'target'], 'properties': {'seed': {'type': 'object', 'title': 'Seed', 'additionalProperties': True}, 'tal_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Tal Id', 'default': None}, 'target': {'type': 'object', 'title': 'Target', 'additionalProperties': True}, 'user_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'User Id', 'default': None}, 'part_ids': {'anyOf': [{'type': 'array', 'items': {'type': 'string'}}, {'type': 'null'}], 'title': 'Part Ids', 'default': None}, 'max_parts': {'anyOf': [{'type': 'integer'}, {'type': 'null'}], 'title': 'Max Parts', 'default': None}, 'tal_label': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Tal Label', 'default': None}, 'ts_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Ts Handle', 'default': None}, 'part_layer': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Part Layer', 'default': None}, 'part_scope': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Part Scope', 'default': None}, 'part_filter': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Part Filter', 'default': None}, 'point_layer': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Point Layer', 'default': None}, 'map_session_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Map Session Id', 'default': None}, 'territory_name': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Territory Name', 'default': None}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'seed_buildDictOutput', 'additionalProperties': True}
set_map_state
[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).
Schéma d’entrée
{'type': 'object', 'title': 'set_map_stateArguments', 'required': ['map_session_id'], 'properties': {'mode': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Mode', 'default': None}, 'zoom': {'anyOf': [{'type': 'number'}, {'type': 'null'}], 'title': 'Zoom', 'default': None}, 'center': {'anyOf': [{'type': 'array', 'items': {'type': 'number'}}, {'type': 'null'}], 'title': 'Center', 'default': None}, 'active_tal_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Active Tal Id', 'default': None}, 'map_session_id': {'type': 'string', 'title': 'Map Session Id'}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}, 'pending_job_reference': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Pending Job Reference', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'set_map_stateDictOutput', 'additionalProperties': True}
show_map_overlay
[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.
Schéma d’entrée
{'type': 'object', 'title': 'show_map_overlayArguments', 'properties': {'ts': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Ts', 'default': None}, 'route_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Route Id', 'default': None}, 'ts_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Ts Handle', 'default': None}, 'overlay_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Overlay Id', 'default': None}, 'overlay_kind': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Overlay Kind', 'default': None}, 'user_request': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'User Request', 'default': None}, 'map_session_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Map Session Id', 'default': None}, 'merge_strategy': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Merge Strategy', 'default': None}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}, 'expected_revision': {'anyOf': [{'type': 'integer'}, {'type': 'null'}], 'title': 'Expected Revision', 'default': None}, 'expected_content_hash': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Expected Content Hash', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'show_map_overlayDictOutput', 'additionalProperties': True}
submit_feedback
[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.
Schéma d’entrée
{'type': 'object', '$defs': {'FeedbackCategoryParam': {'enum': ['product_gap', 'mcp_tool_gap', 'documentation_gap', 'data_issue', 'workflow_confusion', 'bug', 'suggestion', 'other'], 'type': 'string'}, 'FeedbackSeverityParam': {'enum': ['low', 'medium', 'high', 'blocking'], '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'}}, 'title': 'submit_feedbackArguments', 'required': ['user_goal', 'what_happened'], 'properties': {'category': {'$ref': '#/$defs/FeedbackCategoryParam', 'default': 'other'}, 'severity': {'$ref': '#/$defs/FeedbackSeverityParam', 'default': 'medium'}, 'user_goal': {'type': 'string', 'title': 'User Goal'}, 'confidence': {'$ref': '#/$defs/FeedbackConfidenceParam', 'default': 'medium'}, 'session_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Session Id', 'default': None}, 'client_name': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Client Name', 'default': None}, 'related_tool': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Related Tool', 'default': None}, 'what_happened': {'type': 'string', 'title': 'What Happened'}, 'client_version': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Client Version', 'default': None}, 'correlation_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Correlation Id', 'default': None}, 'context_summary': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Context Summary', 'default': None}, 'include_context': {'type': 'boolean', 'title': 'Include Context', 'default': False}, 'expected_capability': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Expected Capability', 'default': None}, 'related_product_area': {'anyOf': [{'$ref': '#/$defs/FeedbackProductAreaParam'}, {'type': 'null'}], 'default': None}, 'suggested_improvement': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Suggested Improvement', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'submit_feedbackDictOutput', 'additionalProperties': True}
tasks_cancel
[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.
Schéma d’entrée
{'type': 'object', 'title': 'tasks_cancelArguments', 'required': ['task_id'], 'properties': {'task_id': {'type': 'string', 'title': 'Task Id'}}}
Schéma de sortie
{'type': 'object', 'title': 'tasks_cancelDictOutput', 'additionalProperties': True}
tasks_get
[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.
Schéma d’entrée
{'type': 'object', 'title': 'tasks_getArguments', 'required': ['task_id'], 'properties': {'task_id': {'type': 'string', 'title': 'Task Id'}}}
Schéma de sortie
{'type': 'object', 'title': 'tasks_getDictOutput', 'additionalProperties': True}
tasks_result
[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.
Schéma d’entrée
{'type': 'object', 'title': 'tasks_resultArguments', 'required': ['task_id'], 'properties': {'task_id': {'type': 'string', 'title': 'Task Id'}}}
Schéma de sortie
{'type': 'object', 'title': 'tasks_resultDictOutput', 'additionalProperties': True}
territory_merge
[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.
Schéma d’entrée
{'type': 'object', 'title': 'territory_mergeArguments', 'properties': {'ts': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Ts', 'default': None}, 'objective': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Objective', 'default': None}, 'ts_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Ts Handle', 'default': None}, 'dwell_time': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Dwell Time', 'default': None}, 'part_layer': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Part Layer', 'default': None}, 'point_layer': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Point Layer', 'default': None}, 'new_tal_label': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'New Tal Label', 'default': None}, 'repair_policy': {'type': 'string', 'title': 'Repair Policy', 'default': 'default'}, 'source_tal_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Source Tal Id', 'default': None}, 'map_session_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Map Session Id', 'default': None}, 'territory_id_a': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Territory Id A', 'default': None}, 'territory_id_b': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Territory Id B', 'default': None}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}, 'balance_tolerance': {'anyOf': [{'type': 'number'}, {'type': 'null'}], 'title': 'Balance Tolerance', 'default': None}, 'disruption_weight': {'anyOf': [{'type': 'number'}, {'type': 'null'}], 'title': 'Disruption Weight', 'default': None}, 'visit_frequency_field': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Visit Frequency Field', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'territory_mergeDictOutput', 'additionalProperties': True}
territory_rebalance
[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.
Schéma d’entrée
{'type': 'object', 'title': 'territory_rebalanceArguments', 'properties': {'ts': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Ts', 'default': None}, 'objective': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Objective', 'default': None}, 'ts_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Ts Handle', 'default': None}, 'dwell_time': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Dwell Time', 'default': None}, 'part_layer': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Part Layer', 'default': None}, 'point_layer': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Point Layer', 'default': None}, 'new_tal_label': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'New Tal Label', 'default': None}, 'repair_policy': {'type': 'string', 'title': 'Repair Policy', 'default': 'default'}, 'source_tal_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Source Tal Id', 'default': None}, 'map_session_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Map Session Id', 'default': None}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}, 'balance_tolerance': {'anyOf': [{'type': 'number'}, {'type': 'null'}], 'title': 'Balance Tolerance', 'default': None}, 'disruption_weight': {'anyOf': [{'type': 'number'}, {'type': 'null'}], 'title': 'Disruption Weight', 'default': None}, 'visit_frequency_field': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Visit Frequency Field', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'territory_rebalanceDictOutput', 'additionalProperties': True}
territory_split
[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.
Schéma d’entrée
{'type': 'object', 'title': 'territory_splitArguments', 'properties': {'ts': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Ts', 'default': None}, 'objective': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Objective', 'default': None}, 'ts_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Ts Handle', 'default': None}, 'dwell_time': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Dwell Time', 'default': None}, 'part_layer': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Part Layer', 'default': None}, 'point_layer': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Point Layer', 'default': None}, 'new_tal_label': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'New Tal Label', 'default': None}, 'repair_policy': {'type': 'string', 'title': 'Repair Policy', 'default': 'default'}, 'source_tal_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Source Tal Id', 'default': None}, 'map_session_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Map Session Id', 'default': None}, 'guidance_handle': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Guidance Handle', 'default': None}, 'balance_tolerance': {'anyOf': [{'type': 'number'}, {'type': 'null'}], 'title': 'Balance Tolerance', 'default': None}, 'disruption_weight': {'anyOf': [{'type': 'number'}, {'type': 'null'}], 'title': 'Disruption Weight', 'default': None}, 'new_territory_label': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'New Territory Label', 'default': None}, 'target_territory_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Target Territory Id', 'default': None}, 'visit_frequency_field': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Visit Frequency Field', 'default': None}}}
Schéma de sortie
{'type': 'object', 'title': 'territory_splitDictOutput', 'additionalProperties': True}
workflow_advisor
[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.
Schéma d’entrée
{'type': 'object', 'title': 'workflow_advisorArguments', 'required': ['intent_category'], 'properties': {'state': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'State', 'default': None}, 'inputs': {'anyOf': [{'type': 'object', 'additionalProperties': True}, {'type': 'null'}], 'title': 'Inputs', 'default': None}, 'intent_category': {'type': 'string', 'title': 'Intent Category'}}}
Schéma de sortie
{'type': 'object', 'title': 'workflow_advisorDictOutput', 'additionalProperties': True}
Ajouté
ezt
30 September 2026 02:40
Ajouté
import_geojson
30 September 2026 02:40
Ajouté
export_geojson
30 September 2026 02:40
Ajouté
show_map_overlay
30 September 2026 02:40
Ajouté
configure_map
30 September 2026 02:40
Ajouté
load_analysis_panel
30 September 2026 02:40
Ajouté
get_map_selection
30 September 2026 02:40
Ajouté
set_map_state
30 September 2026 02:40
Ajouté
tasks_result
30 September 2026 02:40
Ajouté
tasks_get
30 September 2026 02:40
Ajouté
tasks_cancel
30 September 2026 02:40
Ajouté
analyze_routes
30 September 2026 02:40
Ajouté
delete_route
30 September 2026 02:40
Ajouté
seed_build
30 September 2026 02:40
Ajouté
isochrone_build
30 September 2026 02:40
Ajouté
calculate_route
30 September 2026 02:40
Ajouté
geocode_address
30 September 2026 02:40
Ajouté
request_assignment_upload
30 September 2026 02:40
Ajouté
request_account_upload
30 September 2026 02:40
Ajouté
ingest_accounts
30 September 2026 02:40
Ajouté
analyze
30 September 2026 02:40
Ajouté
create_territory_from_parts
30 September 2026 02:40
Ajouté
get_part_selection
30 September 2026 02:40
Ajouté
request_part_selection
30 September 2026 02:40
Ajouté
reintegrate_branch
30 September 2026 02:40
Ajouté
extract_tal_branch
30 September 2026 02:40
Ajouté
territory_rebalance
30 September 2026 02:40
Ajouté
territory_merge
30 September 2026 02:40
Ajouté
territory_split
30 September 2026 02:40
Ajouté
delete_territory
30 September 2026 02:40