
Key Takeaways
- The task today: An agent working through a scanned geologic map must pass each result to the next tool; Stratigraphic Amenity, an open-source server whose tools AI agents call (an MCP server), runs that chain. Drill databases avoid re-typing by joining each assay on its sample number.
- The problem: A model carrying a value to the next call re-types it and can drop a record or change a digit; the server checks only a pasted query result’s shape. Scans of 50 to 200 MiB register but are too big for the agent to read.
- What the tool does: It gives the scan, crops of its detected regions and query results local
geomap://names (handles); a tool handed one uses the server’s copy unless a typed extent, bounds or georeference overrides it. Scans too big to read are listed as UNREADABLE but still processed; the agent reads their crops.
TL;DR
Passing a scanned geologic map and its results between an AI agent’s tools? Stratigraphic Amenity, an open-source server whose tools AI agents call (an MCP server), gives the scan, crops of its detected regions and stored query results short local names: geomap:// handles. A tool handed one fetches the server’s own copy, not one the model re-typed, much as a drill database joins assays by sample number. A typed pixel extent, bounds or georeference overrides the stored one, so leave it out unless you mean to replace it. A scan over the 50 MiB read limit is listed as UNREADABLE but still processed, and the agent reads its crops instead.
Why Should an AI Geology Agent Pass Names, Not Values?
Every exploration database manager knows the slipped decimal point, and anyone building AI geology agents should know it too. The lab reports 1.23 g/t and someone keys in 12.3. An ordinary interval then looks like a discovery until the next check catches it. The fix that stuck was not better typists. It was to stop typing. A drill database stores the sample number against the interval. It loads the lab’s own certificate file, joined on that number. The value travels once, from the lab’s file into the table, and everyone downstream carries the sample number.
An AI agent working through a scanned geologic map brings the typist back. Its tools hand results to each other, and the obvious courier is the model. The knowledge bundle a query found for the map’s area has to reach the overlay tool. The box around the detected map panel has to reach the georeferencing fit. If the model carries those values, it reads them out of one tool result. Then it writes them back as the next call’s arguments. A language model copying a few hundred earthquake records into a tool call is a typist with a large vocabulary. It can drop a record, round a magnitude or change one digit of a longitude. The overlay will then draw what it receives.
What Are geomap:// Handles in AI Geology Tools?
Stratigraphic Amenity, our open-source MCP server for scanned geologic maps, gives the agent sample numbers instead. The server keeps local file paths to itself. The scan you register with the server gets a geomap:// name. So does everything the server makes from it: each crop, each georeference revision, each knowledge bundle and each rendered overlay.
A tool that needs one of them takes the name and fetches the server’s own copy. When the tool that plots knowledge results onto the map receives a bundle_uri, it reads the file the query wrote. What it draws therefore comes from the query’s own records, not from a copy the model typed. A bundle handle is 54 characters whether the bundle holds three earthquakes or three hundred.
Where a Typed Value Still Wins
The handles do not close every door, and they shouldn’t. The overlay tool also accepts a bundle inline, and a georeference inline. The georeferencing tool accepts a typed pixel extent. A map query accepts typed bounds. Each has an honest use:
- a bundle from another session;
- a georeference fitted in another program;
- a map panel the detector missed, which the user outlined by hand.
But the server checks only an inline bundle’s shape, a handful of required keys. It does not check whether the records match anything a query found.
What matters most is precedence, because it decides who wins when both are present. The georeferencing tool takes an explicit extent first, then the box stored with a named crop, then the stored detection. A map query takes explicit bounds before the stored georeference. The overlay takes an inline georeference before the stored one. In every case the typed value wins. Suppose an agent copies the map panel’s box out of the processing result, to be helpful. It has just replaced the server’s record with its own transcription, and a correct transcription looks exactly like a wrong one. Custody is something the agent opts into, by leaving the value out.
The Scan an AI Geology Agent Can Point At but Not Open
The principle shows most clearly at its limit. The server registers a source image of up to 200 MiB. A resource read, though, returns at most 50 MiB. Between the two is a scan the server will work on but will not hand over. It is not an exotic file. For example, a 24 by 36 inch sheet at 200 dpi, as an uncompressed RGB TIFF, is about 99 MiB. In the resource list, its source carries a name that begins UNREADABLE. The description gives its size and the limit, and a read of it fails.
Reading the Pieces Instead
Everything else still works. The detector runs on that scan, and the overlay renderer draws the records onto it. Both reach the file through the server, not through the agent. The fit in between never opens the file at all: it uses the map-panel box the detector stored. The agent directs work on a sheet it cannot load. What it reads instead are the pieces the server cuts:
- a crop of each detected region;
- a 2 by 2 mosaic of the main map’s corners, where the coordinate ticks usually sit;
- a downsampled preview, which the server builds on its own side, not through a read, so the read limit never blocks it.
That is close to how a geologist reads a large sheet anyway. The eye goes to the legend, the margins and the patch that matters, rarely to every pixel at once.
So UNREADABLE is less an error than an instruction: refer to this, read something smaller. When a read does fail, it comes back as a protocol error, not a tool result. Its data carries oversize_image and a recovery hint that says the same thing (“Read a smaller derived artifact”). The instruction has limits worth knowing. Without the detector there are no crops, so the smaller pieces an agent would read instead do not exist. And the flag appears in the resource list and nowhere else. The links that tool results carry name the same resource without it. An agent that never lists resources learns about the limit when its read fails.
What a Handle Doesn’t Carry
A handle protects the transfer, not the reading. At the end of the run, the agent still has to open the bundle and write sentences about it. The typist returns for that last step. The habit that helps is one exploration reporting already has. Quote the number and cite where it came from, here the bundle’s URI beside every count.
A handle is also local. A sample number means something only inside the database that issued it. Likewise, a geomap:// URI resolves only while that server’s registry and files stay where they are. If a file goes missing, a read fails with artifact_not_found and “Resource backing file is missing.” It is a name on one workbench, not an address anyone else can open.
Nor does every handle name a fixed thing. Processing writes each crop to a path keyed by the map ID, the region type and its index. The registry reuses the URI for that path. If the scan or the detector changes, processing again puts new pixels and a new box under an old crop URI. A crop the new run did not produce stays listed, with its old box. The map’s handle, likewise, resolves to its current georeference. After a refit, the overlay draws an older bundle on the new fit, not the one that bounded its query.
The Question to Ask of an AI Geology Agent
None of this makes the model’s reading less important. It narrows where reading has to happen. The usual question about an agent is what it can read: how large an image, how long a document. The more useful question for a geology agent is what the model has to copy. Every value it re-types between two tools is a transcription with no certificate behind it. Every handle it passes instead is one fewer. The evidence that survives best in the final answer is the evidence the model never touched until it had to write the sentence.
Putting that habit into an agent doesn’t take new tooling. It takes knowing which argument makes a tool look up the server’s record, and which makes it trust the caller. Stratigraphic Amenity writes that choice into its open-source code rather than leaving it to prompt wording. You can check it, and exercise it, with any MCP host and a scan of your own.
The bundle on its way to the overlay is the clearest case. Both routes end in the same few lines. One takes what the model wrote; the other fetches what the query stored.
Where the Server Fetches Its Own Copy
The two doors sit side by side in one small function. The overlay tool calls it to get the bundle it will draw:
def _bundle_data(
self,
*,
bundle_uri: str | None,
bundle: Mapping[str, Any] | None,
) -> dict[str, Any]:
if bundle is not None:
return redact_paths(dict(bundle))
if bundle_uri is None:
raise McpToolError("artifact_not_found", "bundle_uri or inline bundle is required.")
content = self.registry.read_resource(bundle_uri)
if "text" not in content:
raise McpToolError("unsupported_media", "Bundle resource must be JSON text.")
return json.loads(content["text"])The server takes an inline bundle as given. It only strips anything that looks like a local path. Before that, the tool’s input schema has checked four top-level keys, not the records’ values (mcp/schemas.py:436-448). A call can’t send both: with bundle_uri and bundle together, it fails validation with invalid_arguments (mcp/schemas.py:336).
A bundle_uri goes the other way. read_resource is the same function that answers an MCP resources/read (mcp/resources.py:335-362). It looks the name up in the registry. It checks that the backing file still exists inside an allowed root or the server’s cache. Then it checks the size against the 50 MiB read limit and returns the JSON the query wrote. The server writes each bundle once, under a fresh random ID, and never rewrites it (mcp/resources.py:218-238). The name therefore points at one snapshot.
The same read limit is where UNREADABLE comes from. When the registry lists resources, it marks anything over the limit before the agent asks (mcp/resources.py:479-488). If the agent reads one anyway, the registry raises oversize_image with the observed and limit byte counts (mcp/resources.py:346-355). The limit is a constructor argument of ResourceRegistry. The server builds its registry from its root settings alone, so no environment variable changes it (mcp/resources.py:91-123).
The geomap:// URI Templates
The server publishes seven URI templates for its MCP resources (mcp/resources.py:428-450):
geomap://maps/{map_id}: the map’s registration record;geomap://maps/{map_id}/source: the source image;geomap://maps/{map_id}/georef.json: the current georeference;geomap://maps/{map_id}/georef/{revision}.json: one fixed georeference revision;geomap://artifacts/{artifact_id}{extension}: a crop or detection overlay;geomap://bundles/{bundle_id}.json: a stored query result;geomap://overlays/{overlay_id}{extension}: a rendered overlay.
Running It With an AI Geology Agent
To try it, clone the Stratigraphic Amenity repository and install the full profile, since the crops come from the detector:
uv sync --group dev --extra mcp --extra geo --extra assets --extra detectorsRegister stratigraphic-amenity-mcp with your MCP host, with your map folder in GEOMAP_MCP_ALLOWED_ROOTS. Then call, in order:
geomap_list_capabilities, to see what is ready.geomap_prepare_detectorsonce if map processing is not ready, after the user approves the download.geomap_prepare_knowledgeonce if the knowledge providers you need are not ready, also after approval. Without the knowledge base or a synced mirror, the default earthquake and fault lookups return no records, only warnings.geomap_register_mapwith the scan, thengeomap_process_image.geomap_georeferencewith the map, the CRS and at least two control points (four or more give an independent error check).geomap_query_map, which returns abundle_uri.geomap_render_knowledge_overlay, with nothing but names:
{
"map_uri": "geomap://maps/<map-id>",
"bundle_uri": "geomap://bundles/<bundle-id>.json"
}With no inline bundle or georef, the tool reads both from the server (mcp/adapter.py:584-588). The bundle comes through the registry, and the map’s current georeference comes from its stored record. It returns:
- a new
geomap://overlays/<id>.svg, plus a.pngwhen the raster step succeeds; - the annotations it hid as out of bounds, and the warnings;
- an inline preview capped at 1,536 pixels on its long edge (
mcp/adapter.py:600-642,mcp/images.py:17-18).
Before reading anything large, call resources/list: a name that begins UNREADABLE means read a crop instead.
FAQs
What does UNREADABLE mean in an MCP resource list?
In Stratigraphic Amenity, a resource whose name begins UNREADABLE is larger than the server’s 50 MiB read limit. Its description gives the size and the limit. A read of it fails with oversize_image. The tools still use the file on the server, so read a crop or the attached preview instead.
Why pass handles instead of data between MCP tools?
A model that carries a value from one tool result into the next call re-types it. Copying hundreds of records, it can drop one or change a digit. A geomap:// handle lets the next tool fetch the server’s own stored copy instead. A drill database works the same way, joining assays on sample numbers.
Can an AI geology agent read a scan larger than 50 MiB?
Not through resources/read. The stock server stops every read at 50 MiB, and no environment variable raises it. Source images of up to 200 MiB still register, and the tools still process and draw on them. The agent reads the crops and the downsampled preview instead.
Should I send a knowledge bundle inline or by bundle_uri?
By bundle_uri whenever the bundle came from this server. The overlay tool then reads the file the query wrote, so it draws from the query’s own records, hiding any outside the queried area. An inline bundle suits a bundle from another session, but the server checks only its shape. You can’t send both: the call fails with invalid_arguments.
Try It With Geocluster
Stratigraphic Amenity is one of the Geocluster tools, MCP servers that let AI agents work with geology data and maps.
An AI geology agent can follow these handles from scan to cited answer in the open-source georeferencing pipeline.


