<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Muhammed Enes Duran</title>
    <description>The latest articles on DEV Community by Muhammed Enes Duran (@muend).</description>
    <link>https://dev.to/muend</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3955086%2F5f1cd487-ae94-40cd-9672-3f78bf4408ad.jpeg</url>
      <title>DEV Community: Muhammed Enes Duran</title>
      <link>https://dev.to/muend</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/muend"/>
    <language>en</language>
    <item>
      <title>Why AI-Generated GIS Code Can Run Successfully and Still Be Wrong</title>
      <dc:creator>Muhammed Enes Duran</dc:creator>
      <pubDate>Thu, 23 Jul 2026 15:01:21 +0000</pubDate>
      <link>https://dev.to/muend/why-ai-generated-gis-code-can-run-successfully-and-still-be-wrong-473k</link>
      <guid>https://dev.to/muend/why-ai-generated-gis-code-can-run-successfully-and-still-be-wrong-473k</guid>
      <description>&lt;p&gt;Geospatial code has an unusual failure mode: it can execute without errors, produce a clean-looking result, and still be &lt;strong&gt;methodologically wrong&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This problem is becoming more important as AI coding agents generate complete GIS workflows from short natural-language instructions.&lt;/p&gt;

&lt;p&gt;The issue is not always that an AI agent does not know GeoPandas, rasterio, PostGIS, Earth Engine, or ArcPy. In many cases, it knows the correct API and can produce executable code.&lt;/p&gt;

&lt;p&gt;The problem is that API correctness is not the same as geospatial correctness.&lt;/p&gt;

&lt;h2&gt;
  
  
  A silent CRS failure
&lt;/h2&gt;

&lt;p&gt;Consider a simple buffer operation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="sb"&gt;`buffered = gdf.buffer(1000)`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code looks reasonable. It runs successfully and produces new geometries.&lt;/p&gt;

&lt;p&gt;But what does 1000 mean?&lt;/p&gt;

&lt;p&gt;When the dataset uses a geographic coordinate reference system such as &lt;strong&gt;EPSG:4326&lt;/strong&gt;, its coordinate units are degrees rather than metres. The operation may still return valid geometries, but they do not represent a &lt;strong&gt;1,000-metre buffer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A safer workflow makes the spatial assumptions explicit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;gdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crs&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The input dataset has no CRS.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;gdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_geographic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;projected_crs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;estimate_utm_crs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;projected_crs&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A suitable projected CRS could not be determined.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;gdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_crs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;projected_crs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;buffered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even this pattern is not universally correct. A locally estimated UTM projection may be unsuitable for datasets that span multiple zones, countries, or continents.&lt;/p&gt;

&lt;p&gt;The important point is not one specific code snippet. A defensible workflow must ask:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is the CRS defined?&lt;/strong&gt;&lt;br&gt;
Are the coordinate units appropriate?&lt;br&gt;
Is the projection suitable for the geographic extent?&lt;br&gt;
How will the output be verified?&lt;/p&gt;

&lt;p&gt;Without those checks, plausible output can hide an invalid spatial operation.&lt;/p&gt;
&lt;h2&gt;
  
  
  A machine-learning result that looks better than it is
&lt;/h2&gt;

&lt;p&gt;Spatial machine learning has a similar problem.&lt;/p&gt;

&lt;p&gt;A common model evaluation starts with a random train/test split:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.model_selection&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;train_test_split&lt;/span&gt;

&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;train_test_split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;test_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For independent observations, this can be a reasonable starting point.&lt;/p&gt;

&lt;p&gt;Spatial observations, however, are often correlated with nearby observations. A random split can place neighbouring samples in both the training and test sets.&lt;/p&gt;

&lt;p&gt;The model then appears to generalize, while it may only be exploiting local spatial similarity.&lt;/p&gt;

&lt;p&gt;This can produce an impressive accuracy score that collapses when the model is applied to a genuinely new region.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A more defensible evaluation may require:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;spatial blocks or geographic groups&lt;/li&gt;
&lt;li&gt;region-level holdouts&lt;/li&gt;
&lt;li&gt;distance-based separation&lt;/li&gt;
&lt;li&gt;temporal separation for future prediction&lt;/li&gt;
&lt;li&gt;maps of residuals and prediction errors&lt;/li&gt;
&lt;li&gt;comparison between random and spatial validation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Spatially blocked validation often produces a lower score.&lt;/p&gt;

