arcpi: Not Everything Needs an MCP to Be Agentic

Every time I open my feed these days, somebody has wrapped an API in an MCP server. Weather? MCP. Calendar? MCP. I fully expect my toaster to ship one next week. Don't get me wrong, I like MCP and I use it. But I kept staring at the ArcGIS REST API, with years of well-documented endpoints for search, geocoding, routing, demographics and maps, and asking myself a simple question: does an agent really need another server in front of all of that to be agentic?

So I built arcpi to find out.

The pi conversation in the terminal on the left, and the live map with two drive-time areas and their hatched yellow overlap on the right

The old way and the new way, in one script

arcpi runs on pi, a minimalist agent harness that lives in the terminal. I gave it four small Markdown skills, written from the Esri REST documentation, that tell the model which endpoint to call and with which parameters. And one TypeScript extension that makes the HTTP call as the signed-in user. That's it. No ArcGIS MCP server, no build step, no dependencies.

The magic is code mode. Instead of calling one tool, waiting, thinking, and calling the next, the agent writes one small JavaScript program and runs it in a locked-down sandbox. In that same program it can call plain ArcGIS REST endpoints through tools.arcgis_request (the old way) and an MCP tool through tools.mcp__mappi__run_map_code (the new way). The program chains the calls, filters the results, and hands only the answer back to the model. This idea isn't mine: Cloudflare's Code Mode post and Armin Ronacher's What is Codemode got me thinking. I just pointed it at REST and MCP at the same time.

And that is the point I want to make. MCP is not going away. The live map in arcpi, mappi, is an MCP server, because it is a running browser page and that is exactly what MCP is good at. But the ArcGIS services already have a documented interface, so I let the agent use it directly. Use MCP where it earns its keep, use the existing API where it already exists, and let code mode glue them together.

A real run

Here is what I asked it: locate the Empire State Building and the World Trade Center, create a 10-minute drive-time polygon around each, and if they intersect, show the intersection with hashed yellow markers. Make sure the map shows everything.

The agent read the ArcGIS skill and the three references it needed, then wrote one program. That program geocoded both landmarks, solved both drive-time areas in a single routing request, asked the geometry service for the overlap, and drew all of it on the live map. The ArcGIS part took 5.3 seconds. The whole answer took about three minutes, and most of that was the model thinking.

Map of Manhattan with a blue 10-minute drive-time area around the Empire State Building, a red one around the World Trade Center, and two small yellow hatched overlap areas

Notice what the model never saw: coordinates. Shapes are saved to files and passed around by reference, so the model reads "a polygon with 42 vertices in this file", not the 42 vertices. The chat gets the explanation, the map gets the geometry. I like keeping those two apart.

And guess what? When I looked at the first recording, the model had spent more than three minutes reasoning, mostly second-guessing how to call the geometry service's intersect operation and what an empty result would look like. The skill had no example for it. I added one, re-recorded, and the reasoning dropped from about 11,500 characters to about 7,000 and the answer came back faster. Skills are documentation, and better documentation makes a faster agent. These are single runs, not benchmarks, but it was fun to watch.

p(døøm) = 0

Now, the important part: making sure the agent does not kill us all. I wrote up how to run arcpi inside NVIDIA's OpenShell, where everything is denied unless a policy explicitly allows it. The network is closed except for the model API and ArcGIS, the credentials stay outside the sandbox as placeholders, and anything the agent tries that isn't allowed shows up in the logs. By my math:

p(døøm) = p(agent goes rogue) × p(OpenShell lets it out)
        = p(agent goes rogue) × 0
        = 0

Airtight. Well, almost. The OpenShell recipe is written but I have not run it yet, so for now the honest error bar is 0 ± "I'll let you know" :-)

Try it

The code is on GitHub, and there is a short, plain-language walkthrough with screenshots here. Before you write your next MCP server, check whether the API documentation plus code mode already gets you there. Sometimes it does, and sometimes you want both. More to come.

Comments

Popular posts from this blog

How to use Esri Flex API on Android and iPhone

Augmented Reality, iPhone and ArcGIS Server

Weighted Map Feature Clustering With Attributes