Repository navigation
ArcGIS Pro MCP Studio v0.3.0
Second development preview: typed MCP results with output schemas, resources and prompts; geoprocessing discovery, risk tiers and dry runs; approval for project and feature edits; deterministic agent evaluations; synthetic CC0 test data; a live operation matrix over every operation; and committed live ArcGIS Pro acceptance evidence, including the first full operation matrix run. Unsigned; not a production release.
Added
- Live acceptance tooling and committed evidence.
tools/run-acceptance.ps1(Windows PowerShell 5.1 compatible) records the commit, .NET and ArcGIS Pro facts, and the hashes of the built and loaded add-in DLLs, runs the live harnesses by section, and with-Commitwritesdocs/acceptance/<date>-<sha7>/(manifest.json,summary.md,SHA256SUMS). Committed manifests list only evidence the folder contains, with paths relative to it ("evidencePathsRelative": true), and the committedstress/summary.jsonnames each layout capture by its committed image (images/layout-<case>.png). The script rejects an explicit-PipeNamethat is not a live ArcGIS Pro discovery record, reports ambiguous cached add-in DLLs, and records-VisuallyInspectednotes (visuallyInspected[]) and their-VisualNotesSource, and-RunNotesdisclosures about how the run was carried out (runNotes[]and a "Run notes" section ofsummary.md). A hand-writtenANNEX.mdat a folder root, outsideSHA256SUMSand titled as added after the run, is the only file allowed in a committed folder after the run. docs/acceptance/README.md defines the folder contract; a Core test validates every committed manifest and requires each listed evidence path to exist (folders that predate relative paths are exempt). The first two evidence runs, on 2026-09-28 and 2026-09-29, covered verify, the loaded-DLL match, host probe, the MCP smoke test and the three bundled urban workflows x 3 runs on ArcGIS Pro 3.7.1 in default mode, with layouts visually inspected. They used the earlier third-party test data, so their folders are kept in the private development archive only (see Public history). Autonomous mode and the feature/GP/ArcPy section are not covered. - First full live operation matrix evidence:
docs/acceptance/2026-10-03-e7deee2(commit e7deee2, ArcGIS Pro 3.7.1.1904, default mode) adds the operation matrix to verify, the DLL match, smoke and 3 workflows x 3 runs: 41/41 operations covered, 90/90 cases passed, eight dockpane approval cards (7 approved, 1 denied). The cards were decided by Claude through computer use at the maintainer's direction, not by the maintainer's own clicks; the firstproject.opencard expired and was re-queued, and ArcGIS Pro's modal "Save all edits?" prompt during the finalproject.openwas answered by the same means, which is what saved the matrix's feature edits (annex). Autonomous mode and the separate feature/GP/ArcPy section are not covered. ARCGIS_PRO_MCP_REVISION_LOG=1diagnostics: the host logs every workspace revision change with the state behind it (project URI, map and layout names), the event behind each revision advance and the outcome and timing of every post-write settle (settle-ok,settle-unsettledorsettle-timeoutwith drain and sampling milliseconds, sample count and published revision), to one file per ArcGIS Pro process. The log stops growing at 50 MB, and log lines are not built while logging is off.tools/analyze-revision-log.py(standard library only) attributes each settle to the audited operation that wrote it and reports, per operation, the settle count, budget-cap rate, unsettled count and p50/p95 drain, sample and total times. See deployment.SECURITY.mdwith supported versions, private reporting through GitHub security advisories, and a threat model summary.CONTRIBUTING.mdwith prerequisites, build, test and packaging commands, and pull request conventions.- This changelog and a public roadmap.
- Bundled workflows are embedded in
ArcGISProMCP.Coreand seeded into%LOCALAPPDATA%\ArcGISProMCP\workflowsat start-up, soworkflow_listis populated on a fresh install. Seeding is a no-op once a workflow is present; existing files are never overwritten. - Workflow operation allowlists: an optional
allowedOperationslist on workflows, enforced on save and run and reported as the validation issueoperation_not_allowed. Operation descriptors gainexecutesUserCode(true forgp.runandarcpy.run-script), whichregistry_searchandregistry_browseentries also report. Allowlist entries are operation ids and are not version-pinned. registry_searchacceptscapabilitiesandmaxRiskfilters.- Approval cards show a "Runs user code" warning for
arcpy.*runs and forgp.runrequests that use a custom toolbox (.pyt/.atbx/.tbx) or a Python expression; matchinggp.runresults carry auser_code_executionnotice. - Audit records gain
kind(operation,dry-runorapproval),autonomousBypass,decisionandactor. Unknown operation ids and every local approve/deny decision are now audited. An audit write failure for an unknown operation id adds theaudit_write_failednotice like every other invocation, and a failed approval audit write in the dockpane is reported in the activity feed. - Audit log rotation:
operations.jsonlrotates once it exceeds 16 MiB and the newest five rotated files are kept. The log is appended through a handle that shares read, write and delete access with other processes; if rotation fails (for example while another ArcGIS Pro process holds the file), it is skipped and the record is still appended. - MCP resources:
arcgis://project/stateplus templates for operations, workflows (id@version), skills and observation handles. - MCP prompts: one per bundled skill (
skill.<id>) and, while ArcGIS Pro is running, one per saved workflow (run.<id>) with arguments from the workflow parameters. - Typed tool results: every tool returns
{ ok, result, error }asstructuredContent(and the same JSON as its text block) with anoutputSchema, and setsisErroron failure witherror=code,message,retryable,revision.registry_invokeandworkflow_runresults withsuccess: falseare errors that keepresult. Bridge results are typed records shared by the add-in and the gateway (ArcGISProMCP.Bridge.Protocol.BridgeContracts). - Every tool declares
readOnlyHint,destructiveHint,idempotentHintandopenWorldHint(always false). Onlyregistry_invokeandworkflow_runare destructive. registry_describereturnsresultSchema, the JSON schema of the operation'sregistry_invokeresult envelope. Itsdatamember is the operation'soutputSchema, now declared by 15 stable-shape operations (project.get,map.list,layer.list,feature.*,table.query,table.statistics,metadata.get,layout.list,layout.inspect,view.capture);datastays unconstrained for the rest, includinggp.*.registry_invokeacceptsdryRun: the operation is validated statically and never executed, needs no revision or approval token, and neither queues a review nor consumes a token. A completed dry run is notisErroreven when it reportsvalid: false. CombiningdryRunwithidempotencyKeyfails withdry_run_idempotency_conflict.approval_statusacceptswaitSeconds(0-120) to wait for the dockpane decision instead of polling. Each wait holds a pipe slot, so the host allows at most two concurrent waits; further calls return the current status immediately with awaitNoticeasking the client to poll.tests/ArcGISProMCP.Server.Tests: in-process MCP client/server harness with a scriptable fake bridge andtools/list,resources/listandprompts/listsnapshots (UPDATE_SNAPSHOTS=1regenerates them).- Geoprocessing operations
gp.searchandgp.describe(ReadOnly) over the installed system toolbox metadata, returning execution names, risk tiers and the positional parametersignaturegp.runexpects. gp.query(ReadOnly, no review) runs only the allowlisted read-only system toolsmanagement.GetCount,management.GetRasterPropertiesandmanagement.GetCellValue, resolved against the system toolboxes, with no map outputs, no overwrite and no history.- Geoprocessing risk tiers (UserCode, Destructive, ConsumesCredits, ReadOnlyQuery, Standard). Approval cards for
gp.runsay "Modifies/deletes input data in place" or "Consumes ArcGIS Online credits"; results carrygp_mutates_input,gp_consumes_creditsorgp_tool_not_indexednotices. - Static
gp.rundry runs: operations can implementIDryRunnableOperation;gp.rundry runs validate parameters against the catalog and report the tier, confirmation, user-code and autonomous-refusal flags without executing. ArcGIS Pro has no validate-only geoprocessing API, so this is static validation, not the tool's own validation. - The registry now has 41 operations (39 without the opt-in ArcPy pair); the MCP tool count stays 16.
- Draft MCP registry packaging:
dotnet packonArcGISProMCP.Servernow produces theArcGISProMCP.GatewayNuGet dotnet tool (package typeMcpServer, commandarcgis-pro-mcp) with.mcp/server.jsonforio.github.nicoazel/arcgis-pro-mcpand the bundled skills.tools/pack-gateway.ps1packs intoartifacts/packagesand verifies the package; nothing is published. The single-file release bundle is unchanged. See deployment. - Agent evaluations (
evals/, see evals/README.md): deterministic suites that run in everydotnet testwithout model calls. E1 routes 30 natural-language tasks throughregistry_searchover the add-in's 41 real descriptors; E2 routes 30 tasks throughgp.searchover the installed ArcGIS Pro system toolboxes (skipped when Pro is absent) plus a 5-task subset over synthetic toolboxes that always runs. Held-out sets (16 + 16 tasks) were written before search tuning. Each suite reports recall@1, recall@5, MRR and misses, fails when one more task misses the top 5 than at the baseline inevals/baseline.jsonor MRR drops more than 0.05 below it, and can write a scorecard toevals/results/<date>-<sha7>/. A Python live harness (evals/live/) runs a Claude model against a running gateway by hand; it is not part of CI. - MCP contract tests in
tests/ArcGISProMCP.Server.Tests: aninitializesnapshot, the bridge method and parameter shape of every tool (snapshot), success and error envelopes of every tool validated against itsoutputSchema, and malformed-call handling. tests/ArcGISProMCP.Operations.Tests: behavior tests for the portable operations over fake ArcGIS services ("fake Pro": real descriptors, registry and executor), and a guard that every portable descriptor is identical to what the add-in registered before the operations moved.tests/ArcGISProMCP.Testingshared test library: FakePro, the scenario loader,FakeHostRuntimerunning the add-in's real bridge request handler, andInProcessBridgeClient. Not shipped.- End-to-end tests (MCP client -> gateway -> bridge handler -> registry/executor -> fake ArcGIS services) covering the approval chain for
feature.delete,feature.updateandproject.save;gp.search,gp.describe,gp.rundry run and run; autonomous refusal; and stale-revision andworkspace_changedhandling. tools/ArcGISProMCP.FakeHostdevelopment and eval host that serves a scenario project over the real named pipe with a discovery record (interactive y/n approvals;--auto-approvefor eval runs only;--autonomous,--toolboxes), plus thetools/test-fakehost.ps1smoke test. Not shipped.- E3 golden trajectories (
evals/trajectories, 8 tasks, 4 of which exercise approval) replayed against the end-to-end server and graded onschemaValidArgs,approvalDiscipline(null for a trajectory with no gated invoke; one approval covers one invoke; gated invokes must carryexpectedRevision) andtaskSuccess; evals/README.md explains how to run live evals against FakeHost. - Synthetic test data.
tests/datais now generated bytools/create-synthetic-test-data.py(ArcPy, fixed seed2272;ProposedMassingderived throughtools/create-urban-massing-fixture.py) and dedicated to the public domain under CC0 1.0. It replaces the earlier third-party-derived set (git history is not rewritten) and keeps every dataset name, field, geometry type and value the bundled workflows, the stress runner and the tests rely on;tests/data/expected-statistics.jsonrecords the feature counts and sums.tests/data/README.mddocuments the provenance, licence, coordinate system and layout, and a Core test checks that each shapefile set is complete, that only generated files are present, that no local or drive-letter path (:\) and no third-party, vendor or place name is embedded in the generated data (SHP/,MasterPlan.gdb/,expected-statistics.json), and that the expected statistics are consistent. The generator scans the build and the README before publishing, and validates the extent window, theDesignSitesGlobalID field and the Z-enabled massing. - Live operation matrix:
tools/run-live-operations.ps1with its ordered plantools/live-operations-plan.jsonexercises all 41 operations against a disposable project in default mode (happy paths plus negative cases: invalid arguments, stale revision, missing and broken layers,confirmation_requiredwithout a token, dry runs), asks the operator to approve or deny eight review cards in the dockpane, and writes per-call request/result JSON,summary.jsonanderrors.mdwith a message audit. It refuses a host in autonomous mode and a project outside-DisposableRoot, copies test data instead of touchingtests/data, and supports-PlanOnly,-SkipCards(a pre-run without an operator; card cases are recorded as skipped, the summary recordsskipCards: true, so the section cannot pass),-Offlineand-OperatorTimeoutSeconds. The script exits 1 unless the summary reportsallPassed; an aborted run recordsabortedand never passes. Each card case counts one decision however often its card was queued (queuedcounts every request), an approved DENY card has its token cancelled before the case fails and is counted asapprovedDenyCard, a refused approval request is counted asrequestFailed(neither as approved nor skipped), a gateway that times out is stopped and restarted on the next call, and the broken-layer case is skipped with the locked files named when ArcGIS Pro keeps the shapefile open.tools/run-acceptance.ps1gains theoperationssection; a Core test checks that the plan covers every operation descriptor, that a passedoperationsstep comes withoperations/summary.json, and that committed operation summaries show a live host, no-SkipCardspre-run, no abort, one decision per card case, no failed case and full coverage. - The ArcPy scripts the live feature/GP/ArcPy acceptance script expects (
hello.py,nonzero.py,timeout.py,setup_acceptance.py) are committed undertools/arcpy-scripts, so the configured ArcPy script root can point at the repository. - GitHub issue forms (bug report, feature request), a pull request template and
CODE_OF_CONDUCT.md(Contributor Covenant 2.1). README badges for CI, licence and status. - A static GitHub Pages landing page (
site/) and.github/workflows/pages.yml, which deployssite/with SHA-pinned actions on pushes tomainthat change it, or by hand, once the repository variablePAGES_ENABLEDistrue. -SkipTestsfortools/verify-release.ps1andtools/package-release.ps1(CI packages without re-running tests).pack-addin.ps1,verify-release.ps1andpackage-release.ps1declare#Requires -Version 7.0.- Tag-triggered release workflow (
.github/workflows/release.yml): a pushedvX.Y.ZorvX.Y.Z-rc.Ntag onmainwhose version matchesDirectory.Build.props,server.jsonandConfig.damlrunstools/package-release.ps1with the full test suite and creates a draft GitHub release (a prerelease for-rc.N) with the bundle,SHA256SUMSand notes from this changelog (tools/extract-release-notes.ps1). A final release needs a dated changelog heading. The maintainer reviews and publishes the draft. - Stable error codes instead of
operation_failedfor common caller mistakes. A missing map, layer, layout or map frame fails withmap_not_found,layer_not_found,layout_not_foundorframe_not_found(raised by the shared add-in resolvers; the fake services used in tests match). An argument value the project does not accept fails withinvalid_arguments: an unknown basemap, a layer that is not a feature layer or has the wrong geometry type forsymbology.*,label.configure,table.*orlayer.set-elevation, an unknown field fortable.*orsymbology.set-unique-values, an existing element of another type inlayout.ensure-surround, or afeature.*argument the layer does not accept (an unknown or read-only field, an attribute value that does not fit its field, a geometry of the wrong type or with too few positions, a malformed target, envelope, spatial relationship or selection mode). The live operation matrix assertsinvalid_argumentsfor its threefeature.createnegative cases. Operations raise it explicitly through the new CoreOperationException.InvalidArgument. A plainArgumentExceptionfrom an operation staysoperation_failed: ArcGIS SDK calls also throw it for internal failures, possibly after a change, andinvalid_argumentstells a client that nothing ran. Layout elements placed off the page fail withelement_outside_page(see Fixed). Found by the live operation matrix.
Changed
- Breaking: in autonomous mode
gp.runrefuses Destructive and UserCode tools, tools the toolbox catalog cannot classify (fail closed), and requests flagged as running user code, withdestructive_tool_requires_review, unless the request carries a person-issued approval token. Curated destructive tools (management.Delete,Delete_management, ...) are classified Destructive even when the catalog has not indexed them. A request that carries a token takes the normal confirmation path: a valid token runs it (audited withautonomousBypass: false) and an invalid one fails withconfirmation_requiredwithout falling back to the bypass. The refusal message says to request local review withapproval_requestand retry with the token. - Breaking: tool text content is now the
{ ok, result, error }envelope rather than the bare bridge result; readresult(orstructuredContent.result). Failed bridge calls areisErrorresults instead of protocol errors, andskill_getfor an unknown id returnsskill_not_found.registry_browseandworkflow_runresults now always carry all their members (unused ones are null). Enum fields such asriskremain numbers. - Breaking:
project.open,project.saveandfeature.updatenow require a local-review approval token (or autonomous mode). Dockpane buttons that trigger them approve through the same audited approval queue. - Breaking: a workflow whose workspace revision changes mid-run now stops with
workspace_changed(reportingstoppedAtStep,stepIndex,expectedRevision,currentRevision) instead of retrying the step against the new revision.continueOnErrordoes not override this, and completed steps are not rolled back. - Breaking: saved workflows can no longer use
gp.runorarcpy.run-scriptunless theirallowedOperationslists them. WhenallowedOperationsis present it is exhaustive. - Breaking: skill manifests without a non-empty
allowedOperationsare skipped when loading. The bundled master-cartography skill no longer listsproject.save. - Breaking:
resource.readpayloads use camelCase field names (mimeType,name,createdAt) instead of PascalCase. tools/test-mcp.ps1checks every tool's hints and envelopeoutputSchemaand reads results fromstructuredContent.- Registry search ignores articles and prepositions in queries.
- Tool descriptions no longer over-promise:
registry_validateis described as a schema and revision check that does not resolve layers or paths, and theregistry_searchlimit is documented as 1–100 to match the registry clamp. JsonLineAuditLogmoved toArcGISProMCP.Core.Infrastructure, and the Add-In resource store facade is renamedProResourceStore.- The live feature/GP/ArcPy acceptance script documents that it requires autonomous mode and asserts the
autonomous_controlnotice forfeature.updateandproject.save. - One release status across all documentation: development preview; supported configuration is an interactive same-user workstation with dockpane approvals; autonomous mode is an opt-in expert setting, not recommended.
docs/production-readiness.mdmerged intodocs/deployment.md, which now also covers the implemented surface, acceptance evidence and known limits. The 2026-09-09 live pass is described as local maintainer notes; reviewable evidence is committed underdocs/acceptance/.docs/security.mddescribes the implemented per-PID multi-instance discovery, states thatgp.runcan execute arbitrary Python (Python toolboxes, script tools, Calculate Field expressions) without ArcPy, and documents that workflows cannot execute confirmation-gated steps in default mode.- The release bundle no longer includes the roadmap in its
docs/folder. - Bundled workflows are now version 1.2.0 and no longer end with
project.save; save the project with an explicitproject.savecall. Legends and scale bars sit at least 0.35 in above the bottom edge of their map frame, clear of the basemap attribution (the mixed-use legend moves to the scene frame), and a Core test checks surround placement statically. - Dependencies:
Microsoft.Extensions.Hosting10.0.12 andMicrosoft.NET.Test.Sdk18.10.1. Dependabot ignores semver-major updates ofxunit.v3andxunit.runner.visualstudio, whose 4.x releases move to Microsoft.Testing.Platform, until a deliberate migration. - Operation input schemas are built with a typed
JsonSchemasbuilder that self-checks each schema at startup; the string-basedJsonSchemas.ObjectSchemahelper is removed. Migrated schemas are semantically identical to the originals, guarded by a captured fixture. - Operations seam: a new Esri-free
ArcGISProMCP.Operationsproject holds the operation base class, every operation schema, and 21 of the 41 operations (project.*,map.*,layer.list,view.capture,feature.*,gp.*,arcpy.*), which call ArcGIS Pro through synchronous DTO services (IProjectService,IMapService,ILayerService,IViewCaptureService,IFeatureService,IGeoprocessingService) implemented in the add-in. Descriptors are unchanged. Layout, symbology, table, metadata and the remaining layer operations stay in the add-in. Source-text tests for the moved operations are replaced by behavior tests against fakes; the remaining source-text tests cover code that still needs ArcGIS Pro or WPF. registry_searchandgp.searchrank with a shared search index: punctuation stripping, camel-case splitting, light stemming, whole-word over prefix over compound matches, a softened inverse document frequency and a small synonym map of general GIS vocabulary.gp.searchgives the core system toolboxes a modest prior. The held-out gains come from the matching changes (stemming, camel-case splitting, IDF, the core-toolbox prior); an ablation showed that synonyms only moved the main suites' recall@5 (held-out recall@5 was the same with or without them), so synonym entries fitted to single main-suite tasks were removed. Measured recall@5 before tuning -> now: registry-search 0.833 -> 0.867, gp-search 0.667 -> 0.900, held-out registry-search 0.938 -> 0.938, held-out gp-search 0.688 -> 0.875 (gp suites against ArcGIS Pro 3.7.1; ablation table in evals/README.md).feature.queryandfeature.selectread a light layer schema (IFeatureService.Schema) instead of the full description, so they no longer evaluate editability before a read. Results are unchanged.tools/package-release.ps1fails if development-only hosts (FakeHost) or test support (ArcGISProMCP.Testing, scenario files) reach the release archive.- CI: GitHub Actions are pinned to commit SHAs; pushes run only for
mainandv*tags, and the concurrency group uses the PR number; the workflow is split into build-test, package and lint jobs with read-only permissions; test projects are discovered instead of listed, so new test projects run without editing the workflow; build-test runs the FakeHost smoke test, and the package job does not re-run the tests; Dependabot updates NuGet packages and Actions weekly; PSScriptAnalyzer (clean acrosstools/) and ruff (tools/andevals/) lint the scripts. layout.ensure-surroundanchors the surround atx,yand lets ArcGIS Pro size it from its style. A scale bar keeps its style height and may round its width to whole divisions (accepted from half to one and a half times the requested width); legends and north arrows still fit inside the requested box.boundsreports the actual size, and asurround_resizednotice says when it differs from the request. A newly created surround that does not converge is removed again.
Fixed
project.openhung while the current project had pending feature edits: ArcGIS Pro showed its modal "Save all edits?" prompt and the MCP call waited until a person answered it.project.opennow refuses withpending_editswhen the project has unsaved feature edits and otherwise withunsaved_project_changeswhen it has unsaved project changes, before anything is opened: a live spot check on ArcGIS Pro 3.7.1 showed the modal "Save changes to ?" prompt on every dirty project, including aftermap.ensureon a just-saved project and on an untouched project, which ArcGIS Pro marks dirty right after opening it. In practiceproject.opentherefore usually needs aproject.savefirst. The refusals are checked through the new CoreIExecutionPreconditionbefore the executor validates the approval token, so they do not spend the token (the operation re-checks when it runs).project.savenow saves pending feature edits before the.aprx, reportseditsSaved, and fails withedits_not_saved(leaving the.aprxunsaved) when ArcGIS Pro cannot save them; its description no longer claims that saving the.aprxpersists every pending change. Found by live acceptance (the operation matrix's finalproject.open).feature.queryandfeature.selectreturnedobjectId: -1andglobalId: null, andfeature.selectcould match nothing, whenever specific fields were requested: the query only fetched the requested columns. The ObjectID and GlobalID columns are now always fetched. Found by the live operation matrix.- The project's unsaved (dirty) flag is no longer part of the workspace revision. ArcGIS clears it about 2.5 s after
project.savereturns, which invalidated an approval granted for the next call (project.openin the live matrix). Edits made in Pro still advance the revision through host events, but an edit that raises none of the subscribed events (layer order, standalone tables, map properties, layout page setup, bookmarks, project items, which map a map frame shows) no longer invalidates approvals, even on a just-saved project (known gaps). - Workflows stopped with
workspace_changedright afterlayer.addin live ArcGIS Pro, because deferred ArcGIS notifications arrived after the 50 ms settle window. The revision log showed the cause: ArcGIS raises layout element and map member events as echoes of an MCP write up to about half a second after it returns. Writes now settle by draining the host once (main CIM thread, ArcGIS UI dispatcher at application-idle, main CIM thread again), then waiting until ArcGIS has raised no workspace event for 300 ms (the operation matrix pre-run caught a layout element echo of a data-source swap arriving 6 ms after a revision-quiet window), and then for a 150 ms quiet period (three unchanged samples 50 ms apart). The 3 s settle budget bounds this waiting and is cancellable; when it runs out the write publishes a plain snapshot. Every snapshot is read on ArcGIS Pro's main CIM thread, so the budget does not bound how long a write takes to return: while Pro is busy (for example loading a newly added layer), that read waits for it, and one write afterlayer.addtook about 30 s in a live run. The revision log records every settle's outcome and timing. Both early evidence runs (2026-09-28 and 2026-09-29, kept in the private development archive only) passed 9/9 (3 workflows x 3 runs). Map member events whose only hints areConnectionStatusorExpansionno longer advance the revision, and neither do layout element events ArcGIS raises by itself (map frame navigation and activation, legend/scale bar/north arrow refreshes, map frame property refreshes), which a live stress run showed arriving about 1 s aftersymbology.set-simplereturned. Which view has focus is no longer part of the workspace revision, so pane switches no longer cause spuriousworkspace_revision_mismatch. Found by live acceptance. feature.*operations failed on shapefile layers with "The definition of the table ... does not support this operation": ArcGIS throws fromHasGlobalID()for shapefiles instead of returning false. Found by live acceptance; the GlobalID check now treats the two exceptions the SDK documents for it,NotSupportedExceptionandGeodatabaseException, as no GlobalID.layer.addwithnamereused an existing same-named layer even when its data source was broken or pointed at a different dataset, and echoed the requested source for it (Created: false), so the next step failed. A same-named layer whose data connection is healthy is now never removed: it is reused when its path matches the request (ignoring case, trailing separators,/vs\and an optional.shp) or names the same workspace and dataset (a file geodatabase feature class with or without its feature dataset), reused with asource_unverifiedwarning when ArcGIS reports no dataset path (for example service layers), and otherwise only repaired in place (Repaired: true, symbology kept) or left unchanged with alayer_source_mismatchwarning. Only a broken layer is repaired in place or, as a last resort, removed and re-added at the same position (Replaced: true), with alayer_repairednotice. An in-place swap counts as successful when the layer is no longer broken, and any remaining path difference is reported rather than triggering a re-add.Sourceis the path the layer actually reads, with the request inRequestedSource, plusDataSourceStatus(ok,broken,unverified,mismatch). The swap keeps the shapefile folder or file geodatabase open until ArcGIS has replaced the data source. Found by live ArcGIS Pro acceptance.- Operations on a layer with a broken data source (
feature.*,table.query,table.statistics,symbology.set-simple,symbology.set-unique-values,layout.set-frame-extent) failed withoperation_failed"Object reference not set to an instance of an object." They now fail withlayer_data_source_unavailableand a message naming the layer and how to repair it. Operations can raise the new CoreOperationException(code, message), which the executor reports with its own code; codes the executor, workflows, bridge or gateway report themselves (confirmation_required,workspace_revision_mismatch,workspace_changed,invalid_arguments,operation_not_found, ...) are rejected. Found by live ArcGIS Pro acceptance. feature.layer.describereturnsspatialReference: nullinstead of failing when a feature class has no spatial reference.- Malformed tool arguments (a missing required argument or a value of the wrong JSON type) return the standard envelope with
error.codeinvalid_arguments, a message naming the argument andretryable: false, instead of the SDK's generic error text withoutstructuredContent. Unknown tool names remain a JSON-RPC error. - In-process hosts built concurrently could intermittently publish the injected bridge client as a required
bridgetool argument (a race on the reflectionParameterInfocache that the MCP SDK relies on). Tools and resources are now built once per process, and the server tests run in parallel again. - The dockpane no longer shows a hard-coded "Skills 1" count. Skills are loaded by the MCP gateway process, which the add-in cannot see, so the expander header now shows only the workflow count.
invalid_workflowerrors fromworkflow_saveandworkflow_runprefix each issue with its code (for exampleoperation_not_allowed: ...), so clients can tell policy violations from other validation failures.- A request cancelled by ArcGIS Pro shutting down now fails with the retryable code
host_stoppinginstead of the genericbridge_request_failed.docs/security.mdstates that keyed requests cannot be cancelled by callers once started. - Idempotent registry and workflow requests run under the host lifetime rather than the first caller's cancellation token, so cancelling one caller no longer cancels work another caller with the same key is waiting on.
table.queryon a layer whose shapefile was renamed after it was added failed withoperation_failed"A geodatabase exception has occurred." ArcGIS keeps the table handle and flags the layer broken only later, so opening the table succeeds and the next read throws. Reads of layer data (table.*,feature.*,symbology.*) now report a geodatabase error aslayer_data_source_unavailablewhen the dataset was not found or the layer's local files are gone; the original error is kept for the audit trail, not in the message. Other geodatabase errors, such as an invalid where clause, are unchanged. Found by the live operation matrix.layout.ensure-surroundrefused a 2.5 x 0.5 in scale bar ("did not accept its requested frame binding and bounds") because ArcGIS Pro sizes the scale bar height from its style. See Changed. Found by the live operation matrix.layout.add-map-frameaccepted a frame placed entirely off the page. Map frames and surrounds must now lie on the page, andlayout.set-textanchors on it (0.01 in tolerance); otherwise the call fails withelement_outside_page, naming the element and the page size, before anything is created. A Core test checks that every bundled workflow places its frames, surrounds and text on the page. Found by the live operation matrix.map.activatefailed with a NullReferenceException when an open map pane had no map view yet; the pane lookup is now null-safe. Found by the operation matrix pre-run.tools/run-urban-stress.ps1runs against a git-ignored copy of the fixtures instead oftests/data, so the lock files ArcGIS Pro keeps next to open data no longer land in the repository fixtures and fail the fixture tests during a live session. The copy lives inartifacts/urban-stress/fixtures/<key>/, where the key is the first 12 hex digits of the SHA-256 oftests/data/expected-statistics.json: a complete copy is reused only while the key matches (so data sources keep a stable path across runs), a data change gets a fresh copy, an interrupted copy is redone, and lock files are removed from a new copy.
Security
- Confirmation now covers project replacement (
project.open), project persistence (project.save) and attribute/geometry edits (feature.update). - Workflows can no longer silently adopt a newer workspace revision and continue writing against state nobody reviewed. Only a successful write step advances the run's expected revision; a read-only step or a
continueOnErrorfailure that observes a mid-run change no longer authorizes the following writes. - User-code operations are opt-in per workflow, and approvals that execute Python are visibly labelled.
gp.runrequests for Calculate Field, Calculate Fields and Calculate Value are labelled as running user code even without an explicit expression type, because ArcGIS Pro defaults it to Python 3. Python toolboxes called by an imported alias remain a documented false negative for the user-code label; autonomous mode refuses them as unclassified.- Approval decisions, autonomous bypasses and unknown operation ids are recorded in the audit log.
- User
.atbxtoolboxes are read as untrusted archives: each metadata entry is bounded to 16 MiB of decompressed bytes whatever size it declares, an archive may hold at most 10,000 entries and 64 MiB of metadata, and script (.py) entries are only checked for presence, never read. An archive over a limit is reported as unreadable. - Approvals stay in the ArcGIS Pro dockpane: MCP elicitation is intentionally not used, so the client UI cannot become the approval authority.
approval_statuswaitSecondsonly waits for the dockpane decision. - A dockpane button click (for example Open project) can no longer approve an identical request that an MCP client queued for review. The panel's self-approval always creates its own approval entry (
IApprovalService.Request(..., reuseExisting: false)), and the transient entry no longer flashes in the approval cards. - The gateway never selects a development FakeHost automatically. Discovery records carry
hostKind(arcgis-pro|fakehost; missing meansarcgis-pro), and FakeHost records are used only when selected withARCGIS_PRO_MCP_HOST_PID/ARCGIS_PRO_MCP_PIPEor the development opt-inARCGIS_PRO_MCP_ALLOW_FAKEHOST=true. FakeHost prefixes its project name with[FakeHost]and warns when ArcGIS Pro is running.
Removed
- Rhino-era documents (
docs/integrated-rhino-*,docs/master-plan-demo.md) and the internaldocs/FRESH_CONTEXT_HANDOFF.md. - Machine-specific checkout paths from the manual acceptance procedure.
Latest committed live acceptance evidence: docs/acceptance/2026-10-03-e7deee2 (commit e7deee2). It covers only the commit, ArcGIS Pro version and sections it names.
Unsigned development preview. Verify the bundle against SHA256SUMS and its inner checksums.sha256, then follow deployment.