&lt;p&gt;That lower score may be the more honest result.&lt;/p&gt;

&lt;p&gt;Rendering is not verification&lt;/p&gt;

&lt;p&gt;Maps introduce another form of silent failure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A map can render correctly while using:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an unsuitable projection&lt;/li&gt;
&lt;li&gt;misleading classification boundaries&lt;/li&gt;
&lt;li&gt;inconsistent scales across dates&lt;/li&gt;
&lt;li&gt;inaccessible colour choices&lt;/li&gt;
&lt;li&gt;unreported missing values&lt;/li&gt;
&lt;li&gt;geometries silently removed during processing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A change-detection map can look convincing even when the source rasters are misregistered, collected in different seasons, or processed at incompatible levels.&lt;/p&gt;

&lt;p&gt;Successful rendering proves that the software produced an image.&lt;/p&gt;

&lt;p&gt;It does not prove that the analysis is valid.&lt;/p&gt;

&lt;p&gt;What an AI agent needs beyond API knowledge&lt;/p&gt;

&lt;p&gt;A general-purpose coding agent may know which function to call. It does not necessarily know when a workflow should stop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For geospatial tasks, an agent needs explicit operational rules:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Never calculate area or distance in a geographic CRS.&lt;/li&gt;
&lt;li&gt;Treat spatial leakage as a default risk in predictive modelling.&lt;/li&gt;
&lt;li&gt;Validate geometries and account for input and output row counts.&lt;/li&gt;
&lt;li&gt;Preserve georeferencing through raster and deep-learning pipelines.&lt;/li&gt;
&lt;li&gt;Report uncertainty when the analytical method supports it.&lt;/li&gt;
&lt;li&gt;Verify outputs numerically and visually.&lt;/li&gt;
&lt;li&gt;Refuse invalid spatial or temporal comparisons instead of producing a misleading result.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are not optional improvements added after the analysis.&lt;/p&gt;

&lt;p&gt;They are part of the analytical method itself.&lt;/p&gt;

&lt;p&gt;**&lt;/p&gt;

&lt;h2&gt;
  
  
  Building GeoAI Skills
&lt;/h2&gt;

&lt;p&gt;**&lt;/p&gt;

&lt;p&gt;I built GeoAI Skills to encode these safeguards as reusable Agent Skills.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The current public preview contains 18 skills covering:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;geospatial data engineering&lt;/li&gt;
&lt;li&gt;PostGIS and spatial SQL&lt;/li&gt;
&lt;li&gt;remote sensing&lt;/li&gt;
&lt;li&gt;Google Earth Engine&lt;/li&gt;
&lt;li&gt;geospatial deep learning&lt;/li&gt;
&lt;li&gt;spatial statistics&lt;/li&gt;
&lt;li&gt;geostatistics and interpolation&lt;/li&gt;
&lt;li&gt;terrain and hydrology&lt;/li&gt;
&lt;li&gt;suitability analysis&lt;/li&gt;
&lt;li&gt;point clouds and LiDAR&lt;/li&gt;
&lt;li&gt;network accessibility&lt;/li&gt;
&lt;li&gt;movement trajectories&lt;/li&gt;
&lt;li&gt;change detection&lt;/li&gt;
&lt;li&gt;cartography and geovisualization&lt;/li&gt;
&lt;li&gt;guarded ArcGIS Pro automation&lt;/li&gt;
&lt;li&gt;machine-learning experiment standards&lt;/li&gt;
&lt;li&gt;software engineering and DevOps practices&lt;/li&gt;
&lt;li&gt;multi-stage GeoAI workflow orchestration
The objective is not to make an AI agent sound like a GIS expert.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The objective is to make assumptions visible, require verification, and fail loudly when a workflow is not defensible.&lt;/p&gt;

&lt;p&gt;A land-suitability request, for example, may involve data preparation, remote sensing, terrain analysis, multi-criteria decision analysis, and cartographic delivery.&lt;/p&gt;

&lt;p&gt;GeoAI Skills uses a central orchestrator to route each stage to the relevant specialist skill while maintaining shared safeguards such as CRS checks, leakage prevention, uncertainty reporting, and verification.&lt;/p&gt;

