Adding tools
You can extend the agent in three complementary ways:
- MCP tools — recommended for geospatial work; any FastMCP server, in-process or over HTTP.
@ai_tool— plain Python functions registered in the core tool registry (with validation, retries, circuit breaker, HitL).@frontend_tool— tools that trigger UI events in the React client.
MCP tools (recommended)
from mcp.server.fastmcp import FastMCP
from stacgis_ai import MCPClient, AgentLoop, ...
server = FastMCP("geo-tools")
@server.tool()
def geocode_address(address: str) -> str:
"""Geocode an address or place name to lat/lon."""
...
@server.tool()
async def calculate_route(start_lat: float, start_lon: float,
end_lat: float, end_lon: float) -> str:
"""Driving route between two coordinates (GeoJSON LineString)."""
...
client = MCPClient(server=server) # direct in-memory mode
await client.connect()
# or over HTTP:
# client = MCPClient(server_url="http://tools.internal:9000/mcp")
Tool names, docstrings and type hints are converted into the JSON tool schemas the LLM sees — write the docstring like a tool description: say what it does and when to use it.
To gate a tool behind human approval, list its name in MCPClient's
approval_tools set — e.g. MCPClient(server=server, approval_tools={"my_tool"}).
This surfaces x_requires_approval: true on the tool schema so the loop
pauses for a decision (see ApprovalRegistry in the
Architecture docs).
@ai_tool (core registry)
from stacgis_ai import ai_tool
@ai_tool(description="Get the current weather for a city",
requires_approval=False) # set True to require HitL approval
async def get_weather(city: str, units: str = "celsius") -> dict:
...
@ai_tool functions live in the ToolRegistry and are executed by
AgentOrchestrator, which applies per-tool retry policies and circuit
breakers. Expose them with create_flow_router() (health, tool listing,
single & batch execute endpoints).
@frontend_tool (UI commands)
These tools do no server-side work — they tell the frontend what to do.
The agent loop emits an SSE step with state: "frontend_tool", and the
React useAgent hook fires your onFrontendEvent callback:
from stacgis_ai.frontend_tools import frontend_tool
@frontend_tool(event="zoom_to", description="Zoom the map to (lat, lon)")
async def zoom_to_location(lat: float, lon: float, zoom: int = 14):
"""Zoom the map to a coordinate."""
@frontend_tool(event="draw_geometry",
description="Draw a GeoJSON feature on the map")
async def draw_geometry(geojson: str, layer_id: str = "agent_layer"):
"""Render a GeoJSON geometry as an overlay."""
Built-in GIS events already registered by the package:
| Event | Purpose |
|---|---|
zoom_to | Navigate the map viewport to a coordinate |
highlight_feature | Highlight a feature by id |
show_toast | Display a toast notification |
open_panel | Open a named side panel / drawer |
draw_geometry | Draw a GeoJSON overlay |
set_layer_visibility | Show / hide a map layer |