Skip to main content

Adding tools

You can extend the agent in three complementary ways:

  1. MCP tools — recommended for geospatial work; any FastMCP server, in-process or over HTTP.
  2. @ai_tool — plain Python functions registered in the core tool registry (with validation, retries, circuit breaker, HitL).
  3. @frontend_tool — tools that trigger UI events in the React client.
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.

tip

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:

EventPurpose
zoom_toNavigate the map viewport to a coordinate
highlight_featureHighlight a feature by id
show_toastDisplay a toast notification
open_panelOpen a named side panel / drawer
draw_geometryDraw a GeoJSON overlay
set_layer_visibilityShow / hide a map layer