&lt;p&gt;**&lt;/p&gt;

&lt;h2&gt;
  
  
  Guarded ArcGIS Pro automation
&lt;/h2&gt;

&lt;p&gt;**&lt;/p&gt;

&lt;p&gt;The suite also includes an arcgis-pro-automation skill.&lt;/p&gt;

&lt;p&gt;It works with my open-source &lt;strong&gt;arcgis-mcp-bridge&lt;/strong&gt; project to support controlled local ArcPy execution.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;The integration covers workflows involving:&lt;br&gt;
*&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ArcGIS Pro projects&lt;/li&gt;
&lt;li&gt;file and enterprise geodatabases&lt;/li&gt;
&lt;li&gt;vector and raster geoprocessing&lt;/li&gt;
&lt;li&gt;spatial and statistical analysis&lt;/li&gt;
&lt;li&gt;network analysis&lt;/li&gt;
&lt;li&gt;maps and layouts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because ArcPy operations can modify real projects and datasets, the execution model includes path restrictions and confirmation gates for mutating or destructive actions.&lt;/p&gt;

&lt;p&gt;The goal is not unrestricted automation.&lt;/p&gt;

&lt;p&gt;It is controlled automation with explicit boundaries.&lt;/p&gt;

&lt;p&gt;**&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing whether the correct skills activate
&lt;/h2&gt;

&lt;p&gt;**&lt;/p&gt;

&lt;p&gt;A skill is not useful if the agent does not activate it for the right request—or activates it for unrelated requests.&lt;/p&gt;

&lt;p&gt;The repository currently contains 131 typed evaluation scenarios across the 18 skills.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;These scenarios include:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;positive activation cases&lt;/li&gt;
&lt;li&gt;negative cases&lt;/li&gt;
&lt;li&gt;ambiguous requests&lt;/li&gt;
&lt;li&gt;collisions between related skills&lt;/li&gt;
&lt;li&gt;artifact-oriented evaluations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a frozen 17-skill, 120-case routing suite using Claude Code 2.1.214 and Claude Sonnet 5, the published results were:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;100% routing precision&lt;br&gt;
92.86% routing recall&lt;br&gt;
92.5% full-route accuracy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;These results have an important limitation.&lt;/p&gt;

&lt;p&gt;They measure whether the expected skills were selected for that exact runtime, model, and evaluation suite. They do not prove that every generated analysis is correct, and they are not universal compatibility or answer-quality claims.&lt;/p&gt;

&lt;p&gt;The eighteenth skill, arcgis-pro-automation, was added after the frozen benchmark and is not included in those headline figures.&lt;/p&gt;

&lt;p&gt;Routing evidence, behavioural correctness, and real-world analytical quality should be evaluated separately.&lt;/p&gt;

&lt;p&gt;Trying the public preview&lt;/p&gt;

&lt;p&gt;The complete suite can be explored and installed interactively with:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npx skills add muend/geoai-skills&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;A single specialist skill can also be installed independently:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npx skills add muend/geoai-skills --skill postgis-spatial-sql&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub repository:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/muend/geoai-skills" rel="noopener noreferrer"&gt;https://github.com/muend/geoai-skills&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Skills collection:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.skills.sh/muend/geoai-skills" rel="noopener noreferrer"&gt;https://www.skills.sh/muend/geoai-skills&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The project is still an early public preview.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I am particularly interested in reproducible examples of:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GIS code that executed successfully but produced an invalid result&lt;/li&gt;
&lt;li&gt;spatial leakage in real machine-learning projects&lt;/li&gt;
&lt;li&gt;ArcGIS Pro or ArcPy failure modes that should be guarded explicitly&lt;/li&gt;
&lt;li&gt;ambiguous requests that could activate the wrong specialist skill&lt;/li&gt;
&lt;li&gt;important verification steps that the current suite does not yet require&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What is the most common silent geospatial failure you have encountered?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>showdev</category>
      <category>geospatial</category>
    </item>
    <item>
      <title>Building a Secure MCP Bridge for ArcGIS Pro and ArcPy</title>
      <dc:creator>Muhammed Enes Duran</dc:creator>
      <pubDate>Wed, 01 Jul 2026 15:44:07 +0000</pubDate>
      <link>https://dev.to/muend/building-a-secure-mcp-bridge-for-arcgis-pro-and-arcpy-511g</link>
      <guid>https://dev.to/muend/building-a-secure-mcp-bridge-for-arcgis-pro-and-arcpy-511g</guid>
      <description>&lt;p&gt;ArcGIS Pro has a powerful Python runtime through ArcPy, but it is not a lightweight dependency. It is native, licensed, Windows-bound, and tightly coupled to the ArcGIS Pro environment.&lt;/p&gt;

