Skip to main content
Glama

arcpro-mcp

Control ArcGIS Pro with natural language through the Model Context Protocol (MCP). Works with Cursor, Claude, and Codex — bring your own LLM, keep your data local.

CI License: MIT Python 3.10+

arcpro-mcp lets an AI agent drive a live ArcGIS Pro session: inspect projects, explore layers and attributes, check coordinate systems, and (soon) run geoprocessing and build layouts — all from a chat prompt in your favorite MCP client.

Unlike Esri's built-in assistant, this is open source, connects to any LLM via your MCP client, and acts on your actual open session rather than only generating code.


Architecture

ArcGIS Pro hosts arcpy, and arcpy.mp.ArcGISProject("CURRENT") only works inside the running app. So the MCP server (a separate process started by your client) talks to a small bridge running in ArcGIS Pro's Python window over a local socket:

Cursor / Claude / Codex
        │  stdio (MCP)
        ▼
arcpro-mcp  (this package, separate process)
        │  TCP socket, length-prefixed JSON  (127.0.0.1:18748)
        ▼
arcpro_bridge.py  (runs in ArcGIS Pro's Python window)
        │  arcpy / arcpy.mp
        ▼
ArcGIS Pro  (live session)

Design choices vs. existing tools:

  • Socket, not file polling — lower latency, cleaner framing for big payloads.

  • Layered + tested — src/ package, dependency-free bridge, pytest, CI.

  • Safety first — read-only by default; destructive tools ask for confirmation; execute_python is off unless explicitly enabled; every call is audit-logged.

  • Great UX — structured output, MCP resources, and live layer-name auto-complete.


Related MCP server: ArcGIS Pro MCP

Requirements

  • ArcGIS Pro 3.x (provides arcpy)

  • Python 3.10+ for the MCP server (the arcgispro-py3 conda env works well)

  • An MCP client: Cursor, Claude Desktop, or Codex

  • uv recommended (or plain pip)

Install

git clone https://github.com/qiobn/arcpro-mcp
cd arcpro-mcp
uv sync          # or: pip install -e .

Usage

1. Start the bridge in ArcGIS Pro

Open ArcGIS Pro with a project, open the Python window, and run:

exec(open(r"C:/path/to/arcpro-mcp/bridge/arcpro_bridge.py").read())

You should see [ArcPro MCP Bridge] Listening on 127.0.0.1:18748.

2. Configure your MCP client

See examples/mcp-config.example.json. For Cursor or Claude Desktop:

{
  "mcpServers": {
    "arcgis-pro": {
      "command": "uv",
      "args": ["--directory", "C:/path/to/arcpro-mcp", "run", "arcpro-mcp"]
    }
  }
}

3. Talk to it

"Ping ArcGIS Pro and tell me what's in the project." "List the layers, then show me the first 10 rows of the roads layer." "What's the coordinate system of C:/data/parcels.shp?"


Tools

Read-only

Tool

Description

ping

Connectivity + project status

get_project_info

Project path, default GDB, maps

list_directory

List files (confirm paths before loading)

describe_data

Data type, geometry, extent, CRS

list_feature_classes

Feature classes in a workspace

list_layers

Layers on the active map

list_fields

Fields of a dataset/layer

count_features

Row count of a layer

get_layer_features

Preview attribute rows

get_unique_values

Distinct values of a field

get_workspace / list_layouts

Inspect workspace / layouts

Write / map-modifying

Tool

Description

add_vector_layer / add_raster_layer

Add data to a map

remove_layer

Remove a layer (asks for confirmation)

set_layer_visibility / zoom_to_layer

Toggle / navigate

create_map / save_project

Map and project management

select_by_attribute / clear_selection

SQL-based selection

set_workspace

Set default GP workspace

run_geoprocessing

Run any ArcPy tool by dotted name

create_layout / export_layout

Build and export print layouts (PDF/PNG/...)

execute_python (arbitrary arcpy) exists but is disabled unless you set ARCPRO_MCP_ALLOW_EXEC=1 in both the client env and the ArcGIS Pro env.

Configuration

Variable

Default

Description

ARCPRO_MCP_HOST

127.0.0.1

Bridge host

ARCPRO_MCP_PORT

18748

Bridge port

ARCPRO_MCP_TRANSPORT

stdio

stdio or streamable-http

ARCPRO_MCP_ALLOW_EXEC

(off)

Enable execute_python (1/true)

ARCPRO_MCP_AUDIT_LOG

~/.arcpro_mcp/audit.log

Audit log path (empty to disable)

ARCPRO_MCP_LOG_LEVEL

INFO

Server log level

Safety

  • Read-only tools are annotated readOnlyHint and are always safe.

  • Destructive tools (coming next) require confirmation via MCP elicitation.

  • execute_python is opt-in and double-gated (client + bridge).

  • Every requested operation is appended to an audit log for review/replay.

Roadmap

  • v0.1 — socket bridge, read-only tools, audit log, CI, tests

  • v0.2 — write tools (add/remove layer, selection, geoprocessing) with HITL confirm

  • v0.3 — execute_python hardening, reflection middleware (CRS/units sanity), layouts & export

  • v0.4 — main-thread dispatch queue / optional .NET Add-In (QueuedTask) for rock-solid arcpy.mp

  • v1.0 — installer, multi-client docs, compound tool mode to cut tokens

License

MIT — see LICENSE.

Acknowledgements

Architecture informed by the excellent nkarasiak/qgis-mcp, jjsantos01/qgis_mcp, geo2004/MCP-ArcGISPro, and the safety patterns in gdal-mcp.

Available Tools

25 tools
add_raster_layerAdd Raster LayerB

Add a raster layer (GeoTIFF, IMG, ...) to the active map (or a named map).

Inputs: path (required), map_name. Returns: object

add_vector_layerAdd Vector LayerA

Add a vector layer (shapefile, feature class, GeoJSON, GeoPackage, ...) to the active map (or a named map). Confirm the path with list_directory first.

Inputs: path (required), map_name. Returns: object

clear_selectionClear SelectionA
Idempotent

Clear the active selection on a layer.

Inputs: layer (required). Returns: object

count_featuresCount FeaturesB
Read-only

Count the features (rows) in a layer in the active map.

Inputs: layer (required). Returns: object

create_layoutCreate LayoutC

Create a print layout with a map frame already added. units: INCH (default), CENTIMETER, MILLIMETER, POINT. Then call export_layout to write it to disk.

Inputs: name, map_name, width, height, +2 more. Returns: object

create_mapCreate MapB

Create a new map in the project. Open it in ArcGIS Pro to view it.

Inputs: name. Returns: object

describe_dataDescribe DataA
Read-only

Describe a dataset: data type, geometry/shape type, feature count, extent, and coordinate system (name, WKID, geographic vs projected, linear unit). Always check this before any distance- or area-based geoprocessing.

Inputs: path (required). Returns: object

export_layoutExport LayoutA
Idempotent

Export a layout to a file. format: PDF (default), PNG, JPG, BMP, TIF, SVG, EPS. dpi applies to raster formats (default 150). output is a full file path.

Inputs: name (required), output (required), format, dpi. Returns: object

get_layer_featuresGet Layer FeaturesA
Read-only

Preview rows from a layer's attribute table. limit caps rows (default 10, max 50). fields optionally restricts which columns are returned.

Inputs: layer (required), limit, fields. Returns: object

get_project_infoGet Project InfoA
Read-only

Get the current project: .aprx path, default geodatabase, and the maps it contains (with their spatial reference).

Inputs: no named parameters. Returns: object

get_unique_valuesGet Unique ValuesA
Read-only

Return distinct values of a field — useful before building where-clauses or categorized symbology. limit caps the number of values returned.

Inputs: layer (required), field (required), limit. Returns: object

get_workspaceGet WorkspaceA
Read-only

Get the current arcpy.env.workspace (default location for GP inputs/outputs).

Inputs: no named parameters. Returns: object

list_directoryList DirectoryA
Read-only

List files in a directory, optionally filtered by a glob pattern (e.g. '*.shp'). Use this to confirm exact filenames before loading data — never guess paths.

Inputs: path (required), pattern. Returns: object

list_feature_classesList Feature ClassesA
Read-only

List feature classes in a workspace (folder, file geodatabase, or feature dataset). pattern filters by name; geometry_type filters by Point/Polyline/Polygon.

Inputs: workspace, pattern, geometry_type. Returns: object

list_fieldsList FieldsA
Read-only

List the fields of a dataset or layer (name, alias, type, length). Use before building SQL where-clauses or attribute operations.

Inputs: dataset (required). Returns: object

list_layersList LayersA
Read-only

List layers in the active map (or a named map). Returns each layer's name, visibility, and whether it is a feature/raster/group layer.

Inputs: map_name. Returns: object

list_layoutsList LayoutsA
Read-only

List print layouts in the project with page size and units.

Inputs: no named parameters. Returns: object

pingPingA
Read-only

Check connectivity to the ArcGIS Pro bridge and read project status: ArcGIS Pro version, project path, list of maps, the active map, and whether a map view is open. Always call this first.

Inputs: no named parameters. Returns: object

remove_layerRemove LayerA
Destructive

Remove a layer from a map by name. This cannot be undone.

Inputs: layer (required), map_name. Returns: object

run_geoprocessingRun GeoprocessingA

Run any ArcPy geoprocessing tool by its dotted name with positional parameters. Examples: tool='analysis.Buffer', params=['roads', 'roads_buf', '500 Meters']; tool='management.Project', params=[input, output, 32650]. ALWAYS check the input coordinate system with describe_data before distance/area operations — reproject first if the data is geographic (degrees). Outputs are overwritten if they already exist. Returns tool outputs and arcpy messages.

Inputs: tool (required), params (required). Returns: object

save_projectSave ProjectB
Idempotent

Save the current ArcGIS Pro project (.aprx).

Inputs: no named parameters. Returns: object

select_by_attributeSelect by AttributeA
Idempotent

Select features in a layer using a SQL where-clause (e.g. "population > 100000"). selection_type: NEW_SELECTION (default), ADD_TO_SELECTION, REMOVE_FROM_SELECTION, SUBSET_SELECTION. Returns the selected feature count. Use list_fields and get_unique_values first to build a valid clause.

Inputs: layer (required), where_clause (required), selection_type. Returns: object

set_layer_visibilitySet Layer VisibilityC
Idempotent

Show or hide a layer on the map.

Inputs: layer (required), visible (required), map_name. Returns: object

set_workspaceSet WorkspaceA
Idempotent

Set arcpy.env.workspace — the default location for inputs/outputs when a full path is not given (e.g. a folder or a file geodatabase path).

Inputs: workspace (required). Returns: object

zoom_to_layerZoom to LayerA
Idempotent

Zoom the active map view to a layer's full extent. Requires an open map view.

Inputs: layer (required), map_name. Returns: object

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.7/5.0

    Scored across 25 tools

    Disambiguation5/5

    Each tool targets a distinct operation or data type. 'list_*' tools operate on different entities (directory, feature classes, fields, layers, layouts), and 'get_*' tools retrieve different information. No overlapping purposes.

    Naming Consistency5/5

    All tool names follow a consistent verb_noun pattern in snake_case (e.g., add_raster_layer, list_layers, select_by_attribute). Even 'ping' fits as a common verb. No mixing of conventions.

    Tool Count4/5

    With 25 tools, the server covers a broad range of ArcGIS Pro functionalities without being excessive. It is slightly on the high side but well-scoped for the domain.

    Completeness3/5

    The set covers core workflows: data loading, querying, selection, geoprocessing, and layout creation. However, it lacks tools for spatial selection, feature editing, symbology control, and other advanced operations common in GIS.

    Maintenance

    ActivityInactive
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers