Every change Wellknown observed on this MCP server, newest first, with what it was before and what it became. Tool-surface changes carry the definition diff. Nothing here is edited after the fact.
Authorization not required, issuer https://mcp.easyterritory.ai/
Changed the definition of "analyze"
⟨10 unchanged words⟩ dock with this result (needs map_session_id).comparison_slotThefillsdocktheshowssameone alignment at a time: with several tal_ids each TAL gets its own dock(noentryside-by-sideandlayout).thenonedockorfollowsomittedthepushesmap'snothing.activeNotTAL. comparison_slot does the sameenumandasalsoload_analysis_panel.presentation_modereturns(single|compare).cross_tal_comparison. none or omitted pushes nothing.","enum":["none","single"," ⟨170 unchanged words⟩
Authorization not required, issuer https://mcp.easyterritory.ai/
Changed the definition of "account_build", "analyze", "analyze_routes" and 44 more
[Tier 1 — Attribute Grouping Builder] When:Builds territoriesshouldthatmirrorfollow an accountattributecolumnfrom,CRMsuchexportsas(rep name,territory_name,or territory code).Prerequisites
Changed the definition of "account_build", "analyze", "analyze_routes" and 44 more
before
—after
Build Territories by Account Fieldbefore
—after
{"destructiveHint":true,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Build Territories by Account Field"}before
—Authorization not required, issuer https://mcp.easyterritory.ai/
Changed the definition of "analyze", "delete_territory", "ensure_map_viewer" and 3 more
⟨49 unchanged words⟩ that job's snapshot and wins when both arepassed;passed,aexcept when the snapshotwithouthas no pointsreturnsandNO_POINT_LAYERthewithopendo_this_nextmapnamingdoes:
Authorization not required, issuer https://mcp.easyterritory.ai/
Changed the definition of "account_build", "analyze", "auto_build" and 16 more
⟨149 unchanged words⟩ },"repair_policy":{"default":"default","description":"Topology repair for dissolved territories. default fills interior holes in the dissolved geometry (no part is added or reassigned; repair_summary.changed_part_ids lists the territories' own parts). report_only detects holes and changes nothing (warning REPAIR_AVAILABLE). strict fails with REPAIR_REQUIRED when a hole is found.","enum":["default","strict","report_only"],"title":"Repair Policy","type":"string" ⟨52 unchanged words⟩
⟨18 unchanged words⟩ inline ts or result.ts_handle from prior compute jobs)., or map_session_id alone to analyze the open map as it is now, including points ingested after the build. A job_id or ts_handle is that job's snapshot and wins when both are passed; a snapshot without points returns NO_POINT_LAYER with do_this_next naming the map_session_id retry.
Certificate recorded, valid to 2027-02-11
Authorization not required, issuer https://mcp.easyterritory.ai/
Unknown → Live
First tool surface recorded: 47 tools
https://mcp.easyterritory.ai (mcp_streamable_http) — from mcp_registry, with the record
Showing the latest 14 events. The API returns up to 500 and filters by kind: ?kind=tool_surface_changed
⟨27 unchanged words⟩ ":null,"title":"Grouping Field"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"ignore_visit_frequency":{"anyOf":[{"type":"boolean ⟨212 unchanged words⟩
[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_handleComputesfromterritorypriorstatisticscomputeforjobs),one ormap_session_id alone to analyze the open map as it ismorenowalignments,including pointsi.e.ingestedhowafterbalanced thebuild. A job_id or ts_handle is that job's snapshot and wins when bothterritories arepassed, except when the snapshot has no pointsandthe open map does: then the live map is analyzed and the result warns ANALYZED_LIVE_SESSION. 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, nothowpointalignmentscolumns)compare:workload (territory hours; aliasaccounttotal_workload_hours)counts,account_count. Point columns must be fieldsdeclaredatmetricingesttotals (metric_fields/workload_fields,e.g. Revenue, Units Sold); undeclared columns,areworkloaddiscardedhours,atbalanceingestscores, andfail with UNDECLARED_FIELD — re-ingest with the column declared to analyze it. Responsecross-TALincludescomparison;available_metrics.hypothetical_movesMETRICpreviewsCOLUMNSaALWAYSrealign.RENDERInput:the territory_metric_gridsmap_session_id (andtheMC dock) carry Total Count plus a Total <metric> column for EVERY declared metric_fieldsopencolumnmapofastheitpointislayernow),in declaration orderts_handle,even when metrics names only account_countts, orworkload —acount-only panel is never the correct outcome when metrics are declared. Declared names may becompletedbarebuildstringsjob_id (Designerpasspull)tal_idsorwhen{field,label,type}severalobjectsTALs(ingestexist).Metric cells are parsed leniently: '14,651', '$1,200.50', and padded strings sum as numbers. DWELL / WORKLOAD (T-171)metrics:workload hours need onsite/dwell time — requestomitdwell_time,forTALeverybuild_provenance.dwell_timedeclared(auto_build)column, orthe point layer's dwell_time_field. When none resolves, Analyze STILL RUNS and reports counts, metrics, classification breakdowns, and balance on thosenamedimensions;them; workloadis OMITTED (no total_workload_hours column, workload_total null) — never drive-only hours. The result then carries workload_omitted {tal_ids, ask_user, retry}: reportandtheaccount_countstatisticsareFIRST,alwaysthenavailable.relayWorkloadask_userneedsverbatimdwell (it asks for an average onsite/dwell time). Re-run analyze withdwell_timeonly if, theuser answers; never invent a default (including 30 minutes). Do not askbuild'sforrecorded dwellbefore the first analyze just to,avoidor thenote. DRIVE-TIME TALSlayer's(isochrone_builddwell_time_field): accounts are counted inside;eachwithoutprovideritpolygonthe(measured_by=area_polygon),resultinhas everyarea that containsotherthemstatistic,so areanototalsworkload,canandexceedworkload_omitted.ask_user, theaccount count; area_overlaps lists accounts shared across origins. scope and hypothetical_moves doquestionnotforapply.thePreferuser. analysis_panel=single with map_session_idso the MC dockfillson this call — that istheone-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 proofmap'stheanalysis dockopened. Otherwise pass the full Analyze result(or its task_idanalysis_panel.status=pushed)to load_analysis_panel. WORKLOAD UNITS:. territories[].workload_total isminutes (workload_unit=minutes). Quoteminutes; hoursonly 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_loadedareeventinopensterritory_metric_grids.theDrive-timedock.alignmentsChat-onlycountJSONaccountsisinsidenoteachcompletionpolygon.whenReturns amap is open. Use ezt://guidance/analysis-presentation for narrative. Full atom: ezt://guidance/workflows/analyze-and-present. Scenarios: AN-001..007, MC-009, S002.task.
⟨7 unchanged words⟩ open map's analysis dock with this result (passneeds map_session_id); prefer it.. comparison_slot fills the same dock (no side-by-side ⟨44 unchanged words⟩ ],"default":null,"title":"Dwell Time"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"hypothetical_moves":{"anyOf":[{"items ⟨136 unchanged words⟩
[Tier 2 — RouteReturnsFacts]theWhen:totalyouhoursneedforper-routeeachnumbersofforthe routes calculate_routealready drewhas—drawn: drive hours, dwell hours, route_workload_hours,stop coordinates, centroid, bbox(e.g. 'which of my Houston runs has roomproviderfordriveonetimemoreplusstop'dwell,'total hours fornoeachvisit-frequencyroute'multiplier). Prerequisites:,atstopleastcoordinates,onecentroid,routeandinbbox.theSynchronous.session/TSNeeds(map_session_idpreferred, elseor ts_handle); dwell confirmed by the user unless calculate_route already stored it. route_workload_hours,=andproviderdwelldrivefromtimedwell_time+orconfirmedthe dwell,with NO visit-frequencyrecordedmultiplier.ondwell_hourstheisroute;oneunresolved dwellper stopreturnsinCLARIFICATION_REQUIREDstop_count.(needs_dwell). A circuit's stop_count includes the return to the start, so thataccount is chargedstop's dwelltwice. Cluster and territory workload charge each account once. This is a DIFFERENT quantity from those hours — never sum, compare, or substitute one for thecountsother.twice.FACTSFactsONLYonly: no capacity,no headroom, noranking,no overloaded flag. Applyorconstraintsoverloadlikeflags.'nearestTheseroutehoursunderare7ahours'differentyourselfmeasure fromcentroid + 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 returnsterritoryworkload); do NOT feed these hours into auto_build, territory_rebalance,andorclusteraworkload; territoryworkload figure. Unresolved dwell returns CLARIFICATION_REQUIRED / needs_dwell — ask the user, never invent a default (including 30 minutes). Synchronous: nostatisticstask_id.comeScenarios:fromRT-011.analyze.
⟨10 unchanged words⟩ ":null,"title":"Dwell Time"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"map_session_id":{"anyOf":[{"type":"string ⟨37 unchanged words⟩
[Tier 1 — Balanced Territory Builder] When:Partitionspartitioningested account points intoNbalancedterritories over a part layer (for example, ten territories; Mode Asalescount,territoriesModewithBequal workloadtarget,Scoped Split). Execution gate: after sizing, balance, dwell, and scope are known, call THIS tool immediately — searching the catalog, get_guidancemetric, orpolling Tasks never starts a build. A successful responsecountwithover anew task_id is the only proof of submit; do not claim started/restartingpartuntillayerthen.(e.g.ThenZIPs)followanddo_this_nextaddsonlythemwithasthata newtask_id. When workflow_advisor returns next_tool=auto_build, call auto_build next. Canonical examplealignment—(TAL);3existingTXalignmentsZIPstay.territories,Needsworkload-only,the30-minuser'sdwellchoices:build_mode={mode:build_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_countterritory_count, fixed_workload_target,scoped_split. For workload targets+(e.g.target_workload40-hourinterritories)hours,use build_mode={mode:orfixed_workload_targetscoped_split),target_workload: 40}objective (territories only approximate the target). ALWAYS askworkload_bias fordwell/onsite time before calling — Auto Build always computes territoryworkloadhours=(drive+ dwell), even when balancingtimeon+adwell; metricor account count. Pass dwell_time only after+theymetric_biasconfirmfor a column({type: field, field, unit}) or scalar ({type: scalar, value, unit}). Never invent default dwellsuch as1 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,Revenue;askbiaseswhethersum toaggregate workload across a schedule period (pass visit_frequency_field) or ignore it (pass ignore_visit_frequency=true100). Do not inherit the column silently. If no visit-frequency column exists,omit both. Modern clients that negotiate protocol >= 2026-07-28anddeclare 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_userdwell_time (HITL-025 — notaserver failure). build_mode may be omitted when elicitation can fill it. Workload is drive time plus dwell — not Revenuecolumn orany metric column.aWhenscalar the usersays 'balanced on workload' or 'workload-balanced', use workload_bias=100 with no objective.metric; do not ask which metric column workload means — ask dwell (Q5states)only. Confirm balance.dimensionEach(Q2)missingandchoicebiasreturns(Q3)CLARIFICATION_REQUIRED with theuser whenquestionunclearin—ask_user; aworkload-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 askdeclaredwhichvisit-frequency 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_idalsoandneedsanalysis_panel=singlevisit_frequency_field(orload_analysis_panel) so stats appear in the MC dock — not chat-only. Part scope defaultsignore_visit_frequency.topart_scope: bbox_intersect (ZIPs intersecting the account-pointdefault;bbox)parts—neargeographictheproximityaccounts,not state/attribute scope. When the usercannamesreachanationalstatescale) orregion ('TX ZIPs only',explicit'Texaswithonly')part_filter,pass part_scope=explicit ande.g.part_filter={state_abbr: TX}immediately; do not default to.bbox_intersectWith(canmap_session_idpullthetensopenofmapthousandsisofthenationalinput;ZIPs)ts anddo not query_parts first for an obvious state filter. Scope fieldsts_handle aretop-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 qualitynotandneeded.returnsReturns aNATIONAL_PART_SCOPE warning rather than silently switching geography. Long partitions publish named 35–42% subphases (travel cache,task;powerthesolve,resultbordercarriesrefinementtal_id,compactnessts_handle,seam polish)andrenew 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,shapepart_layerfigureschosen(min_solidity,viewer connected (human-in-looppolsby_popper).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:analyzewith analysis_panel=single. Full atom: ezt://guidance/workflows/build-from-accounts. Scenarios: S003,reportsAB-001..017,theMC-011.statistics.
⟨34 unchanged words⟩ ":null,"title":"Expected Revision"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"ignore_visit_frequency":{"anyOf":[{"type":"boolean ⟨216 unchanged words⟩
[Tier 1 — Routing]SequencesWhen:anddrivedrives alistset of stopsin 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. Whene.g. avisit-frequencytechniciancolumnfromishomeonto thepoint layerdepot, then thewordday'sroutecalls,isandambiguousback—home:confirmreturns theuser wants a driving itinerary oforder,thoseroadstopsdistance,notdriveschedule_visitstime,before callingper-legthistimings,tool.andPrerequisites:geometryafrom TomTom or Azure Maps,keyandondraws theserver; stopspathreferencedwithbynumberedpoint_layerstopsmustonalreadythebeopeningested,mapor(map_session_id).passCreatesinlinenolat/lon.territoryTERRITORYandSTOPS:changesafternoaassignment.TALroute_type isbuilt or analyze linkage runs, account points carry territory_id. Route one territory with stop_sets source.filter.territory_id set totheleaf iduser'sfromchoice:thecircuitlast(returnsbuildto(leaf_territories[].territory_idstart),not a displaytourlabel.(endsstartatistheexactlyrequiredoneend stop(home, depot), or open_tour (onepoint_idway).The territory filter belongs onstop_sets,never on start. ORDERED STOP SETS: stop_setsare visited in the ordersuppliedgiven andoptimizationnoNEVERstop movesa stopbetweensets, 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,sets;'as_given'ordertoiskeepoptimizeitorfixed.as_given; group_by_fieldsplitsaddsoneprioritysetbands.intoStopsorderedarebandsinlineoncoordinatesaorcolumnpoint-layervaluereferences (e.g.route_priority: every 1 precedes every 2, optimized inside each bandsource.filter.territory_id).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.Acircuit'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 NEWnew route_idADDSadds aroute (10 Houston routes coexist, each with its own legend row andsecondcolor).route;Reusingrecalculating anEXISTINGexisting route_idREPLACES that route inreplacesplaceit,which is the waye.g. toadd ordrop astop: 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-mintedstop.iddwell_time isfinestored fora one-off).analyze_routes.UseRoutedelete_routedrivetotimedropis awhole route; per-route workload facts come from analyze_routes,differentnevermeasure fromanalyze. Pass dwell_time when you want route workload recomputable later; it is stored as provenance and never invented.territoryQUOTA:workload.billableNo providercalls are capped per APIkeyper calendar month, and a long stop list chunked to the vendor waypoint cap costs one call per chunk. A route isreturnsall-or-nothing:ROUTING_NOT_CONFIGURED; past thecap it returns PROVIDER_QUOTA_EXCEEDED with limit, used,monthlyremainingquota,and period_resets_at ratherPROVIDER_QUOTA_EXCEEDED.thanReturns apartial 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.task.
⟨33 unchanged words⟩ "default":null,"title":"End"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"include_geometry":{"default":true,"title":"Include ⟨88 unchanged words⟩
[Tier 1 — BalancedGroupsPointtheGrouping]pointsPartitionof one ingestedpointlayerdirectlyinto balancedpoint groups withclustersCCPDdirectly,withoutwithusingnoZIPs/countiesZIP orcreatingpartalayerTAL.andThenotoolterritoryaddsalignment. Writes group_field (default group_id)toon every point andcolor-classifies that field incolors thelinked Map Component. Thatlayerwritebyreplacesit,thereplacinglayer'sanyexistingprior color classification (including a leftover color_classification ramp). Sizesize and shape channelsstay. 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}stay).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 theNeedssamebuild_modesizing,(fixed_territory_countbalance,orbiasfixed_workload_target),visit-frequencyobjective, anddwell questions; workload means in-group drive time plus dwelldwell_time,never a metric column. Never invent dwell. build_mode supports fixed_territory_count and fixed_workload_targetasonly.inSUBSET:auto_build; anamed state or region on an already-ingested layer — Florida schools, TX accounts onlymissing—oneusesreturnspoint_filterCLARIFICATION_REQUIREDonwith thefirstquestion.call,point_filter (e.g.point_filter={STATE: FL}. Keys are point properties (one value or a list).Do not re-ingestclusters afiltered extract. Do not pass part_filter or part_scope (those belong to ZIP/part tools). Do notsubsetclusterof thenational layer.layer;Unmatchedunmatched points stayon the layer with group_field cleared and appear as an Ungrouped legend,class.ungrouped.TheMax 10,000-point cap000appliespoints 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, depotsseed_point_layer,branch offices — pass seed_point_layer=<that layer>. It must beaSECOND ingestedsecond point layer,notsuchpoint_layer.asEachtechnicianseedhomes, anchorsexactly ONEone group,soper seed (the seed countmust matchmatches the group count(fixed_workload_target must derive that,sameelsecountseed_count_mismatch)or the call fails INVALID_REQUEST with blocked_by=seed_count_mismatch.; seed_attraction(0-100,(default 70)controlsis theholdpullonof eachgroup centroid: 100 pins it at itsseed,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 inon itsgroup'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 notgroup.produceReturns aTAL. Use auto_build for territories. Followtask; thereturned task. Theresult lists groups(group_id,with point_count, workload_hours,point_ids). Full atom: ezt://guidance/workflows/cluster-points. Scenarios:andCP-001..005.point_ids.
⟨51 unchanged words⟩ Label Prefix","type":"string"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"map_session_id":{"anyOf":[{"type":"string ⟨119 unchanged words⟩
[TierUpdates2durable—mapDurablesettingsMaponConfig]theWhen:TerritorysetSolutionorandupdaterefreshes the live map: project_name (themapdurableheading),TSloaded_part_layersshort([]namehidesusedZIP/partasoutlines;thebuildsMapneverComponentloadheadingthem),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_nameandemits config_changed so a linked session refreshes the heading in place. POINT SYMBOLOGY:zoompoint_layer_classifications(defaultisview; theway to recolor/resize/reshape points on an openlive map— never export GeoJSON, compute breaksjumpsclient-sidethere), andrepost apointTS.symbology.Eachpoint_layer_classificationsentryentriesisare {point_layer, field, method:quantile|equal_interval|categorical|manual,quantileclass_count|(2-12),equal_intervalchannel:|color|size|shape,categoricaloptional|colors/sizes/shapesmanual,optionalclass_countstyle:{color,size,opacity2-12,shape}for the layer base symbol}. Supported shapeschannel:circle|square|triangle|diamond|star|cross|house|pin|flag|hexagon|pentagon|shield|arrow_up|building (aliasescolorhome→house,|marker/map_pin→pin,sizehex→hexagon,|arrow/up→arrow_upshape,warehouse/depot→building,optionalplus/x→cross).colors/sizes/shapes/style};Thethe server computesbreaks fromthein-session points, writes point_layers[].<channel>_classification, pushes config_changed,breaks and returnsthe applied classes withper-class counts.Categorical missing values become an Ungrouped class.One classification per channel perlayer: a second color entry replaces the first. Two encodings on one layer mix channels (color+size or color+shape).layer; clear:truewith point_layer and channelremovesdropsone.thatShapeschannelincludeclassificationcircle,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 onesquare,entrytriangle,perdiamond,layerstar,tocross,givehouse,twopin,pointflag,layershexagon,distinctpentagon,colorsshield,orarrow_up,shapes.building. classification(without a point_layer)staysis a project-levelmap_config patchsettingandthat doesNOT paint points; a point-layer-targeted classification is routed to symbology with anotwarning.colorPARTpoints.LAYERSInput:builds never load a part layer overlay; ZIP/county outlines appear only when named. loaded_part_layers=[ids] shows them, loaded_part_layers=[] hides them all (active_part_layer clears too), and the choice persists across later builds. request_part_selection loads the part_layer it selects on. 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. Prefermap_session_idfor an open MC —(the livesession 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-1map). Prerequisites: map_session_id, ts_handle, orinline 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.ts.
⟨69 unchanged words⟩ ":null,"title":"Expected Revision"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"loaded_part_layers":{"anyOf":[{"items":{" ⟨112 unchanged words⟩
[Tier 2 — Territory From Parts] When: create or updateCreates one leaf territory fromcommitted part IDs (manual MC-005 build oraRL-011/012).listPrerequisites:of part_idsfrom 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 toe.g.showathecommittednewmapterritoryselection).,Ifinresult.map_refresh.notifiedaisnewfalsealignment orstatus isanrebind_required,existingcalloneget_map_visualization(job_id=<this job>tal_id). Withno open MC, pass ts_handle frommap_session_id theprior result (never repost inline ts when a handle exists);livethemap'snewTerritoryTALSolution isappended to that TSupdated andtherepainted;resultotherwisereturnsts_handle (a newts_handle.ts_handle is returned). map_session_id wins when both are passed.Completed result includes created_territory.territory_id, leaf_territories, and selection_summary (requested/unique counts plus duplicate_part_ids) —territory_name is a displaylabel only (e.g. T1 → territory_id like tal-t1-t1). Later realign into this territorylabel;MUSTtheuseresult's created_territory.territory_id(preferred) oris theexactstableleafiddisplayfornamelaterwhenedits,unique;plusneverleaf_territoriesinventandshorthandselection_summary (T3 ≠ Territory 3duplicate_part_ids)— if unclear, ask which leaf from leaf_territories. DWELL: this tool.createsDwellaisNEWoptionalTALhere anddoesis notinherit dwell_timeinherited fromaanpriorearlierauto_build.auto_build;Dwelldwell_time isNOT required to create the territory (same as direct_build). Optional dwell_time={type:scalar,value,unit} stamps build_provenancerecorded for laterAnalyze.analyze. Withoutdwellit,Analyze still runs andanalyze reports everyotherstatisticbut OMITSexcept 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-bandandforreturnsdwellworkload_omitted.ask_user.whenReturns ahydrated 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.task.
⟨21 unchanged words⟩ ":null,"title":"Dwell Time"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"map_session_id":{"anyOf":[{"type":"string ⟨85 unchanged words⟩
[Tier 2 — DeleteRoute]Whendelete:dropremovesaone whole route from themap and the TSTerritory(e.g.Solution'deleteand theTuesday Aopenroute'map,'remove route houston-tue-a'). Prerequisites: map_session_id for an open MCin(preferred),oneelsesynchronousts_handle;call.plusNeeds route_id(oranthe route's exact uniqueroutelabel). NOT for removing a single stop — for that,call calculate_route again with the SAME route_id and the remainingplusstops,map_session_idwhichorreplacests_handle.theA routeinthatplace.isAlready-absentalreadyroutesgonereturnreturns ok withalready_absent=true and no error.already_absent=true;Anan ambiguous label returns AMBIGUOUS_ROUTE withmatching_route_ids — pass an explicit route_idmatching_route_ids.ratherAthansingleguessing.stopSynchronous:isnoremovedtask_id.byVerifyrecalculating theopen map legend no longer lists thesameroute.route_idScenarios:withRT-010.calculate_route.
{"properties":{"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"map_session_id":{"anyOf":[{"type":"string ⟨31 unchanged words⟩
[Tier 1 — Delete TAL] When: wipeDeletes one wholeTerritory Alignment Layer (user language:territorylayer,alignment,all territories, active alignment — not only 'TAL') from the TS and open map(e.g. 'removeTAL:the'the territory layer','wipe all'all territories',)'removefrom thealignment', 'clear tal-tx-10t before rebuild'). Prerequisites:Territorymap_session_idSolutionforandanthe openMC (preferred)map,elseints_handle;oneplussynchronous call. Needs tal_id(oranthe TAL's exact uniqueTALlabel). NOT for deleting one leaf territory — that is delete_territory. Do NOT N× delete_territory to wipe an alignment. Synchronous: no task_id /,noplusRealignmap_session_idprogressoroverlay.ts_handle. Points, part-layer overlays, and routes stay;active_tal_idthebecomesmap switches to a remainingTAL, __points__,alignment or__empty__. Already-absent returns oktowithpointsalready_absent=true.only.AmbiguousAlabelTALreturnsthatAMBIGUOUS_TAL.isNext:alreadyverifygonethereturnslegendokdroppedwiththealready_absent=true;alignment,anthenambiguousauto_buildlabel/returnsaccount_buildAMBIGUOUS_TAL./Onedirect_buildterritorywhenisrebuilding.removedScenarios:withHITL-057,delete_territoryT-134.instead.
{"properties":{"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"map_session_id":{"anyOf":[{"type":"string ⟨31 unchanged words⟩
[Tier 1 — Delete Territory] When: deleteDeletes one leaf territory from anexisting TAL (e.g. 'delete T1', 'remove territory West'). Prerequisites: ONE current TS reference —alignment;preferitsmap_session_idpartsforbecomeanunassigned.openNeedsMCtal_id,else ts_handle or ts; tal_id;territory_id (stablethe leaf idfrom created_territory.territory_id / leaf_territories preferred,oranits 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. When a rep leavesand theneighbors should takeTerritoryoverSolutionthefromterritorymap_session_id,call territory_merge instead — this toolts_handle,leavesorthets.deletedAterritory'sterritorypartsthatunassigned.isAlready-absentalreadyterritoriesgonereturnreturns ok with already_absent=true(no job). Next: follow the returned Tasks status operation until completed,andcallnoitstask;resultotherwiseoperationreturnsonce,athentaskverifyand the open maplegend norepaints.longerRelated:liststerritory_mergethehandsterritoryaANDleavingtherep's territoryfill/outline is gone from the canvas (not just the legend) before claiming success. Geography-only delete does not requiretoAnalyze.itsScenarios:neighbors;feedbackdelete_taldelete-territory,removesRL-013awrapper,wholeT-087alignmentlast-leafinpaintoneclear.call.
⟨8 unchanged words⟩ ":null,"title":"Expected Revision"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"map_session_id":{"anyOf":[{"type":"string ⟨118 unchanged words⟩
[TierBuilds1territories—fromKnownknownAssignmentspart-to-territoryBuilder]assignmentsWhen:(auserZIP-to-territoryhassheetexplicitorpart-to-territoryfile,assignmentsincluding(spreadsheet,alegacythree-levelfile,region/district/territoryhierarchicalhierarchy via territory_path).assignments_handle mustandbeaddsathemservertouploadthehandleTerritory(aup_...)Solutionfromasrequest_assignment_uploada—newcsv_text/csv_file/assignments,alignmentor(TAL).aAssignmentsPOSTcomeofinlinetheorCSVasfileantoassignments_handleits(aup_...)key-lessfromresult.upload_url.request_assignment_upload.LegacySheetsspreadsheetswith(Postal Code+plus Territory/Region/Division)are auto-mappedcolumnswhenarestaged.mappedConflictingautomatically.duplicateDuplicate part_idsdefault tofollowduplicate_part_policy=keep_firstduplicate_part_policy (firstdefaultwinskeep_first).Prerequisites: part_layer chosen; viewer connected for MC-first; ts/ts_handleReturnsoptional.aNottask;fortheaccountresultpointcarrieslocations—usetal_idingest_accounts.andNotts_handleforandbalancedpaintspartitioning—usetheauto_build.openNotmap.forLocationattributepointsgrouping—useloadaccount_build.withNext:ingest_accounts;verifybalancedTALpartitionsincomeMCfrom(MC-011),auto_build;analyzegrouping+byload_analysis_panel.aScenarios:columnDB-001..005,isMC-011.account_build.
⟨43 unchanged words⟩ ":null,"title":"Expected Revision"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"map_session_id":{"anyOf":[{"type":"string ⟨127 unchanged words⟩
[Tier 1 — Router] Deterministic entry point for ambiguousMapsoraopen-endedfree-text territoryrequests. When: the user's goal or build type is unclear and you want the correct workflow beforerequestacting.toPrerequisites:anoneworkflow.(staticRead-only keyword routing; nocompute).compute. Returnsanintent_category, the recommended tool order, theTier 1main tool+ Tier 2 alternatives(tier1_tool,required_inputs,nullclarifying_questionswhentotheaskrequestwhenisinputsambiguous)areandmissingalternatives,a guidance_urirequired_inputs,(ezt://guidance/workflows/{name})clarifying_questions forthe fullmissingatominputs, and`guidance` —thesame EMEPworkflowatomguideinlinedinline as guidance {uri, title, excerpt, truncated}, so you do NOT need a second resources/read to get the workflow text. Use.itCoverstobuildingdisambiguateterritories (auto_buildvs, account_buildvs, direct_buildand 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 bestcluster_points,orderseed_build)/,reachable-areaediting (a drive-time or drive-distance area around origins — service area, catchmentrealign,coveragesplit,isochronemerge,which routes to isochrone_buildrebalance)/ periodic-scheduling (a recurring cadence — 'every 30 days','twice a month' — and which day each visit lands onanalyze,which routesgeocodingtoandschedule_visitsingest,never auto_build or cluster_points) / push-to-designer (an EasyTerritory Designer rolodex project fromshowing aTS: export_geojson then POST FromTerritorySolution to createZIP orPUT .../Projects/{projectId}/... to update; territories,partpointslayer,androutesimport; ezt_pat_; there is no MCP push tool),/drive-timepull-from-designerareas (a Designer rolodex project into the MCP: GET REST/Agent/Projects thenisochrone_build),GETrecurring.../Projects/{projectId}/TerritorySolutionvisitwithschedulesezt_pat_(schedule_visits),then import_geojson geojson_gzip; thereandismovingnoaMCPTerritorypullSolutiontool).toScenarios:orAB-016,fromACB-003,EasyTerritorywrong-build-tool.Designer.
[Tier 1 — MC-First Gate] When: after get_map_visualization returns map_url andReportsbeforewhetheranythecompute,personbuild,hasanalyze,theormapselectionopenwork(viewer_status.connected)withfor ahuman in the loop. Prerequisites:map_session_id fromget_map_visualization. Next: ingest, configure_map, build, realignget_map_visualization,or analyze once viewer_status.connected. Surface-agnostic: the in-chatwaitingMCPupApptoshellwait_secondsconnects(maxthe30samessessionperovercall; thesame SSE channel, soresultthisreportsgaterequested_wait_secondsworksandunchangedmax_wait_seconds).whetherWorks thehuman is lookingsameatfor the in-chat maporand the map_url tab.Do not skip it on anReturnsAppsVIEWER_NOT_CONNECTEDhost.whilewait_secondstheblocksmapatismoststill30closed;sthepersame call(largercanvaluesbearerepeatedclamped;while theresult reports requested_wait_seconds andpersonmax_wait_seconds).opensOnit.VIEWER_NOT_CONNECTEDBuild,callanalyze,againandwithselectiontheresultssamepaintmap_session_idlivewhileonce theuser opens map_url. Scenarios:viewerMV-001,isI-1.connected.
{"properties":{"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"map_session_id":{"title":"Map Session Id", ⟨25 unchanged words⟩
[Knowledge Retrieval] TraverseTraverses the EMEP knowledge graph from a topic file (e.g. 'workflows/realign-by-selection.md') tofindrelated guidance, up to `depth` hops. Returns ⟨8 unchanged words⟩
[Knowledge Retrieval] ListLists 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.
[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 ⟨31 unchanged words⟩
[TierExports2the—territoryProjectsolutionsave]asTheoneonlyGeoJSONTerritoryfileSolutiontoexport.share,Materializearchive,theorworkingopenGeoJSONinFeatureCollectionEasyTerritory(Designer: territory polygons with part_ids,point featurespoints, androute pathsroutes+with 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. Theeachresultcarryingcarriesitsgeojson_artifactdisplayed{artifact_id,style.bytes}tal_ids,zip (default true = gzip download)point_layers, anddownload_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.route_layersSetnarrowinclude_points=falseit;orinclude_pointsinclude_routes=false/toinclude_routesdropomit a family.Reopen with import_geojson, then get_map_visualization(ts_handle=...). Paint travels with the exportInput:every feature and point_layers[] / route_layers[] entry carries style {color, opacity, size, shapemap_session_id,weight} exactly as shownts_handle,so reopen and Designerorimportaneedcompletednobuildrestyling.job_id.AnReturnsEasyTerritoryaDesignertaskrolodexwhoseprojectresult isthis download POSTedgeojson_artifacttoplusDesigneraREST/Agent/Projects/FromTerritorySolutiondownload_url (new)GETorwithPUTthetoAPI.../Projects/{projectId}/FromTerritorySolutionkey;(update).gzipTerritories,unlesspointszip=false),and routes import.notThereanisinlinenobody.MCPimport_geojsonpushreopenstoolthe(ezt://guidance/workflows/push-to-designer).file.
{"properties":{"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"include_points":{"default":true,"title":"Include ⟨98 unchanged words⟩
[Tier 1 — Delegation Extract]ExtractsWhen:oneseniorregionalplannerbranchsendsof aregional subtreehierarchicaltoalignmenta(bydelegatebranch_rollup_territory_idfororboundedbranch_territory_path)editing.intoPrerequisites:itsmasterownTSTerritorywithSolution,hierarchicalsoTAL;abranchdelegaterollupcanoreditpathjustidentified.thatNext:region.storeReturnsbranchthe extract+plusbranch_metadata;branch_metadata,delegatewhichopensreintegrate_branchMCneedsonlater.extractexclude_lockedonly.(defaultScenarios:true)DL-001,leavesDL-002.locked territories out.
⟨67 unchanged words⟩ ":null,"title":"Expected Revision"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"map_session_id":{"anyOf":[{"type":"string ⟨43 unchanged words⟩
[Tier 1 —Plain-language orchestrator]:Usetakeswhenrequest (theexact granular tool is unclear or when a greedy/file-holding clientgoal)wantsandtorunscompletetheanmatchingactionworkflow in onestep. Prefer granular tools directly when you know thecall,step.optionallyPasswithrequestcsv_file (your goal) plusaoptionalhostcsv_filefile/parameter), accounts_handle/, assignments_handle/, map_session_id,/or ts_handle.For anAn uploaded CSV,pass csv_file as the ChatGPT/OpenAI file parameter; ezt parses/stagesisitparsed,server-sidestaged, andcan run ingest_accounts with map_session_id so points appearingestedononto the open map.For fixed-workload builds, args.target_hourstarget_hours /args.workload_target areworkload_targetnormalizedmap toauto_build build_mode.mode=fixed_workload_target. Modern form-capable clientsa(protocolfixed-workload>=auto_build.2026-07-28)Missingmaybuildanswerinputsmissing(partpart_layerlayer,tal_labelalignment name, sizing,dwellbalance,anddwell)optionalreturnbalanceCLARIFICATION_REQUIREDin-bandwith(alignedthewithquestions.auto_build);ThelegacygranularhoststoolskeepdoCLARIFICATION_REQUIREDthe/sameask_usersteps(HITL-025).individually.
⟨46 unchanged words⟩ ":null,"title":"Csv File"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"map_session_id":{"anyOf":[{"type":"string ⟨43 unchanged words⟩
[Tier 1 —GeocodesGeocodeaOnly]listWhen:ofgeocodecustomer addresseswithout 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 geocodestoinlinecoordinates withaccept_suboptimal_geocodes default true). Use this tool when the user must verify geocode qualitymatchfirst;confidence,setwithoutaccept_suboptimal_geocodes=trueloadingtothemacceptasZIP-centroid/suboptimalamatches.pointAddresslayer.columns:Accepts address/address_line1/street, city, state, and postal columns (common CRM headersauto-mapped). QUOTA: billable provider callsarecapped per API key per calendarmapped).month.accept_suboptimal_geocodes=trueCacheacceptshitsZIP-centroidcostmatches.nothingCachedandaddresses arenever counted, so never set use_cache=false orfree;force_regeocode=truebillabletolookupsworkcountaroundagainst acap — that turns free hits intomonthlypaidper-keycalls.quota,Pastandthepastcapit thetoolresultreturnsis PROVIDER_QUOTA_EXCEEDEDcarryingwith limit, used, remaining, andperiod_resets_at; an operator must raiseperiod_resets_at.theReturnscapgeocoded_rows,so report those numbers instead of retrying or splitting the batch. Next:whichpassingest_accountsresult.geocoded_rowscantoload. ingest_accountsor exportalsopointgeocodeslayer.onScenarios:itsGC-001..006.own.
⟨25 unchanged words⟩ "Force Regeocode","type":"boolean"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"map_session_id":{"anyOf":[{"type":"string ⟨51 unchanged words⟩
[Tier 1 — Startup Guide + Handle]ReturnsCALLtheTHISEZTFIRST.MCPRead-only.operatingWhenguide:session start, aftertheblocked_by=guidance_required,cross-toolorworkflowwhenordertheand shared rulesare unclear. Returns result.text(the short cross-tool briefresult.text, same text asserver instructions), result.guidance_handle (therotating code every territory/map toolserverrequiresinstructions), result.server_version(this,server'sandproductresult.seatversion;for thesame string as serverInfo.version), andcaller'sresult.seatkey (plan standard|trial, key_expires_at, and quota_remaining forgeocode/route/isochrone — tell a trial user when the trial or an allowance isgeocoding,closerouting,toandrunningisochroneoutrequests).Per-tool rules are on that tool's description. WorkflowRead-only.essaysUsefulareatinlinedtheasstartguidanceofonadiscover_intent,sessionworkflow_advisor,orandforblocked_by.aExempt:hostget_guidance,thatsubmit_feedback,doesdiscover_intent,notworkflow_advisor,showep_*servertools.instructions.
[Tier 2 — MC Selection Poll] When: readReturns thelatest committed MC session selection — especially whenmostMonicarecentstartedcommitted selectionfrom the legend finger icon and then tells the agent what toondoanwithopen'mymapselection'session (no prior request_part_selection in this turnmap_session_id). Prerequisites:map_session_id of the open MC. Returnspart_layer+, part_ids,(+and selection_task_id whena 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,thesame map_session_id, and moves=[{part_id, to_territory_id}]selectionderivedcame fromevery returnedrequest_part_selection.part_idCoversusingselections thestable 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).personIfstartedunclear,fromaskthewhichmapleaf.legend.Dorealignnottakesresendthesethepart_idsTSasormovesrequestwith anew selection. Scenarios: MC-004 variants, user-initiatedleafselect.territory_id.
{"properties":{"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"map_session_id":{"title":"Map Session Id", ⟨8 unchanged words⟩
[Tier 1 —OpensMC-FirsttheEntry]interactiveFIRSTterritorystepmapof(Mapnon-headlessComponent)territoryandworkreturns(invariantmap_urlI-1).andWhen:map_session_id.openOpens a blank mapfor planning, oropen/reuseshows theuser's MaplatestComponentstateforof an existing Territory Solution (TSe.g. yesterday's work) from ts, ts_handle, or a completedjob.job_id.PassIdempotentnew_project=truepertouser_id:replaceathesecondlivecallworkspacereturnswiththeansameemptymap.project;new_project=trueomittedreplacesempty+viewthereusesopentheworkspaceexistingwithproject.anOptionalempty project. 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):selection. Apps-capable hosts renderthisthe map in chatfrom(ui://easyterritory/map-viewer(_meta.ui.resourceUri);everyotherclient — including Cursor —clientsopensopen map_url in abrowser tab. map_url is always returned. Do not open a second surfacebrowser.orLatercallmapthistoolstooltakeagainmap_session_id.toensure_map_viewer'fix'reportsawhenmap.theScenarios:personMC-000,hasMC-001,theMV-001,mapS004.open.
⟨20 unchanged words⟩ ":null,"title":"Expiry Seconds"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"interaction_flags":{"anyOf":[{"additionalProperties":true, ⟨85 unchanged words⟩
[Tier 2 — Selection Poll] When: poll or retrieve committed part IDs afterReturnsrequest_part_selectiontheORstateafterofMonicaastartsmap selectionfrom the MC legend finger icon. Prerequisites:task (selection_task_id from request_part_selection, orfrom map session state (active_selection_task_idonin ezt://map-sessions/{id}/state). Poll loop:while status=awaiting_user_selection, sleep recommended_poll_delay_ms (fallbackstatuspoll_interval_msawaiting_user_selection,min 250ms) and poll again until status=committedcommitted, expired, orcancelled. Response includes do_this_nextcancelled, andpoll_loop while awaiting. If the user alreadyonce committedand says 'add mytheselectionselectedtopart_ids…',(withcallassignedonceandforunassignedthepart_idscommittedwhentaska(orTALuseisget_map_selectionactive)and.proceedWhile—awaiting,dothenotresponsestartcarriesarecommended_poll_delay_ms.newCommittedselection.part_idsNext:feed realign, create_territory_from_parts, oranalyze scoped to selection.part_ids. Scenarios: AN-007, RL-008.analyze.
{"properties":{"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"selection_task_id":{"title":"Selection Task Id", ⟨8 unchanged words⟩
[Tier 2 — Project reopen] ImportOpens a GeoJSON FeatureCollection (standardplain points, oranaexport_geojsonterritoryartifact,fileincludingthisgzip/zip).serverReturnsproduced,ts_handleplain+orsummarygzip/zip)—aspassats_handleTerritorytoSolutionget_map_visualizationand/returnsanalyzets_handle/andexport_geojsonanext;summary.doInput:notgeojsonrepost(object),thegeojson_text,GeoJSONorbody.geojson_gzipArbitrary(base64).territoryTerritory polygonsWITHOUTwithout part_idsare rejectedreturn(TERRITORY_POLYGONS_WITHOUT_PART_IDS)unlessyou explicitly passpart_layer plusoverlay_policy="centroid_within" tooverlay_policy=centroid_withinassignassigns partswhose centroids fall inside each polygon.byOptional:centroid. point_layer(name fornames importedpoints), tal_label. Supply geojson (object), geojson_text (JSON or sniffed gzip/zip), orpoints;geojson_gziptal_label(base64namesgzip/zip).theAnalignment. EasyTerritory Designerrolodex project arrives here too: GET Designer REST/Agent/Projectsprojects (list) then GET .../Projects/REST/Agent/Projects/{projectId}/TerritorySolutionwith the user's ezt_pat_ Bearer, and pass the (gzipped)fileimport as geojson_gzip.There is no MCP pull tool (ezt://guidance/workflows/pull-from-designer).
⟨31 unchanged words⟩ ":null,"title":"Geojson Text"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"overlay_policy":{"anyOf":[{"type":"string ⟨44 unchanged words⟩
[Tier 1 —AccountDataingest:Intake]loadsWhen:accountload/addoraccount/locationlocation rowsor(CRMaccountrecords, a CSV, a spreadsheet) as point dataintoon aTSnamed pointlayer. ALWAYSlayerbeforeofaccount-derivedthebuildsTerritory(auto_buildSolution,account_build). Prerequisites: MC-first + viewer connected whentohumanaddverifiesthempoints;tots/ts_handletheoptional.map,Geocodesgeocoding rowslacking valid coordinates (accept_suboptimal_geocodesthatdefaultslacktruecoordinates.forRowsbulkcomeaddress-onlyinlineCSVs(rows).Recognized addressorcolumns:asaddress/address_line1/street,accounts_handlecity,fromstaterequest_account_upload,postal columns,whichplushandlescommonlargeCRMfiles;headersa(e.g.clientAddressfile1:pathStreetis1).notCoordinatereadablepassthroughbyacceptsthecase-insensitiveserver.GISCoordinate headerslatitude/lat/LATlatitude/lat andlongitude/lon/lng/long/LON (no rename required);longitude/lon/lng passexplicit latitude_field/longitude_field forthrough; otherheaders (e.g. Addressnames1:needLatitude).latitude_field/longitude_field,Ifand unusednumeric lat/lon-likecoordinate-like columnsexist and ingest would otherwise bulk-geocode, the job fails fast withreturn 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(needs_coordinate_fields)geocode_query_countinsteadonof afile that had coord-like columns, call tasks/cancel or tasks_cancel and fixbulkheaders/fields.geocode. id_fieldmust beis aUNIQUE businessunique 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, …synthesized whenrow_id isabsent)or synthesize a unique row_id client-side before staging. Before this call, inspect headers/samples for metric, dwell-time,;andduplicatesvisit-frequencyreturncolumns.needs_unique_row_id.PassOnlyconfirmeddeclaredbusinesscolumnsmetricsareaskept: metric_fieldsand 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 idworkload_fields,coordinatesdwell_time_field,labelvisit_frequency_field,and declared fields. Also declaregrouping_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 andsearch_fields; lateroperations 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 insteaduse ofrows. 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. WITHOUTanmap_session_idundeclaredthecolumnpointsreturnslandUNDECLARED_FIELD.inReturns aDETACHED 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 calltask; theindicatedresultoperation once and inspect the terminal one-liner field, viewer_outcome,reports 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=truemap_refresh, andthe 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 ANDanyexpected 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 resultgeocode quotablock 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.shortfall.
⟨76 unchanged words⟩ ":null,"title":"Grouping Fields"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"id_field":{"default":"row_id","title": ⟨182 unchanged words⟩
[Tier 1 — Isochrone Build] When: drawDraws the area reachable byDRIVINGdriving from one or more origins within a time or distance budget— '20-minute(drive-time areaaround each branch', service area, catchment, coverage ring, reachable range, isochrone, isodistance (IS-001..IS-006).It draws reachable area. It has no objectiveanddoes notaddsbalanceeachworkload.areaPrerequisites:as aTomTom or Azure Maps key on the server (no key → ISOCHRONE_NOT_CONFIGURED, no degraded mode — neverterritorysubstitutein astraight-line radius); and, for point-layer origins, ingested points plus ts_handle or map_session_id.drive-timeInlinealignment. originsneed no TS. No part layer is involved. ORIGINS x BANDS:origins areinline {latitude, longitude, label?} or a point-layerreference {point_layer, point_ids?, filter?, label_field?} — '40 minutes around each of my 6 branches' is ONE call.reference. bandsare: [{time_budget_seconds}|or {distance_budget_meters}], onebudgetunit perband, and every band in acallmust use the SAME unit(mixing returnsMIXED_BUDGET_TYPES otherwise).;Oneone band→gives oneleaf per origin; several bands → a rolluparea per originwith one leaf per band. Cost is origins x bands provider calls,capped by TOO_MANY_ORIGINS (default 25) /severalTOO_MANY_BANDSgive(defaultnested5).areas.QUOTALimits:those billable calls also draw on a per-API-key monthly cap. The whole fan-out is claimed before any call goes25outorigins,so a build that does not fit returns PROVIDER_QUOTA_EXCEEDED with5limitbands,used,6remaininghours,and period_resets_at having spent nothing. An operator must raise the cap; report those numbers instead of retrying with fewer bands.500LIMITS:km; travel_modeis 'car' or 'truck' ONLY — neither provider offers a walkingcar orcycling reachable range, unlike calculate_route. time_budget_seconds <= 21600, distance_budget_meters <= 500000 (BUDGET_OUT_OF_RANGE).truck; depart_atis supported; arrive_at is not. avoid accepts toll_roads, motorways, ferries, unpaved_roads, carpools, border_crossings, tunnels, car_trains, low_emission_zones. GEOMETRY: eachsupported.territoryAreasISare theprovider's exact polygon — no part layer, no part_ids. Bands nest and nearby origins'provider polygonsoverlap,andthe map paints them overlapping (tightest onmaytop).overlap; analyze countsan accountaccounts inEVERYevery areathat contains it (a 20-minute band includes its 10-minute accounts) and reports the accounts overlapping areas share. realign / territory_split / territory_mergecontaining/them.territory_rebalancerealign/split/merge/rebalance returnISOCHRONE_TAL_NOT_EDITABLE: re-run isochrone_build to change an area; delete_territory / delete_tal still remove one.ISOCHRONE_TAL_NOT_EDITABLE. A band budget isNOTnot territory workloadand NOT route drive time — never report '20-minute band' as 20 minutes of workloadorfeedrouteittime.toNoauto_build.providerAsync:key returnstask_id — follow _meta.next_action (sleep_and_poll → consume_result) and sleepISOCHRONE_NOT_CONFIGURED; theauthoritative sleep_ms, never estimated_remaining_ms.fullPartialcallprovidercountfailureisstillcheckedbuildsagainst theTAL and reports failed_requests[]. Then: analyze(tal_ids=[<tal_id>], map_session_id, analysis_panel='single') for per-area accountsmonthlyandquotametricsfirst (needs an ingested point layerPROVIDER_QUOTA_EXCEEDED).Pass map_session_id here to paint the areas on an open map. Full atom: ezt://guidance/workflows/isochrone-territories.ReturnsScenarios:aIS-001..IS-006.task.
⟨31 unchanged words⟩ ":null,"title":"Depart At"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"map_session_id":{"anyOf":[{"type":"string ⟨81 unchanged words⟩
[Tier 2 — Analysis Panel Display]ShowsWhen:ashowcompletedAnalyzeanalyzeJSONresult in theMCopenbottommap's analysis dock (single orcomparisoncompare)after a completed analyze that omitted analysis_panel. Prefer.analyze(analysis_panel=single,Needsmap_session_id=...)map_session_idsoand thedock fills without this second call. Prerequisites: map_session_id; a full Analyze result — passanalyze task_id/job_idofor thecompletedfull analyzejob, or analyses=[<full Analyze result>]. Theresultmust include(with tal_analyses andts_identity (territory_metric_grids when metrics exist). Ats_identity;thina{tal_analyses}partial objectisreturns INVALID_REQUEST, not an empty dock.). presentation_modeis single|compare|executive|diagnostic — territory_metric_grid is default_presentation.view inside the Analyze:payloadsingle,not a presentation_mode. Does not mutate TS. Re-run analyze after anycompare,TALexecutive, orpoint change (I-2).diagnostic. dismiss=true hides thewholedock,header bar included,and keeps thestatsstatistics on the session.analysis_panel.dismissedChangesisnothingthenintrue.theATerritorylaterSolution. analyze with analysis_panel=singleshows the dock again. Omit analyses on a dismiss call. Dual surface:fills the dockrendersin thein-chat map on Apps-capable hosts and in the map_url tab everywhere else —samemap_session_id, no extracall.Scenarios: MC-009, AN-006, MC-007.
⟨20 unchanged words⟩ ":"Dismiss","type":"boolean"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"job_id":{"anyOf":[{"type":"string ⟨68 unchanged words⟩
[Tier 2 — Part Metadata]ReturnsWhen:attributesenrich(noorgeometry)filterfor partsbefore build, or inspect attributesofwithoutageometry.partPrerequisites:layerpart_layerlistedfromat ezt://part-layers.PassTakes exactly one of filter (e.g. {state_abbr: TX}) or part_ids; neither or bothisreturns INVALID_REQUEST.To check that a part layer exists, read ezt://part-layers instead of probingPaged withan empty query. Next: direct_build, configure_map, or agent-side join before build. Scenarios: DS-002. Returns part_id and attributes only; no geometry.page_token.
⟨9 unchanged words⟩ "default":null,"title":"Filter"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"max_results":{"default":100,"title":"Max ⟨40 unchanged words⟩
[Tier 1 —MovesRealign]ZIPsWhen:ormoveother partsbetween leaf territories within ONE TAL. Prerequisites: existing TAL plus ONE current TS(typedreference:idsmap_session_idor(preferredonesforselectedanonopentheMCmap),ts_handle,fromoronets;territoryexpected_revisionintoisanotheroptionalwithinoptimisticoneconcurrencyalignment (STALE_TS_REVISION → reload and retrytal_id). For: acommitted ad-hoc map selection, call get_map_selection(map_session_id), thenZIPpassmove. moves=[{part_id, to_territory_id}]for each returned,selection.part_idsorplusrealign_operationtal_idwithandpart_ids.theThesameTerritorymap_session_id.SolutionDocomesnotfromrepostmap_session_id (thefull TS and do not startopenanothermap),selection.ts_handle,to_territory_idormustts;beexpected_revisionaaddsleafoptimisticterritory_idconcurrency (prefer created_territory.territory_id /STALE_TS_REVISIONleaf_territorieswhen/thelegendmapcatalogchanged).Ato_territory_iddisplay-nameisaliasaisleafacceptedterritory_idonlyorasthatanleaf's exact, uniquematch to the leaf's actual namedisplay(e.g.name;'Territoryan3'unknown or'T1' when that is the name) — do NOTambiguousinventvalueshorthandreturnsexpansionsUNKNOWN_TERRITORY_ID(T3or≠AMBIGUOUS_TERRITORYTerritorywith3;available_leaf_territories.noAT→Territorycommittedmapping).mapItselection isNOT tal_id. On UNKNOWN_TERRITORY_ID / AMBIGUOUS_TERRITORYreaderror.details.available_leaf_territories andwithstopget_map_selection;guessing;request_part_selectioniflets thespoken destination ispersonstillpickunclear,partsaskon theuser which leaf (id or exact name). Visual moves with a destinationmap.knownremove_empty_territoriesindropsadvance:leavesrequest_part_selectionleft withpurpose=realign. Structuralnorebalance:parts.territory_split/merge/rebalance.ReturnsNext:averifytask; theliveopen maprefresh. Runrepaints. analyze+ load_analysis_panelrefreshesonlystatistics when theTSmap hasa 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,accountEV-001.points.
⟨8 unchanged words⟩ ":null,"title":"Expected Revision"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"map_session_id":{"anyOf":[{"type":"string ⟨160 unchanged words⟩
[Tier 1Merges—anDelegationeditedReintegrate]branchWhen:(proposalmergeTerritoryanSolutionapprovedfromproposalextract_tal_branch)TSback into the master.Prerequisites:Needs branch_metadata,expected_master_revision;andproposalexpected_master_revision;TSreturnsatSTALE_TS_REVISIONcurrentwhenlineage.theSTALE_TS_REVISIONmasterifchangedmastersincemoved—requestthefreshextract,extract.inSequentialwhichmergescaseonly.aNext:freshrefreshextractmasterisAnalysisneeded.panelMerges(I-2).applyScenarios:oneDL-003,atDL-004,aDL-006.time.
⟨25 unchanged words⟩ Master Revision","type":"integer"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"map_session_id":{"anyOf":[{"type":"string ⟨65 unchanged words⟩
[Tier 2 — Data Intake helper] When: you have account/location rows or anStages accountCSV that you cannot inline in a single ingest_accounts call (e.g. a large CSV, hundreds/thousands ofrows).Stage the rows here in one or more chunks, then callfor ingest_accounts(accounts_handle=...).If the file includes metric, workload, visit-frequency, or dwell-time fields, stage the fileandherereturnsfirstupload_handle,inspect returnedcsv_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. WAYS TO STAGE (all return an upload_handle): (A) CSV on disk +andasuggested_ingestshellargs withnetwork (Cursor, ClaudeaCode,geocode_plan.Codex,FourCLIwaysagents)in:call with no rows to get result.upload_url, then run result.curl_example —POST therawCSV filewith Content-Type: text/csv. NotoAPIthekey:returned upload_urlis(notheAPIcredentialkey;(expires in 15min; each POST appends to the same handle).minutes;Fastestfastest for largefiles; do not re-type rows into csv_text or rows chunks. (Bfiles)MCP, csv_file:pass the uploaded CSV as(aChatGPT/OpenAIhost file parameterwhen available. (C)MCP, csv_text:pass csv_text=<raw CSV string>(whole file in onerawcallCSV, up to 16 MB) when you have no shell or no network. Preserve multiline, newlines— do NOT JSON-encode with PowerShell ConvertTo-Json (that corrupts rows into a single linepreserved). Use Python json.dumps, orMCP csv_file instead. (D) MCProws:call with rows=<chunk>; appendchunksmoreappended withupload_handle=<prior handle>.upload_handle. A local path in csv_filefails (UNREADABLE_FILE_REFERENCE); use (A). Every result carries a fresh upload_url for itsreturnshandle;UNREADABLE_FILE_REFERENCE; an expired URL returns UPLOAD_TOKEN_EXPIRED—andcallaagaincall with upload_handleforissues a new one.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.TheHandlehandle is single-use (claimed bythe ingest jobingest_accounts) and expires(defaultafter1h).1Scenarios:hourIA-001..005,byS003.default.
⟨23 unchanged words⟩ ":null,"title":"Csv Text"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"rows":{"anyOf":[{"items":{" ⟨38 unchanged words⟩
[Tier 2 — Direct Build intake] When: you haveStages part-to-territory assignment rowsor(e.g. alegacyZIP-to-territory spreadsheet(e.g. Zip2Terr.csvwith Postal Code+plus Territory/Region/Division)that you cannot inline in direct_build. Stage rows here, then callfor direct_build(assignments_handle=<upload_handle>).WAYS TO STAGE (all return upload_handle): (A)andCSVreturnsonupload_handle.diskSame+fourawaysshellinwithasnetworkrequest_account_upload:call with no rows to get result.upload_url, then run result.curl_example —POST therawfilewith Content-Type: text/csv. No API key:to upload_urlis the credential(expires in 15 min;noeachAPIPOSTkey;appends).15(Bminutes)MCP, csv_file: pass the uploaded CSV as a ChatGPT/OpenAI file parameter. (C) MCP, csv_text:pass csv_text=<raw multiline CSV> — preserve newlines; do NOT JSON-encode with PowerShell ConvertTo-Json(corrupts rows).newlines(Dpreserved)MCP assignments: pass assignments=<chunk,oforrowassignmentobjects>;chunksappendappended with upload_handle.An expiredLegacyupload_urlcolumnsreturnsmapUPLOAD_TOKEN_EXPIREDto—part_idcallandagainterritory_pathwithautomatically.upload_handle.ALegacyfilecolumnsof(PostallocationsCode,ratherTerritory,thanRegion,assignmentsDivision)isauto-mapredirected topart_id + territory_path. Then: direct_build(assignments_handle=<handle>, part_layer=..., tal_label=...,request_account_upload.map_session_id=...).TheHandlehandle is single-use and expires(defaultafter1h).1 hour by default.
⟨38 unchanged words⟩ ":null,"title":"Csv Text"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"ttl_seconds":{"anyOf":[{"type":"integer ⟨22 unchanged words⟩
[TierLets2the—personHumanadjustSpatialboundariesInput]byWhen:pickingMonicapartsselects(e.g.partsZIPs) on the mapfor 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 askputs thehuman to type committed/done/go. User-initiated path: Monica mayopenalsomapstartinto select modefrom the MC legend finger iconon apart-layerpart_layer,rowfor(nopurposepriorrealign,request_part_selection).aWhennewtheterritory,userorrefersatoplain'mylist.selection'Returns/selection_task_id;'theget_part_selectionpartsreportsIstatusselected'(awaiting_user_selection,read the open map session's latestthen committedselection via,get_map_selection(map_session_id)expired, orget_part_selectioncancelled)usingandactive_selection_task_idthefromcommittedezt://map-sessions/{id}/statepart_ids,—anddoitsnotresponseaskcarriesthemrecommended_poll_delay_ms.toTheselectcommitagain.happensNext:onrealign,thecreate_territory_from_partsmap,or analyze with selection.part_ids.notDualinsurface:chat.indestination_territory_idanandApps-capablerealign_operationhostpre-settheahumanrealign.selectsWorks on the in-chat map(ui://easyterritory/map-viewer); otherwise they select inand the map_url tab.The commitApollselectionlooptheabovepersonisstartedidenticalfromonthebothmapsurfaces.legendScenarios:isS001,readMC-004..006,withRL-006..013.get_map_selection.
⟨32 unchanged words⟩ ":null,"title":"Expiry Seconds"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"new_territory":{"anyOf":[{"additionalProperties":true, ⟨81 unchanged words⟩
[Tier 1 — PeriodicSchedulesScheduling]recurringWhenvisits:accounts carryexpandsaeachrecurringaccount's cadence (e.g. every 7 days, twice a month)and the user asks which day each visit happens. Expands demandacross arepeatinghorizon and packsitthe visits into daily work clusters (buckets) underonea technician-dayworkloadcap.ThisDecidestoolwhichdecidesdaytheeachday.visitNeighbors:happens (auto_build,cluster_points, calculate_route. When the user said routeanda visit-frequency column exists, confirm they want a schedule,cluster_pointsnotdividecalculate_route,accountsbeforeamongcalling.peopleItinstead); creates noTALalignment and assigns no technician.Prerequisite: ingest_accounts completed forNeeds point_layer (ingested, with the cadence columndeclared. 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.declared,Required:elsepoint_layerUNDECLARED_FIELD), visit_frequency_field, dwell_time,daily_capacity. Askandthedaily_capacityuser(e.g.for{mode:dwellnot_to_exceed,andhours:the8});dailymissingcap;dwellneverorinventcapacitythem.returnsfrequency_unit=interval_daysCLARIFICATION_REQUIREDmeanswith thevaluequestion.isfrequency_unit:theinterval_daysmaximum(max 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. Valuesorinvisits_per_horizon. never_visit_values (default 0, null, empty) are excluded andreportedlisted inexcluded_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.excluded_accounts. bucket_workload_hours is in-bucket drive plus dwellwith NO visit-frequency,multiplier:aitdifferentismeasureneitherfrom territory workloadnor route_workload_hours — never sum or compare them. Re-runningandwithroute_workload_hours.theThe same visit_layer_name replacesthat schedule inaplace;schedule; adifferentnew name addsa 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 stopone.onReturnsstop_error.aNext:task. calculate_route overthe visits layer filtered toone bucket_idto drivedrives that day.Full atom: ezt://guidance/workflows/periodic-visit-schedule. Scenarios: PS-001..PS-008.
⟨63 unchanged words⟩ "Frequency Unit","type":"string"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"horizon":{"anyOf":[{"additionalProperties":true, ⟨124 unchanged words⟩
[Tier 1 — Seed Build] When: growGrowsONEone territory outward from a seedlocation 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 coordinatesoverfirstawithpartthelayeraddressuntilgeocodingittool;holds amap click already gives coordinates. TARGETtarget:target={type:'location_count'location_count, value: N} or {type:'metric_sum'metric_sum, field:<column declared in metric_fields at<declaredingest>metric>, value: X}. An undeclared field returns UNDECLARED_FIELD — re-ingest,withcountedmetric_fields.fromFitanisingestedCLOSEST:pointgrowthlayer.addsAdds thenearestterritoryadjacentaspartathatleafstilloffitstal_id,then takes the smallest remaining neighbour only when overshootingorlandsasnearerathenewtargetalignment.thanFitstoppingisshort.closest: target_statusreportsis reached|, closest_under|, closest_over|, frontier_exhausted| max_parts_reached; a closest fit that misses the target also warns SEED_TARGET_UNDERSHOT,/orSEED_TARGET_OVERSHOTmax_parts_reached, withthe signed differenceSEED_TARGET_UNDERSHOT/OVERSHOT—warningstelland theuser, parts are indivisible. NO OVERLAP, EVER: with tal_id,difference.partsParts already inany territory ofthatlayeralignment are nevertaken —taken; thenewterritorydrifts awaygrowsfromaround theminstead(blocked_part_count, seed_offset_km).,Aand a seed inside anexisting territory fails SEED_PART_ASSIGNED; pick anotherassignedseedpartorreturnsreassignSEED_PART_ASSIGNED.partspart_scope=explicit withrealign. There is no allow_overlap flag.part_filterSCOPE:confines growthreads 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 locationtoevery part, so a national point layer costs the same as a local one. part_filter / part_ids areanALLOWLIST (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 oncearea.perOne seedwith the same tal_id; earlier territories becomeperblockedcall.forAnlaterundeclaredseeds.metricAsync:field returnstask_id — follow _meta.next_action (sleep_and_poll → consume_result) and sleep the authoritative sleep_ms. The result is handle-only (ts_handle,UNDECLARED_FIELD.noReturnsinlineats)task with created_territory,the grown part ids, target.{requested, achieved, delta},anddo_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 onts_handle; the openmap. Full atom:mapezt://guidance/workflows/seed-grown-territory.repaintsScenarios:withSB-001..SB-004.map_session_id.
{"properties":{"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"map_session_id":{"anyOf":[{"type":"string ⟨155 unchanged words⟩
[Tier 2Changes—theLow-LevelopenMCmap'sState]sessionWhenstate:switch MCmode, activeTAL,alignmentor(active_tal_id), pending jobrefreference, orjump the open MCcamerawith(center([longitude, latitude])and/orand zoom(0-24).CameraSessionhereonly:isthesession-onlyTerritoryandSolutiondoesis notwrite the TS — usechanged (configure_mapto persistsets adefault view. Prerequisites: map_session_id. Prefer configure_map fordurableTS map_config. Prefer request_part_selection for selectiondefaultworkflows.view). The result's render_ack is readthe instant the changeatispublishpublishedtime, sostate=sent_unconfirmed with applied_render_versionsent_unconfirmed one version behind is normal;torender_ack_hintconfirmsaysthewhetherpaint,toreadreread ezt://map-sessions/{map_session_id}/statea secondortwo later. Only a sent_unconfirmed that persists with a render_ack failure detail means the viewer could not apply it; render_ack_hint says which case applies (reread_state or resend). Once youresend.setcameracenter/zoom,reports theopen MC keeps thatview(no automaticinrefit)effect anda reload of map_url reopens there. Every result carries camera {center, zoom,its source}:requested(this call)requested, session(an earlier set_map_state), map_config(the TS default view), or viewer_current(no camera known;).theAMCsetkeepscameraitsstaysviewuntil—changed; switchingactive_tal_id never refits). Scenarios: residual backlog (no dedicatedalignmentscenariodoesbynotdesign).refit.
⟨22 unchanged words⟩ "default":null,"title":"Center"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"map_session_id":{"title":"Map Session Id", ⟨42 unchanged words⟩
[Tier 1 — Map Overlay] When:Showscustomerorwantshides something on themap — US ZIP codes, counties,openaccounts/points,maporfrom aterritory alignment — in anyplainphrasingrequest ('add US zipcodescodes',to'hide themap'zips', 'showzip 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 themymap'accounts') or overlay_kind (part_layer|, point_layer|, tal|, route) withoptionaloverlay_id.Resolves the four MC overlay families and calls the correct underlying step (configure_map for part layers and active TAL; pointPart layersmust already be in the TS from ingest_accounts). Source the TS via ts_handle, inline ts, OR map_session_id (preferred for points: reads theloadLIVEatsessionaTSzoomsowherepointstheypushedarebyvisibleingest_accounts(map_session_id=...visibility.camera_action)are found without;threadinghiding anew ts_handle). Returns overlay_kind, overlay_id, viewer_hint, and forpartlayers a visibility block (state, min_zoom, camera_action). An open map_session_id zooms the MClayertoleavesmin_zoomterritories(camera_action=fit_to_visible).unchanged.WhenPointthelayersusermust alreadynamed 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 againbeunlessingested; alater 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 thelayerisin theTSTerritory Solution but missing from thesession, the tool pushes a refreshmapandisreturnsre-pushed (status=refreshed, witha map_refresh block — check itsrender_ackbefore claiming points are visible. PART LAYERS are never loaded by a build; they show only when asked. 'Hide/remove the zips' returns status=hidden and takes that layer off the map (territories unchanged).ROUTE overlay (overlay_kind=route) needs map_session_id and onlyroute re-shows or hides routes calculate_routealreadydrew— 'hide/remove/clear the route'(route_idclearstargetsthem;one); with no route yet it returnsblocked_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_routeblocked_by=needs_route.withWhen thesame route_id. calculate_route already drawsuseritsnamedownaresultcamera,so this is recovery,do_this_nextnotcarries thenormal path. Prefer this over rawconfigure_mapfor 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 latercalllinkedthatingest_accountssetskeepsit.thoseInput:overlaysmap_session_id (non-destructive compose with pointspreferred). 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-010ts_handle,ChatGPT/externalorMCP.ts.
⟨20 unchanged words⟩ ":null,"title":"Expected Revision"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"map_session_id":{"anyOf":[{"type":"string ⟨90 unchanged words⟩
[Tier 2Sends—productQualityfeedbackFeedback]toWhenEasyTerritory:workflowa blocked,or partial workflow,workaroundarequiredworkaround,confusingatoolconfusing response, ormissing capability/docs. Prerequisites:anone.missingNext:capability.continueReturnsoranendacknowledgementsessionwithafterfeedback_id.acknowledgement.Secrets,Scenarios:emails,GC-006.TerritoryNoSolutionsecretsJSON,credentialsGeoJSON,raw customer data,andoraccount/CSVPII.tablesDoarenotstrippedpastebeforeTerritorystorage;SolutiontheJSON,textGeoJSONisFeatureCollections,meantorforaccount/CSVatablesdescription—ofthosewhatblockshappened,arenotstripped.data.
[Tier 2 — MCP Tasks mirror] Tool-only clientTool equivalent ofnative tasks/cancel. Use only when thisthemirrornativeappearsMCPintasks/cancel,tools/list;listedTasks-capablefor clientsusewithouttasks/canceltheinstead.TasksPrerequisite:capability.task_idRequestsfromcooperativethecancellationimmediatelyofprecedingaasynctask_idsubmission.andReturnsreturns thesameTask statusfields and EZT poll metadatadocument.asWhether thenative method.TerritoryCancellationSolution iscooperative; confirm TSunchanged orpartialpartiallyperupdated follows the originatingtool contract. Scenarios: OP-001.tool.
[Tier 2 — MCP Tasks mirror] Tool-only clientTool equivalent ofnative tasks/get. Use only when this mirror appears in tools/list; Tasks-capable clients use tasks/get. Prerequisite: task_id fromtheimmediately preceding async submission. Polling never starts work. next_actionnativeandMCPsleep_mstasks/get,sitlistedonforthisclientsdocumentwithout(thesameTasksfieldscapability.asReturns thesubmit result) andTaskarestatusrepeateddocumentunderfor_meta.aFollowtask_id. next_actionexactly:is sleep_and_pollmeans sleep the single authoritative(wait sleep_msvalue,call tasks_get once,thenre-read bothonefields;tasks_get), consume_resultmeans call(one tasks_resultonce now; stop_errorcall),meansorstop.stop_error. status=cancelledis a user stop — do not resubmitmeans thesame tool unless asked. Never sleep on estimated_remaining_ms and never poll HTTP /resultuserforstoppedstatus.it. Whilea task is stillqueued, _metaalsocarries 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_mswhich is advisory andnevernot asleep duration. The result is the same Task status document returned by native tasks/get.waitScenarios:time.OP-002,Pollingtool-onlyneverMCPstartsclients.work.
[Tier 2 — MCP Tasks mirror] Tool-only clientTool equivalent ofnativethetasks/result.nativeCallMCPexactlytasks/result,oncelistedafterfortasks_getclientsreturnswithoutstatus=completedtheandTasks_meta.next_action=consume_result.capability.Prerequisite:Returnstask_idthefromoriginatingthattool'scompletedterminalTask.resultReturnsonce theoriginating tool'staskterminalstatushandle-onlyisresultcompleted (consume_result): ts_handle, counts, timings,mapandbinding/shortcut);mapitbinding, neverreturnsan inlineTSTerritory Solution when a handle exists.Do not poll this operation while working. After build results,Apassbuild's ts_handle orthe sametask_id (as job_idto analyze with explicit tal_ids; never resubmit a build to recover)fromisantheAnalyzeinputlookupanalyzeerror.takes.
[Tier 1 — Minimal-Disruption Merge]MergesWhen:amergepairtwoof adjacent territories andrebalance with minimal disruptionrebalances—theincludingrestaofrepthewhoalignmentquitswithorminimalleavesdisruption (their territory is absorbedNbytoitsN-1neighborsterritories),ande.g.goingwhenfromaNreptoleavesN-1andterritoriestheonneighborsanabsorbexistingthealignment.territory.Passterritory_id_b is the territory that goesaway asaway;territory_id_bterritory_id_aandisonean adjacent neighboras territory_id_a (itthat keeps itsid);id.theThe same jobthenrebalances every remainingterritory, so do not follow it with territory_rebalance. Not delete_territory (that leaves the parts unassigned) and not a new auto_build (that discards the alignment).territory. One call removes oneterritory. On NON_ADJACENT_TERRITORIESterritory;picknon-adjacentanotheridsneighbor.returnPrerequisites:NON_ADJACENT_TERRITORIES.adjacentNeedsterritory_id_asource_tal_id andterritory_id_b in source TAL. OpentheMCTerritorypreferredSolutionargs:from map_session_id+ source_tal_id + territory_id_a/b (live session TS is authoritative — do not repost GeoJSON). Headless:, ts_handle, orCompact/GeoJSON ts. Modern form-capable clients may be prompted in-band for missing ids /ts; workloaddwell; legacy hostsbalancekeepneedsCLARIFICATION_REQUIRED.dwell_timeNext:(CLARIFICATION_REQUIREDverifyotherwise).mergedWritesTALainnewMC,derivedanalyze.alignment.Scenarios:ReturnsMG-001..005,aEV-001.task.
⟨32 unchanged words⟩ ":null,"title":"Dwell Time"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"map_session_id":{"anyOf":[{"type":"string ⟨186 unchanged words⟩
[Tier 1Rebalances—anMinimal-DisruptionexistingRebalance]alignmentWhen:afterunderlyingthe account data changedbut,alignmentkeepingshoulditstayas close tocurrent (MDR). OpentheMCcurrentpreferredalignmentargs:asmap_session_idpossible.+Needs source_tal_id(+ objective/dwell) —and thelive session TS is authoritative; do not invent a ts_handle or repost multi-MB GeoJSON when a map sessionTerritoryisSolutionopen.fromHeadless:map_session_id, ts_handleor Compact TS v2 / GeoJSON ts (both valid). Prerequisites: source TAL,refreshed point_layer; viewer connected fororMC-first.ts. point_layermay bedefaultsomitted:to thesource TAL'salignment's buildpointlayer,elseor theTS'sonly pointlayer, islayer;used.otherwise CLARIFICATION_REQUIREDdetails.reason=ambiguous_point_layer(pass point_layerambiguous_point_layer,fromwith details.point_layers).orWorkloadzero_workloadbalance(workload_bias>0needsbutdwell_time; zero_workload means no account maps to apart — check point_layer and dwell; never rerun as is).part. Thesourcecurrentassignmentalignment is thenon-regressionbaseline:if no generatedwhencandidatenothing improves therequestedobjective,thederived TAL stays unchanged and returns NO_IMPROVING_REBALANCE_FOUND. Read solver_diagnostics forresultsource/generated/acceptedisobjectivesNO_IMPROVING_REBALANCE_FOUND andconvergence. Modern form-capable clients may be prompted in-band for missing source_tal_id / uninferable part_layer /nothingunresolvedchanges.workloadWritesdwell;alegacynewhostsderivedkeepalignment.CLARIFICATION_REQUIREDReturns(HITL-025).aNext:taskreportwith retention_pct anddiagnostics, then analyze before/after with analysis_panel=single. Scenarios: RB-001..005, EV-003.solver_diagnostics.
⟨32 unchanged words⟩ ":null,"title":"Dwell Time"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"map_session_id":{"anyOf":[{"type":"string ⟨162 unchanged words⟩
[Tier 1Territory—split:Minimal-DisruptiondividesSplit]oneWhen:territorysplit(e.g.onean oversizedterritoryone)withintominimaltwodisruptionwhiletochanging the rest of thealignment. Prerequisites: existing TAL with target territory; vieweralignmentconnectedasforlittleMC-first.asOpenpossibleMC(Npreferredtoargs:N+1map_session_idterritories).+Needs source_tal_id+and target_territory_id(live,sessionplusTStheisTerritoryauthoritativeSolution—fromdomap_session_idnot(therepostopenGeoJSONmap). Headless:, ts_handle, orCompact/GeoJSONts.Distinct from auto_build Scoped Split (balanced-from-scratch).WorkloadModernbalanceform-capableneedsclientsdwell_time;mayabemissingpromptedidin-bandorfordwellmissingreturnssource/targetCLARIFICATION_REQUIREDidswith(andtheunresolvedquestion.workloadWritesdwell);alegacynewhostsderivedkeepalignment;CLARIFICATION_REQUIRED.theNext:sourcecomparestays.derivedReturnsTALaintask;MC,theanalyzeresultdisruption_summary.carriesScenarios:disruption_summarySP-001..004.(retention_pct).
⟨32 unchanged words⟩ ":null,"title":"Dwell Time"},"guidance_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Guidance Handle"},"map_session_id":{"anyOf":[{"type":"string ⟨186 unchanged words⟩
[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 wantReturns thevalidatednext tool callinsteadforofaguessingmulti-steptheworkflow,order.givenPrerequisites:wherenonethe(staticworkflow stands. Read-only state machine; executesnothing). Passnothing. intent_category(builds: build_from_accounts | account_grouping |known_assignments;known_assignmentsedits:| realign_existing |restructure; maprestructureoverlay:| load_part_layerfor add/show ZIP or part layer; routing:| route_stopsfor driving a known list of stops; reachable areas:| reachable_areafor 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-suppliedperiodic_scheduling. 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_handleanalysis_fresh,tal_label; realign: tal_id,plusmovesselection/realign orrealign_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;flags.route_stopsinputs:route_type=circuit|tour|open_tour, start, stop_sets, end for tour, stops_need_ingest whenthestops are not in the TS yet;valuesreachable_area:knownorigins,sobands,far (part_layer,travel_mode=car|truckterritory_count,origins_need_ingest when the origins are not in the TS yet — the advisor never invents a budget; periodic_scheduling: visit_frequency_fieldtal_id,frequency_unitoperation,dwell_time or dwell_minutes/dwell_hoursroute_type,daily_capacity or daily_capacity_hoursorigins,horizon_daysvisit_frequency_field,max_buckets_per_day — the advisor reports cadence/dwell/capacity...);asmissing_inputsmissingnamesratherthethankeysinventingstillthem).needed. Returns next_tool+with drafted next_args, missing_inputs (reported,blocked_bynever invented),remaining_stepsblocked_by,guidance_uriremaining_steps, and`guidance` (theEMEPworkflowatomguideinlinedinline as{uri, title,guidance.excerpt,Ordertruncated}rules—applied:nomapresources/readbeforeneeded);compute,enforcesviewermc_first,connectedviewer_before_compute,beforebuild_after_ingestcompute,andingestconditionalbeforeanalysis_freshnessbuild,(realignanalyzeonlyafterqueuesanAnalyzeedit when a point layerexists or analysis_requested=true).exists.CallCalling itagainafter eachcompletedstep with the updated stateuntil done. Other categories (delegate, manual_selection, analyze_present, ...) return a discover_intent/guidance fallback. Scenarios: AB-016, ACB-003, DB-001, WI-001,walksWI-002,thewrong-build-tool,wholebuild-before-ingest.workflow.
after
Analyze Territoriesbefore
—after
{"destructiveHint":false,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Analyze Territories"}before
—after
Analyze Routesbefore
—after
{"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"readOnlyHint":true,"title":"Analyze Routes"}before
—after
Auto-Build Territoriesbefore
—after
{"destructiveHint":true,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Auto-Build Territories"}before
—after
Calculate Routebefore
—after
{"destructiveHint":false,"idempotentHint":false,"openWorldHint":true,"readOnlyHint":false,"title":"Calculate Route"}before
—after
Cluster Pointsbefore
—after
{"destructiveHint":true,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Cluster Points"}before
—after
Configure Mapbefore
—after
{"destructiveHint":true,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Configure Map"}before
—after
Create Territory from Partsbefore
—after
{"destructiveHint":true,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Create Territory from Parts"}before
—after
Delete Routebefore
—after
{"destructiveHint":true,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Delete Route"}before
—after
Delete Alignmentbefore
—after
{"destructiveHint":true,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Delete Alignment"}before
—after
Delete Territorybefore
—after
{"destructiveHint":true,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Delete Territory"}before
—after
Build Territories from Assignmentsbefore
—after
{"destructiveHint":true,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Build Territories from Assignments"}before
—after
Discover Workflow Intentbefore
—after
{"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"readOnlyHint":true,"title":"Discover Workflow Intent"}before
—after
Wait for Map Viewerbefore
—after
{"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"readOnlyHint":true,"title":"Wait for Map Viewer"}before
—after
Traverse Expert Pack Linksbefore
—after
{"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"readOnlyHint":true,"title":"Traverse Expert Pack Links"}before
—after
List Expert Pack Topicsbefore
—after
{"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"readOnlyHint":true,"title":"List Expert Pack Topics"}before
—after
Search Expert Packbefore
—after
{"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"readOnlyHint":true,"title":"Search Expert Pack"}before
—after
Export GeoJSONbefore
—after
{"destructiveHint":false,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Export GeoJSON"}before
—after
Extract Alignment Branchbefore
—after
{"destructiveHint":true,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Extract Alignment Branch"}before
—after
EasyTerritory Assistantbefore
—after
{"destructiveHint":true,"idempotentHint":false,"openWorldHint":true,"readOnlyHint":false,"title":"EasyTerritory Assistant"}before
—after
Geocode Addressesbefore
—after
{"destructiveHint":false,"idempotentHint":false,"openWorldHint":true,"readOnlyHint":false,"title":"Geocode Addresses"}before
—after
Get Startup Guidance⟨35 unchanged words⟩ the rotating code every territory/map tool requires),andresult.server_version (this server's product version; the same string as serverInfo.version), and result.seat (plan standard|trial, key_expires_at, and quota_remaining for geocode/route/isochrone — tell a trial user when the trial or an allowance is close to running out). Per-tool rules are on that tool's description. ⟨18 unchanged words⟩
before
—after
{"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"readOnlyHint":true,"title":"Get Startup Guidance"}before
—after
Get Map Selectionbefore
—after
{"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"readOnlyHint":true,"title":"Get Map Selection"}before
—after
Open Mapbefore
—after
{"destructiveHint":true,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Open Map"}before
—after
Get Part Selectionbefore
—after
{"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"readOnlyHint":true,"title":"Get Part Selection"}before
—after
Import GeoJSONbefore
—after
{"destructiveHint":true,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Import GeoJSON"}before
—after
Ingest Accountsbefore
—after
{"destructiveHint":false,"idempotentHint":false,"openWorldHint":true,"readOnlyHint":false,"title":"Ingest Accounts"}before
—after
Build Drive-Time Areabefore
—after
{"destructiveHint":false,"idempotentHint":false,"openWorldHint":true,"readOnlyHint":false,"title":"Build Drive-Time Area"}before
—after
Load Analysis Panelbefore
—after
{"destructiveHint":false,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Load Analysis Panel"}before
—after
Query Territory Partsbefore
—after
{"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"readOnlyHint":true,"title":"Query Territory Parts"}before
—after
Realign Partsbefore
—after
{"destructiveHint":true,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Realign Parts"}before
—after
Reintegrate Alignment Branchbefore
—after
{"destructiveHint":true,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Reintegrate Alignment Branch"}before
—after
Request Account Uploadbefore
—after
{"destructiveHint":false,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Request Account Upload"}before
—after
Request Assignment Uploadbefore
—after
{"destructiveHint":false,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Request Assignment Upload"}before
—after
Request Part Selectionbefore
—after
{"destructiveHint":false,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Request Part Selection"}before
—after
Schedule Visitsbefore
—after
{"destructiveHint":true,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Schedule Visits"}before
—after
Grow Territory from Seedbefore
—after
{"destructiveHint":true,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Grow Territory from Seed"}before
—after
Set Map Statebefore
—after
{"destructiveHint":false,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Set Map State"}before
—after
Show Map Overlaybefore
—after
{"destructiveHint":false,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Show Map Overlay"}before
—after
Submit Feedbackbefore
—after
{"destructiveHint":false,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Submit Feedback"}before
—after
Cancel Taskbefore
—after
{"destructiveHint":true,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Cancel Task"}before
—after
Get Task Statusbefore
—after
{"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"readOnlyHint":true,"title":"Get Task Status"}before
—after
Get Task Resultbefore
—after
{"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"readOnlyHint":true,"title":"Get Task Result"}before
—after
Merge Territoriesbefore
—after
{"destructiveHint":true,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Merge Territories"}before
—after
Rebalance Territoriesbefore
—after
{"destructiveHint":true,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Rebalance Territories"}before
—after
Split Territorybefore
—after
{"destructiveHint":true,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Split Territory"}before
—after
Get Next Workflow Stepbefore
—after
{"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"readOnlyHint":true,"title":"Get Next Workflow Step"}⟨100 unchanged words⟩ call delete_tal instead — never N× this tool. When a rep leaves and the neighbors should take over the territory, call territory_merge instead — this tool leaves the deleted territory's parts unassigned. Already-absent territories return ok with already_absent=true (no job ⟨57 unchanged words⟩
⟨71 unchanged words⟩ Do not skip it on an Apps host. wait_seconds blocks at most 30 s per call (larger values are clamped; the result reports requested_wait_seconds and max_wait_seconds). On VIEWER_NOT_CONNECTED, call again with the same map_session_id while the user opens map_url. Scenarios: MV-001, I-1.
⟨102 unchanged words⟩ failure detail means the viewer could not applyit.it; render_ack_hint says which case applies (reread_state or resend). Once you set center/zoom, the open MC keeps that view (no automatic refit) and a reload of map_url reopens there. Every result carries camera {center, zoom, source}: requested (this call), session (an earlier set_map_state), map_config (the TS default view), or viewer_current (no camera known; the MC keeps its view — switching active_tal_id never refits). Scenarios: residual backlog (no dedicated scenario by design).
⟨6 unchanged words⟩ merge two adjacent territories and rebalance with minimaldisruption.disruption — including a rep who quits or leaves (their territory is absorbed by its neighbors) and going from N to N-1 territories on an existing alignment. Pass the territory that goes away as territory_id_b and one adjacent neighbor as territory_id_a (it keeps its id); the same job then rebalances every remaining territory, so do not follow it with territory_rebalance. Not delete_territory (that leaves the parts unassigned) and not a new auto_build (that discards the alignment). One call removes one territory. On NON_ADJACENT_TERRITORIES pick another neighbor. Prerequisites: adjacent territory_id_a and territory_id_b in source TAL. ⟨43 unchanged words⟩ verify merged TAL in MC, analyze. Scenarios:MG-001..004MG-001..005, EV-001.
⟨64 unchanged words⟩ TAL, refreshed point_layer; viewer connected for MC-first. point_layer may be omitted: the source TAL's build point layer, else the TS's only point layer, is used. CLARIFICATION_REQUIRED details.reason=ambiguous_point_layer (pass point_layer from details.point_layers) or zero_workload (workload_bias>0 but no account maps to a part — check point_layer and dwell; never rerun as is). The source assignment is the non-regression baseline: if ⟨58 unchanged words⟩
{"properties":{"analysis_panel":{"anyOf":[{"description":"single fills the open map's analysis dock with this result (pass map_session_id); prefer it. comparison_slot fills the same dock (no side-by-side layout). none or omitted pushes nothing. Not the same enum as load_analysis_panel.presentation_mode (single|compare).","enum":["none","single","comparison_slot"],"type":"string"},{"type":"null" ⟨176 unchanged words⟩
⟨163 unchanged words⟩ },"repair_policy":{"default":"default","description":"Topology repair for dissolved territories. default fills interior holes in the dissolved geometry (no part is added or reassigned; repair_summary.changed_part_ids lists the territories' own parts). report_only detects holes and changes nothing (warning REPAIR_AVAILABLE). strict fails with REPAIR_REQUIRED when a hole is found.","enum":["default","strict","report_only"],"title":"Repair Policy","type":"string" ⟨49 unchanged words⟩
⟨232 unchanged words⟩ classification is routed to symbology with a warning. PART LAYERS: builds never load a part layer overlay; ZIP/county outlines appear only when named. loaded_part_layers=[ids] shows them, loaded_part_layers=[] hides them all (active_part_layer clears too), and the choice persists across later builds. request_part_selection loads the part_layer it selects on. Optional center ([longitude, latitude]) and zoom ⟨99 unchanged words⟩
⟨74 unchanged words⟩ is rebind_required, call get_map_visualization(job_id=<this job>). With no open MC, pass ts_handle from the prior result (never repost inline ts when a handle exists); the new TAL is appended to that TS and the result returns a new ts_handle. map_session_id wins when both are passed. Completed result includes created_territory.territory_id, leaf_territories, and selection_summary ⟨161 unchanged words⟩
⟨102 unchanged words⟩ ],"default":null,"title":"Ts"},"ts_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Ts Handle"}},"required":["part_layer","part_ids"," ⟨5 unchanged words⟩
⟨46 unchanged words⟩ },"repair_policy":{"default":"default","description":"Topology repair for dissolved territories. default fills interior holes in the dissolved geometry (no part is added or reassigned; repair_summary.changed_part_ids lists the territories' own parts). report_only detects holes and changes nothing (warning REPAIR_AVAILABLE). strict fails with REPAIR_REQUIRED when a hole is found.","enum":["default","strict","report_only"],"title":"Repair Policy","type":"string" ⟨42 unchanged words⟩
⟨20 unchanged words⟩ be a server upload handle (aup_...) fromPOSTrequest_assignment_upload/assignments/upload— csv_text/csv_file/assignments, orrequest_assignment_uploada—POSTafterofparsingtherowsCSVorfilepassingtocsv_text/csv_file.its key-less result.upload_url. Legacy spreadsheets (Postal Code + Territory/Region/Division) are ⟨49 unchanged words⟩
⟨85 unchanged words⟩ },"repair_policy":{"default":"default","description":"Topology repair for dissolved territories. default fills interior holes in the dissolved geometry (no part is added or reassigned; repair_summary.changed_part_ids lists the territories' own parts). report_only detects holes and changes nothing (warning REPAIR_AVAILABLE). strict fails with REPAIR_REQUIRED when a hole is found.","enum":["default","strict","report_only"],"title":"Repair Policy","type":"string" ⟨47 unchanged words⟩
⟨27 unchanged words⟩ cross-tool brief, same text as server instructions)and, result.guidance_handle (the rotating code every territory/map tool requires), and result.server_version (this server's product version; the same string as serverInfo.version). Per-tool rules are on that tool's description. ⟨18 unchanged words⟩
⟨70 unchanged words⟩ 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. No part layer is involved. ORIGINS x BANDS: origins are inline {latitude, ⟨175 unchanged words⟩ carpools, border_crossings, tunnels, car_trains, low_emission_zones. GEOMETRYAND PARTS: each territorystoresIS the provider's exact polygonAND—part_idsnoassignedpartbylayer,centroid_withinnooverlay,part_ids.soBandsanalyzenest andterritorynearbystatsorigins'workpolygonsnormallyoverlap,whileand the mapshowspaintsthethemtrueoverlappingdrive-time(tightestedge.onBandstop).nestanalyzeandcountstheiranpolygonsaccountoverlapinonEVERYpurposearea(tightestthatpaintscontainsonittop);(apart20-minuteassignmentbandisincludesdisjointits—10-minuteaaccounts)partandjoinsreports thetightest band containing it, thenaccountstheoverlappingnearerareasorigin.share. realigncan move/partsterritory_splitbetween/bandsterritory_mergebut/NEVERterritory_rebalancemovesreturnaISOCHRONE_TAL_NOT_EDITABLE:polygon,re-runsoisochrone_buildatorealignedchangeisochrone'sanassignmentarea;intentionallydelete_territorydiverges/fromdelete_talitsstilldrawnremoveshape.one. A band budget is NOT territory workload and ⟨47 unchanged words⟩ analyze(tal_ids=[<tal_id>], map_session_id, analysis_panel='single') forper-bandper-area accounts andmetrics.metrics (needs an ingested point layer). Pass map_session_id here to paint thebandsareas on an open map. Full atom: ezt://guidance/workflows/isochrone-territories. Scenarios: IS-001..IS-006.
⟨65 unchanged words⟩ ":"Origins","type":"array"},"part_layer":{"title":"Part Layer","type":"string"},"part_overlay":{"default":"centroid_within","title":"Part Overlay","type":"string"},"route_type":{"default":"fastest","title": ⟨51 unchanged words⟩ Handle"}},"required":["origins","bands","part_layer"],"title":"isochrone_buildArguments","type":"object"}
⟨15 unchanged words⟩ without geometry. Prerequisites: part_layer from ezt://part-layers. Pass exactly one of filter (e.g. {state_abbr: TX}) or part_ids; neither or both is INVALID_REQUEST. To check that a part layer exists, read ezt://part-layers instead of probing with an empty query. Next: direct_build, configure_map, or agent-side join before ⟨10 unchanged words⟩
⟨95 unchanged words⟩ },"repair_policy":{"default":"default","description":"Topology repair for dissolved territories. default fills interior holes in the dissolved geometry (no part is added or reassigned; repair_summary.changed_part_ids lists the territories' own parts). report_only detects holes and changes nothing (warning REPAIR_AVAILABLE). strict fails with REPAIR_REQUIRED when a hole is found.","enum":["default","strict","report_only"],"title":"Repair Policy","type":"string" ⟨35 unchanged words⟩
⟨75 unchanged words⟩ Continue only after ingest_accounts has loaded the points.FOURWAYS TO STAGE (all return an upload_handle): (A) CSV on disk + a shell with network (Cursor, Claude Code, Codex, CLI agents): call with no rows to get result.upload_url, then run result.curl_example — POST the raw file with Content-Type: text/csv. No API key: upload_url is the credential (expires in 15 min; each POST appends to the same handle). Fastest for large files; do not re-type rows into csv_text or rows chunks. (B) MCP csv_file: pass the uploaded CSV as a ChatGPT/OpenAI file parameter when available. (BC) MCP csv_text: pass csv_text=<raw CSV string>when(whole fileparamsinareoneunavailable.call, up to 16 MB) when you have no shell or no network. Preserve multiline newlines — do NOT JSON-encode with ⟨10 unchanged words⟩ Use Python json.dumps or MCP csv_file instead. (CD) MCP rows: call with rows=<chunk>; append more with upload_handle=<prior handle>. A local path in csv_file fails (DUNREADABLE_FILE_REFERENCE);HTTP:usecall(A).withEverynoresultrows/csv_textcarriestoagetfreshresult.upload_url,upload_urlthenforPOSTitsrowshandle;orancsv_text/csv_file.expired URL returns UPLOAD_TOKEN_EXPIRED — call again with upload_handle for a new one. Then: ingest_accounts(accounts_handle=<upload_handle>, map_session_id=<MC session id>, ⟨152 unchanged words⟩
⟨30 unchanged words⟩ rows here, then call direct_build(assignments_handle=<upload_handle>).FOURWAYS TO STAGE (all return upload_handle): (A) CSV on disk + a shell with network: call with no rows to get result.upload_url, then run result.curl_example — POST the raw file with Content-Type: text/csv. No API key: upload_url is the credential (expires in 15 min; each POST appends). (B) MCP csv_file: pass the uploaded CSV as a ChatGPT/OpenAI file parameter. (BC) MCP csv_text: pass csv_text=<raw multiline CSV> — ⟨4 unchanged words⟩ JSON-encode with PowerShell ConvertTo-Json (corrupts rows). (CD) MCP assignments: pass assignments=<chunk of row objects>; append with upload_handle.(D)AnHTTPexpiredPOSTupload_url/assignments/uploadreturnswithUPLOAD_TOKEN_EXPIREDassignments,—csv_text,calloragaincsv_file.with upload_handle. Legacy columns (Postal Code, Territory, Region, ⟨21 unchanged words⟩
⟨202 unchanged words⟩ reached | closest_under | closest_over | frontier_exhausted |max_parts_reached.max_parts_reached; a closest fit that misses the target also warns SEED_TARGET_UNDERSHOT / SEED_TARGET_OVERSHOT with the signed difference — tell the user, parts are indivisible. NO OVERLAP, EVER: with tal_id, parts ⟨246 unchanged words⟩
⟨53 unchanged words⟩ durable TS map_config. Prefer request_part_selection for selection workflows. The result's render_ack is read the instant the change is published, so state=sent_unconfirmed with applied_render_version one behind is normal; to confirm the paint, read ezt://map-sessions/{map_session_id}/state a second or two later. Only a sent_unconfirmed that persists with a render_ack failure detail means the viewer could not apply it. Once you set center/zoom, the open MC keeps that view (no automatic refit). Scenarios: residual backlog (no dedicated scenario by design).
⟨261 unchanged words⟩ check its render_ack before claiming points are visible. PART LAYERS are never loaded by a build; they show only when asked. 'Hide/remove the zips' returns status=hidden and takes that layer off the map (territories unchanged). ROUTE overlay (overlay_kind=route) needs map_session_id and only ⟨142 unchanged words⟩
⟨105 unchanged words⟩ },"repair_policy":{"default":"default","description":"Topology repair for dissolved territories. default fills interior holes in the dissolved geometry (no part is added or reassigned; repair_summary.changed_part_ids lists the territories' own parts). report_only detects holes and changes nothing (warning REPAIR_AVAILABLE). strict fails with REPAIR_REQUIRED when a hole is found.","enum":["default","strict","report_only"],"title":"Repair Policy","type":"string" ⟨75 unchanged words⟩
⟨105 unchanged words⟩ },"repair_policy":{"default":"default","description":"Topology repair for dissolved territories. default fills interior holes in the dissolved geometry (no part is added or reassigned; repair_summary.changed_part_ids lists the territories' own parts). report_only detects holes and changes nothing (warning REPAIR_AVAILABLE). strict fails with REPAIR_REQUIRED when a hole is found.","enum":["default","strict","report_only"],"title":"Repair Policy","type":"string" ⟨51 unchanged words⟩
⟨117 unchanged words⟩ },"repair_policy":{"default":"default","description":"Topology repair for dissolved territories. default fills interior holes in the dissolved geometry (no part is added or reassigned; repair_summary.changed_part_ids lists the territories' own parts). report_only detects holes and changes nothing (warning REPAIR_AVAILABLE). strict fails with REPAIR_REQUIRED when a hole is found.","enum":["default","strict","report_only"],"title":"Repair Policy","type":"string" ⟨63 unchanged words⟩