&lt;p&gt;That makes it awkward to connect directly to AI tooling.&lt;/p&gt;

&lt;p&gt;I built &lt;code&gt;arcgis-mcp-bridge&lt;/code&gt; as an independent open-source experiment to solve this problem through the Model Context Protocol (MCP).&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/muend/arcgis-mcp-bridge" rel="noopener noreferrer"&gt;https://github.com/muend/arcgis-mcp-bridge&lt;/a&gt;&lt;br&gt;&lt;br&gt;
PyPI: &lt;a href="https://pypi.org/project/arcgis-mcp-bridge/" rel="noopener noreferrer"&gt;https://pypi.org/project/arcgis-mcp-bridge/&lt;/a&gt;&lt;br&gt;&lt;br&gt;
MCP Registry: &lt;code&gt;io.github.muend/arcgis-mcp-bridge&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;ArcPy is extremely useful for GIS automation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;geoprocessing&lt;/li&gt;
&lt;li&gt;feature class and geodatabase operations&lt;/li&gt;
&lt;li&gt;raster workflows&lt;/li&gt;
&lt;li&gt;map and layer management&lt;/li&gt;
&lt;li&gt;coordinate reference operations&lt;/li&gt;
&lt;li&gt;spatial statistics&lt;/li&gt;
&lt;li&gt;network analysis&lt;/li&gt;
&lt;li&gt;export and layout workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But ArcPy is not a normal pure-Python library. It depends on the licensed ArcGIS Pro Python environment and native Esri runtime components.&lt;/p&gt;

&lt;p&gt;For AI/MCP workflows, importing ArcPy directly into the host process is not a clean architecture. If the native runtime crashes, blocks, logs to stdout, or touches the wrong local path, the AI host process is affected too.&lt;/p&gt;

&lt;p&gt;So the main design goal was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Keep the AI/MCP host process lightweight, controlled, and separate from the licensed ArcPy execution runtime.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  What arcgis-mcp-bridge does
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;arcgis-mcp-bridge&lt;/code&gt; is a local-first MCP server that exposes ArcGIS Pro’s ArcPy engine over stdio JSON-RPC.&lt;/p&gt;

&lt;p&gt;It allows MCP-compatible clients to call a controlled set of ArcPy/geoprocessing tools without importing ArcPy directly into the MCP server process.&lt;/p&gt;

&lt;p&gt;The project currently includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;100 declarative geoprocessing tools&lt;/li&gt;
&lt;li&gt;10 GIS verticals&lt;/li&gt;
&lt;li&gt;a two-process architecture&lt;/li&gt;
&lt;li&gt;ArcPy worker-process isolation&lt;/li&gt;
&lt;li&gt;PathGuard filesystem boundaries&lt;/li&gt;
&lt;li&gt;confirmation gates for destructive operations&lt;/li&gt;
&lt;li&gt;mocked ArcPy tests for CI without an ArcGIS license&lt;/li&gt;
&lt;li&gt;optional Sketch-to-GIS / OpenCV pipeline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is not an official Esri project and not an official Anthropic project. It is an independent open-source bridge for ArcGIS Pro, ArcPy automation, and MCP-based GIS workflows.&lt;/p&gt;
&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;The project is split into two layers.&lt;/p&gt;
&lt;h3&gt;
  
  
  Layer A: MCP server
&lt;/h3&gt;

&lt;p&gt;Layer A is the MCP protocol host.&lt;/p&gt;

&lt;p&gt;It handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;stdio JSON-RPC transport&lt;/li&gt;
&lt;li&gt;MCP tool registration&lt;/li&gt;
&lt;li&gt;Pydantic validation&lt;/li&gt;
&lt;li&gt;path validation before worker execution&lt;/li&gt;
&lt;li&gt;dispatching jobs to the worker process&lt;/li&gt;
&lt;li&gt;returning structured MCP tool results&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Layer A does not import ArcPy.&lt;/p&gt;

