drillsite-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@drillsite-mcpRank the top 5 zones to drill first and explain the main driver for each."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
drillsite-mcp
A production-ready Model Context Protocol (MCP) server in Python, built on the official MCP Python SDK. It lets an AI agent (Claude Desktop, Claude Code, or any MCP client) query a drill-site prospectivity API through validated tools, resources and a prompt.
The sample backend is a small FastAPI service that scores mining exploration zones, modelled on the prospectivity platform I run at Metalgenomic. The data here is synthetic.
MCP client (Claude, Inspector)
│ stdio | SSE | streamable HTTP
▼
drillsite-mcp (MCPServer: tools · resources · prompt)
│ httpx, API key, timeouts, safe error mapping
▼
Drill-site Prospectivity API (FastAPI + Pydantic)FastMCP → MCPServer. In SDK v2 the
FastMCPclass was renamedMCPServer(from mcp.server.mcpserver import MCPServer). The decorator API is the same. To run this on SDK v1, usefrom mcp.server.fastmcp import FastMCPand pinmcp<2.
What it exposes
Kind | Name | What it does |
Tool |
| Zones at or above a score, best first (read-only) |
Tool |
| One zone with score, drivers and field notes (read-only) |
Tool |
| Top N zones to drill first, with the main driver of each (read-only) |
Tool |
| Writes a field observation to the backend |
Resource |
| All zones as JSON |
Resource template |
| One zone as JSON |
Prompt |
| Asks the agent for a drilling order grounded in the tools |
Related MCP server: Earth Grounding MCP
Production concerns handled
Input validation before any request leaves the server. Zone ids must match
Z-000, scores are bounded 0–1,top_n1–20, notes ≤ 500 characters. Invalid input is rejected by the SDK's Pydantic validation and never reaches the API.One shared HTTP client created in the server lifespan and closed on shutdown, with connect/read timeouts and the API key sent as a header.
Safe error mapping. Timeouts, 401/403, 404, 422 and 5xx become short
ToolErrormessages. Status codes, URLs and keys are logged to stderr, never returned to the model.Prompt-injection mitigation. Text written by people (field notes) comes back under
untrusted_field_notes, with control characters stripped and length capped. The server instructions and thedrill_planprompt tell the agent to treat it as data.Tool annotations (
readOnlyHint,destructiveHint, …) so clients can decide which calls need user confirmation.stdout is reserved for the protocol under stdio; logs go to stderr.
End-to-end tests run MCP client → server → httpx → FastAPI in one process.
Quick start
Requires Python 3.11+.
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
pytest # 10 tests, all in-processRun the API (terminal 1):
export DRILLSITE_API_KEY=dev-key # Windows PowerShell: $env:DRILLSITE_API_KEY="dev-key"
drillsite-api # http://127.0.0.1:8000 (docs at /docs)Run the MCP server (terminal 2), on either transport:
drillsite-mcp --transport stdio # for Claude Desktop / Claude Code
drillsite-mcp --transport sse --port 8001 # SSE at http://127.0.0.1:8001/sse
drillsite-mcp --transport streamable-http --port 8001 # http://127.0.0.1:8001/mcpConfiguration (environment variables):
Variable | Default | Purpose |
|
| Backend base URL |
|
| Sent as |
|
| Defaults for the CLI flags |
|
| stderr log level |
Connect from Claude Desktop or Claude Code (stdio)
{
"mcpServers": {
"drillsite": {
"command": "/absolute/path/to/.venv/bin/drillsite-mcp",
"args": ["--transport", "stdio"],
"env": { "DRILLSITE_API_KEY": "dev-key" }
}
}
}Claude Code: claude mcp add drillsite -- /absolute/path/to/.venv/bin/drillsite-mcp --transport stdio
Debugging with the MCP Inspector
The MCP Inspector is the official tool for exercising an MCP server by hand. It needs Node.js. Keep the API running.
1. UI mode over stdio. The Inspector launches the server for you:
npx @modelcontextprotocol/inspector -e DRILLSITE_API_KEY=dev-key drillsite-mcp --transport stdioOpen the URL it prints. Click Connect, then:
Tools → List Tools: you should see the four tools with their JSON schemas.
Run
rank_zoneswithtop_n = 2→Z-001,Z-004.Run
get_zonewithzone_id = ../admin→ a validation error. The request never reached the API (check terminal 1: no log line).Resources: read
zones://all, then the templatezones://{zone_id}withZ-003.Prompts: get
drill_plan.
2. UI mode over SSE. Start the server with --transport sse --port 8001, then in
the Inspector choose transport SSE and URL http://127.0.0.1:8001/sse.
3. CLI mode (scriptable, good for CI smoke tests):
npx @modelcontextprotocol/inspector --cli drillsite-mcp --transport stdio --method tools/list
npx @modelcontextprotocol/inspector --cli drillsite-mcp --transport stdio \
--method tools/call --tool-name rank_zones --tool-arg top_n=2
npx @modelcontextprotocol/inspector --cli drillsite-mcp --transport stdio \
--method resources/read --uri zones://Z-001Common problems
Symptom | Likely cause |
Inspector connects, then the server dies at once | Something printed to stdout. Under stdio only protocol messages may go there; use logging (stderr). |
Every tool returns "not authorised" |
|
"The prospectivity API is unreachable" | The API is not running, or |
SSE connects but tools time out | Wrong URL: SSE is |
Set LOG_LEVEL=DEBUG to see every backend call in stderr.
Docker
docker build -t drillsite-mcp .
docker run --rm -p 8001:8001 -e DRILLSITE_API_URL=http://host.docker.internal:8000 drillsite-mcpThe image runs the server over SSE on port 8001 as a non-root user.
Project layout
src/drillsite_mcp/api.py sample FastAPI backend
src/drillsite_mcp/server.py MCP server: tools, resources, prompt, transports
tests/test_server.py end-to-end tests through a real MCP clientLicense
MIT
Available Tools
4 toolsadd_field_noteA
Attach a short field observation to a zone. This writes to the backend.
| Name | Required | Description | Default |
|---|---|---|---|
| note | Yes | ||
| zone_id | Yes | Zone id, e.g. Z-001 |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false and destructiveHint=false, so the safety profile is clear. The description's 'This writes to the backend' corroborates mutation but adds little new beyond the annotations. No mention of error behavior or whether notes are append-only vs overwriting.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two terse sentences, front-loaded with the action and resource. Every sentence earns its place with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists so return values need not be described, and annotations cover the write/safety profile. What remains missing is whether the zone must pre-exist and whether repeated notes overwrite or accumulate, a minor gap for such a simple tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 50% — zone_id is documented in the schema with a format example, while note has only min/max length constraints. The description's 'short' hints at note brevity but adds no meaning beyond what the schema constraints convey.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'Attach a short field observation to a zone.' This is clearly distinguishable from read-only siblings (list_zones, get_zone, rank_zones) by the write action. It doesn't name siblings explicitly, but the operation type makes selection obvious.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by 'short field observation' — an agent can infer this is for brief, zone-scoped notes. However, there is no explicit when-to-use guidance, no mention of alternatives, and no note about prerequisites (e.g., whether the zone must exist).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_zoneARead-only
Get one zone with its score, the drivers behind it, and field notes.
| Name | Required | Description | Default |
|---|---|---|---|
| zone_id | Yes | Zone id, e.g. Z-001 |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=false, so the safety profile is covered. The description adds a useful preview of the payload shape (score, drivers, field notes), but says nothing about not-found behavior or whether drivers/notes can be empty.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One sentence, front-loaded with the verb and resource, with the returned content as a tight trailing clause. Nothing is padded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be detailed, and the single parameter is fully documented in the schema. Only the missing not-found/empty-section behavior keeps it from a 5.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% with a single required parameter, and the schema itself supplies the 'Z-001' format example and regex pattern. The description adds no meaning beyond that, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Get one zone') and enumerates what comes back (score, drivers, field notes), which distinguishes it from list_zones by singularity. It never names the sibling explicitly, so the differentiation is inferential rather than stated.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The singular 'one zone' plus the required zone_id implies this is the detail lookup after a list_zones/rank_zones call, but the description never says when to use it versus those siblings or what to do if the zone is unknown. Usage is only implied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_zonesARead-only
List candidate drill zones at or above a score, best first.
| Name | Required | Description | Default |
|---|---|---|---|
| region | No | Region filter | |
| min_score | No | Minimum score, 0-1 |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=false, so safety is covered. The description adds genuinely new behavior in "best first" (sort order) and "candidate" (pre-filtered result set), but says nothing about result limits, pagination, or what happens with a null region. Useful but thin given the low bar.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single sentence, zero filler, with the filter condition and sort order front-loaded. Every clause carries information the agent needs.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present and annotations covering the read-only profile, the description does not need to explain return values. The remaining gap is the unaddressed relationship to rank_zones and the absence of any result-size behavior, which leaves the agent guessing at boundaries.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and both parameters carry their own descriptions ("Region filter", "Minimum score, 0-1"), so the schema does the heavy lifting. The description reinforces that min_score is inclusive ("at or above") but adds no format detail for region, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description pairs a specific verb ("List") with a specific resource ("candidate drill zones") plus the qualifying condition ("at or above a score") and output ordering. It is distinct from get_zone by being plural/listing, but it never acknowledges rank_zones, which sounds closely related, so sibling differentiation is incomplete.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase "at or above a score" implies the usage context (threshold-based candidate selection), which is more than nothing. However, there is no explicit when-to-use/when-not guidance and no routing to rank_zones or get_zone for agents weighing alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
rank_zonesARead-only
Rank the top N zones to drill first, with the main driver of each score.
| Name | Required | Description | Default |
|---|---|---|---|
| top_n | No | How many zones to return |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the key safety profile: readOnlyHint=true and openWorldHint=false. The description adds that results are ranked and include a main driver, but does not expand much beyond that, which is acceptable given the annotation coverage and output schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single compact sentence with the purpose and output summary front-loaded. Every element earns its place with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The output schema exists, annotations cover read-only and closed-world behavior, and the description explains that results are ranked with a main driver. It is complete enough for invocation, though the ranking methodology could be clearer.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and top_n is already documented as how many zones to return. The description echoes 'top N' but adds no extra semantics such as ranking criteria or tie-breaking.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: rank zones and return the top N for drilling, including each score's main driver. It is distinguishable from list_zones and get_zone by the ranking/prioritization purpose, though it does not explicitly name siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Implies the tool is for prioritizing which zones to drill first, but gives no explicit when-to-use guidance, prerequisites, or alternatives such as list_zones or get_zone. The usage context is inferable rather than stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
4 tool updates
v0.1.0- First observed
add_field_note - First observed
get_zone - First observed
list_zones - First observed
rank_zones
TDQS
Scored across 4 tools
list_zones and rank_zones both return zones ordered by score, so an agent may hesitate between threshold-based listing and top-N ranking. The descriptions do distinguish filtering from ranking and driver emphasis, but the overlap is real. get_zone and add_field_note are clearly distinct.
All tools follow a consistent snake_case verb_noun pattern: list_zones, get_zone, rank_zones, add_field_note. Singular/plural use is also predictable for one-zone versus many-zone operations.
Four tools is well-scoped for a drill-zone ranking and field-note server. Each tool supports a distinct part of the workflow without excessive surface area.
The core workflow of finding, inspecting, ranking, and annotating zones is covered. The main gap is that field notes are append-only, with no update or delete operation, which is a minor but noticeable lifecycle limitation.
Maintenance
Related MCP Connectors
Context and control for any AI. Agent identity, scoped access, and proof across tools.
Find, vet, and run MCP tools through a secure audited gateway with prompt-injection risk scoring
Verifiable Earth ground truth for AI agents: water, hazard, ground stability, resource, with proof.
GIS tools for AI agents: 65 free tools + 8 paid (hazard/site-scouting/GeoJSON export)
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to interact with the OSDU® data platform through Model Context Protocol, providing tools for search, storage, schema management, and dataset operations.2Apache 2.0
- FlicenseBqualityCmaintenanceEnables AI agents to query verifiable physical-world ground truth for any point on Earth — water availability, seismic and space-weather hazard, ground stability, and resource indications — with every answer backed by a Bitcoin-anchored provenance record that can be independently verified. It also lets agents check whether an Earth-science hypothesis has already been tested, list documented nulls and retractions, and run bounded controlled tests that return UNTESTABLE rather than fabricate a result.11-
- AlicenseNot gradedqualityAmaintenanceEnables agents to retrieve ground elevations worldwide, profile route ascent/descent and grades, sample grid areas for high/low points and relief, and test whether terrain blocks a sightline, drawing on USGS 3DEP and Open Topo Data with per-value provenance.363 npm2Apache 2.0
- AlicenseNot gradedqualityCmaintenanceEnables LLM agents to access third-party APIs through a least-privilege set of audited tools with allowlists, per-caller scopes, label gates, write confirmation, limits, and PII-masked logging. It restricts reads to labeled data, prevents sending or destructive actions, and records every allowed or denied call.MIT