&lt;p&gt;That is intentional. The MCP server should remain lightweight and should not depend on ArcGIS Pro’s native runtime being importable in the same process.&lt;/p&gt;
&lt;h3&gt;
  
  
  Layer B: ArcPy worker
&lt;/h3&gt;

&lt;p&gt;Layer B is a separate Python process running inside the licensed ArcGIS Pro Python environment.&lt;/p&gt;

&lt;p&gt;It handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;importing ArcPy&lt;/li&gt;
&lt;li&gt;executing geoprocessing tools&lt;/li&gt;
&lt;li&gt;collecting ArcPy messages&lt;/li&gt;
&lt;li&gt;returning one structured JSON result frame&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This separation matters because ArcPy can be slow to import, can depend on license state, and can interact with native runtime components. Keeping it in a worker process protects the MCP server process from crashes and native runtime instability.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why two processes?
&lt;/h2&gt;

&lt;p&gt;The two-process model gives the project a clean failure boundary.&lt;/p&gt;

&lt;p&gt;If the ArcPy worker fails, times out, or crashes, the MCP server can return a structured error instead of dying with it.&lt;/p&gt;

&lt;p&gt;The basic flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MCP client
   ↓ stdio JSON-RPC
Layer A: MCP server
   ↓ NDJSON subprocess bridge
Layer B: ArcPy worker
   ↓
ArcGIS Pro / ArcPy runtime
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is slower than keeping ArcPy warm in the same process, but it is much safer as a first production boundary.&lt;/p&gt;

&lt;p&gt;A future version could add a warm worker pool behind the same execution interface, but the isolation model should remain explicit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Filesystem safety: PathGuard
&lt;/h2&gt;

&lt;p&gt;GIS workflows often touch sensitive local data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;file geodatabases&lt;/li&gt;
&lt;li&gt;enterprise connection files&lt;/li&gt;
&lt;li&gt;infrastructure datasets&lt;/li&gt;
&lt;li&gt;cadastral data&lt;/li&gt;
&lt;li&gt;client project folders&lt;/li&gt;
&lt;li&gt;personal or proprietary spatial datasets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because of that, the bridge uses a PathGuard boundary.&lt;/p&gt;

&lt;p&gt;Each filesystem-touching tool declares whether a path is used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;read&lt;/li&gt;
&lt;li&gt;write&lt;/li&gt;
&lt;li&gt;read list&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The server validates paths before spawning a worker, and the worker validates again before execution.&lt;/p&gt;

&lt;p&gt;The goal is not to make arbitrary AI file access convenient. The goal is to make the allowed local execution boundary narrow, explicit, and auditable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Destructive operation gates
&lt;/h2&gt;

&lt;p&gt;Some ArcPy operations mutate data.&lt;/p&gt;

&lt;p&gt;Examples include append, delete, repair geometry, calculate field, projection definition, and other state-changing workflows.&lt;/p&gt;

&lt;p&gt;For those operations, the project requires explicit confirmation in the tool input.&lt;/p&gt;

&lt;p&gt;The rule is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Destructive tools must require &lt;code&gt;confirm=true&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This gives both the MCP host and the user a clear point where mutation is intentional.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing without ArcGIS Pro in CI
&lt;/h2&gt;

&lt;p&gt;A major practical issue with ArcPy projects is CI.&lt;/p&gt;

&lt;p&gt;Hosted CI runners usually do not have ArcGIS Pro installed, and they do not have a licensed ArcPy runtime available.&lt;/p&gt;

&lt;p&gt;To keep the project testable, the automated test suite mocks ArcPy and focuses on the parts that can be verified without Esri runtime access:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pydantic contracts&lt;/li&gt;
&lt;li&gt;PathGuard behavior&lt;/li&gt;
&lt;li&gt;registry invariants&lt;/li&gt;
&lt;li&gt;destructive operation gates&lt;/li&gt;
&lt;li&gt;worker error mapping&lt;/li&gt;
&lt;li&gt;configuration validation&lt;/li&gt;
&lt;li&gt;import boundaries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The current suite contains 81 unit tests and runs without ArcGIS Pro.&lt;/p&gt;

&lt;p&gt;Real geoprocessing execution still requires a licensed ArcGIS Pro environment on Windows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current tool surface
&lt;/h2&gt;

&lt;p&gt;The project currently exposes 100 tools across 10 GIS verticals:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;map and layer management&lt;/li&gt;
&lt;li&gt;data management&lt;/li&gt;
&lt;li&gt;geometry analysis&lt;/li&gt;
&lt;li&gt;coordinate reference and projection&lt;/li&gt;
&lt;li&gt;raster operations&lt;/li&gt;
&lt;li&gt;vision analytics&lt;/li&gt;
&lt;li&gt;export and layout&lt;/li&gt;
&lt;li&gt;editing and topology&lt;/li&gt;
&lt;li&gt;network analysis&lt;/li&gt;
&lt;li&gt;spatial statistics&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The optional vision component includes a Sketch-to-GIS pipeline using OpenCV. The idea is to extract hand-drawn boundaries from an image and commit them into a geodatabase workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;The package is available on PyPI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;arcgis-mcp-bridge
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project also includes a setup command for preparing an ArcGIS Pro worker environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;arcgis-mcp-setup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For vision-related functionality:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"arcgis-mcp-bridge[vision]"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Real ArcPy execution requires a licensed ArcGIS Pro Python environment. The server uses &lt;code&gt;ARCPY_PYTHON_PATH&lt;/code&gt; to locate the worker interpreter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example MCP use cases
&lt;/h2&gt;

&lt;p&gt;Potential workflows include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;list feature classes inside a file geodatabase&lt;/li&gt;
&lt;li&gt;buffer parcels and write outputs to a scratch geodatabase&lt;/li&gt;
&lt;li&gt;dissolve features by attribute&lt;/li&gt;
&lt;li&gt;run raster slope/aspect analysis&lt;/li&gt;
&lt;li&gt;export layouts&lt;/li&gt;
&lt;li&gt;inspect geometry errors&lt;/li&gt;
&lt;li&gt;run spatial statistics tools&lt;/li&gt;
&lt;li&gt;prepare repeatable GIS automation tasks from natural language requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The point is not to let an AI agent freely mutate GIS projects. The point is to expose a controlled, typed, auditable tool surface around common ArcPy workflows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/muend/arcgis-mcp-bridge" rel="noopener noreferrer"&gt;https://github.com/muend/arcgis-mcp-bridge&lt;/a&gt;&lt;br&gt;&lt;br&gt;
PyPI: &lt;a href="https://pypi.org/project/arcgis-mcp-bridge/" rel="noopener noreferrer"&gt;https://pypi.org/project/arcgis-mcp-bridge/&lt;/a&gt;&lt;br&gt;&lt;br&gt;
MCP Registry: &lt;code&gt;io.github.muend/arcgis-mcp-bridge&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Esri Community discussion:&lt;br&gt;&lt;br&gt;
&lt;a href="https://community.esri.com/t5/python-questions/open-source-mcp-bridge-for-arcgis-pro-arcpy/m-p/1711533#M75310" rel="noopener noreferrer"&gt;https://community.esri.com/t5/python-questions/open-source-mcp-bridge-for-arcgis-pro-arcpy/m-p/1711533#M75310&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Feedback wanted
&lt;/h2&gt;

&lt;p&gt;I am especially interested in feedback from GIS developers, ArcPy users, and people building MCP servers.&lt;/p&gt;

&lt;p&gt;The main questions are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Does this architecture make sense for a local licensed runtime like ArcGIS Pro?&lt;/li&gt;
&lt;li&gt;Which ArcPy tools or workflows should be prioritized next?&lt;/li&gt;
&lt;li&gt;What safety boundaries would be required before using this with real project data?&lt;/li&gt;
&lt;li&gt;Which ArcGIS Pro versions should be tested first?&lt;/li&gt;
&lt;li&gt;Would a warm worker pool be worth the added complexity, or is spawn-per-call isolation the better default?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The project is open source, and feedback is welcome.&lt;/p&gt;

</description>
      <category>python</category>
      <category>gis</category>
      <category>opensource</category>
      <category>mcp</category>
    </item>
  </channel>
</rss>
