From 73fdd12ccb96f73a819f0d0ebe9737b49fe6f415 Mon Sep 17 00:00:00 2001 From: i-norden Date: Wed, 11 Mar 2026 02:06:50 -0400 Subject: [PATCH 1/2] update mcp --- main.go | 2 +- mcp/client.go | 12 ++++++++++++ mcp/handlers.go | 10 ++++++++++ mcp/server_test.go | 27 ++++++++++++++++++++++++++- mcp/tools.go | 7 ++++++- 5 files changed, 55 insertions(+), 3 deletions(-) diff --git a/main.go b/main.go index e09f71c..4e526f5 100644 --- a/main.go +++ b/main.go @@ -4,7 +4,7 @@ // // Usage: // -// roteiro-agent --server-url http://localhost:8080 --api-key cairn_abc123 +// roteiro-agent --server-url http://localhost:8080 --api-key roteiro_abc123 package main import ( diff --git a/mcp/client.go b/mcp/client.go index b2aa51f..2794775 100644 --- a/mcp/client.go +++ b/mcp/client.go @@ -410,6 +410,18 @@ func (c *Client) ListOperations() (json.RawMessage, error) { return json.RawMessage(body), nil } +// ListAnalysisOperations calls GET /api/analysis/operations. +func (c *Client) ListAnalysisOperations() (json.RawMessage, error) { + body, code, err := c.get("/api/analysis/operations", nil) + if err != nil { + return nil, err + } + if code != http.StatusOK { + return nil, fmt.Errorf("GET /api/analysis/operations returned %d: %s", code, truncate(body, 500)) + } + return json.RawMessage(body), nil +} + // GetDatasetSchema calls GET /api/datasets/{name}/schema. func (c *Client) GetDatasetSchema(name string) (json.RawMessage, error) { body, code, err := c.get("/api/datasets/"+url.PathEscape(name)+"/schema", nil) diff --git a/mcp/handlers.go b/mcp/handlers.go index 94737f8..817d8df 100644 --- a/mcp/handlers.go +++ b/mcp/handlers.go @@ -66,6 +66,8 @@ func HandleToolCall(client *Client, name string, args json.RawMessage) (string, return handleComputeServiceArea(client, params) case "list_operations": return handleListOperations(client) + case "list_analysis_operations": + return handleListAnalysisOperations(client) case "browse_catalog": return handleBrowseCatalog(client, params) case "browse_catalog_enhanced": @@ -514,6 +516,14 @@ func handleListOperations(client *Client) (string, error) { return formatJSON(data), nil } +func handleListAnalysisOperations(client *Client) (string, error) { + data, err := client.ListAnalysisOperations() + if err != nil { + return "", err + } + return formatJSON(data), nil +} + // requireString extracts a required string parameter. func requireString(params map[string]interface{}, key string) (string, error) { v, ok := params[key] diff --git a/mcp/server_test.go b/mcp/server_test.go index 041db1c..833d18f 100644 --- a/mcp/server_test.go +++ b/mcp/server_test.go @@ -98,7 +98,7 @@ func TestToolsList(t *testing.T) { "diff_datasets", "execute_sql", "list_spatial_tables", "get_duckdb_info", "list_duckdb_datasets", "geocode", "reverse_geocode", "compute_route", "compute_isochrone", "compute_route_matrix", "compute_service_area", - "list_operations", "browse_catalog", "browse_catalog_enhanced", + "list_operations", "list_analysis_operations", "browse_catalog", "browse_catalog_enhanced", "get_catalog_entry", "list_catalog_categories", "list_catalog_tags", "import_from_catalog", "browse_stac_catalog", "browse_stac_collections", "browse_stac_items", "import_stac_asset", "search_stac", @@ -109,6 +109,31 @@ func TestToolsList(t *testing.T) { } } +func TestToolsCall_ListAnalysisOperations(t *testing.T) { + mux := http.NewServeMux() + mux.HandleFunc("GET /api/analysis/operations", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + fmt.Fprint(w, `{"operations":[{"id":"topology","name":"Topology Analysis"}]}`) + }) + srv := testServer(t, mux) + + resp := sendRequest(t, srv, "tools/call", 22, map[string]interface{}{ + "name": "list_analysis_operations", + "arguments": map[string]interface{}{}, + }) + + if resp.Error != nil { + t.Fatalf("unexpected error: %v", resp.Error) + } + result, _ := resp.Result.(map[string]interface{}) + content, _ := result["content"].([]interface{}) + first, _ := content[0].(map[string]interface{}) + text, _ := first["text"].(string) + if !strings.Contains(text, "Topology Analysis") { + t.Errorf("response should contain operation name, got: %s", text) + } +} + func TestToolsCall_ListDatasets(t *testing.T) { mux := http.NewServeMux() mux.HandleFunc("GET /datasets", func(w http.ResponseWriter, r *http.Request) { diff --git a/mcp/tools.go b/mcp/tools.go index d8b2f5d..3c9544c 100644 --- a/mcp/tools.go +++ b/mcp/tools.go @@ -108,7 +108,7 @@ func AllTools() []Tool { }, { Name: "run_process", - Description: "Run a single geoprocessing operation on a dataset. Operations include: buffer, clip, simplify, reproject, centroid, convex_hull, intersection, union, difference, sjoin, dissolve, voronoi, spatial_stats, morans_i, hotspot, kernel_density, and more.", + Description: "Run a single geoprocessing operation on a dataset via /api/process. Use list_operations first to discover the live operation catalog and parameter names on the connected server.", InputSchema: InputSchema{ Type: "object", Properties: map[string]PropertySchema{ @@ -303,6 +303,11 @@ func AllTools() []Tool { Description: "List all available geoprocessing operations with their parameter schemas. Useful for discovering what operations are supported and what parameters they accept.", InputSchema: InputSchema{Type: "object"}, }, + { + Name: "list_analysis_operations", + Description: "List all available advanced analysis operations from /api/analysis/operations.", + InputSchema: InputSchema{Type: "object"}, + }, { Name: "browse_catalog", Description: "Browse the built-in data catalog to discover available datasets for import. Supports text search and category filtering.", From 2f2daa0496777155f907110ae7ca2ea1e8ad13ac Mon Sep 17 00:00:00 2001 From: i-norden Date: Wed, 11 Mar 2026 02:07:12 -0400 Subject: [PATCH 2/2] update docs --- README.md | 15 ++++++++------- SKILL.md | 35 ++++++++++++----------------------- 2 files changed, 20 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index b3b0265..85c6853 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # roteiro-agent -MCP (Model Context Protocol) server for [Roteiro](roteiro.io) — a spatial data platform. Enables AI agents (Claude Desktop, VS Code, Cursor) to work with geospatial datasets, run geoprocessing operations, execute PostGIS queries, and more. +MCP (Model Context Protocol) server for Roteiro, a spatial data platform. Enables AI agents (Claude Desktop, VS Code, Cursor) to work with geospatial datasets, run geoprocessing operations, execute SQL, and more. ## Installation @@ -41,9 +41,9 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`: ```json { "mcpServers": { - "cairn": { + "roteiro": { "command": "roteiro-agent", - "args": ["--server-url", "https://your-cairn-instance.com", "--api-key", "cairn_abc123"] + "args": ["--server-url", "https://your-roteiro-instance.com", "--api-key", "roteiro_abc123"] } } } @@ -56,9 +56,9 @@ Add to `.vscode/mcp.json`: ```json { "servers": { - "cairn": { + "roteiro": { "command": "roteiro-agent", - "args": ["--server-url", "http://localhost:8080", "--api-key", "cairn_abc123"] + "args": ["--server-url", "http://localhost:8080", "--api-key", "roteiro_abc123"] } } } @@ -71,9 +71,9 @@ Add to `.mcp.json`: ```json { "mcpServers": { - "cairn": { + "roteiro": { "command": "roteiro-agent", - "args": ["--server-url", "http://localhost:8080", "--api-key", "cairn_abc123"] + "args": ["--server-url", "http://localhost:8080", "--api-key", "roteiro_abc123"] } } } @@ -105,6 +105,7 @@ Add to `.mcp.json`: | `compute_route_matrix` | Origin-destination time/distance matrix | | `compute_service_area` | Distance-based service area polygons | | `list_operations` | Available geoprocessing operations | +| `list_analysis_operations` | Available advanced analysis operations | | `browse_catalog` | Browse the built-in data catalog | | `browse_catalog_enhanced` | Browse enhanced catalog with filters | | `get_catalog_entry` | Get enhanced catalog entry by ID | diff --git a/SKILL.md b/SKILL.md index 5efa580..664be83 100644 --- a/SKILL.md +++ b/SKILL.md @@ -43,28 +43,17 @@ Queries must be SELECT-only (read-only). ## Geoprocessing Operations -Use `run_process` for single operations or `run_pipeline` for chains. Available operations include: - -| Operation | Description | Key Parameters | -|-----------|-------------|----------------| -| `buffer` | Create buffer around features | `distance` (meters) | -| `clip` | Clip features by a mask | `clip_dataset` | -| `simplify` | Reduce geometry complexity | `tolerance` | -| `reproject` | Change coordinate system | `target_crs` (e.g. "EPSG:4326") | -| `centroid` | Calculate centroids | — | -| `convex_hull` | Convex hull of features | — | -| `intersection` | Intersect two datasets | `overlay_dataset` | -| `union` | Union two datasets | `overlay_dataset` | -| `difference` | Subtract one dataset from another | `overlay_dataset` | -| `sjoin` | Spatial join | `join_dataset`, `predicate` | -| `dissolve` | Merge features by attribute | `by` (field name) | -| `voronoi` | Voronoi polygons | — | -| `spatial_stats` | Descriptive statistics | — | -| `morans_i` | Spatial autocorrelation | `attribute` | -| `hotspot` | Getis-Ord Gi* hotspot analysis | `attribute` | -| `kernel_density` | Density estimation | `bandwidth`, `cell_size` | - -Use `list_operations` to get the full list with parameter schemas. +Use `run_process` for single operations or `run_pipeline` for chains. + +Always call `list_operations` first to fetch the live server operation catalog and parameter names. The current server supports a broad set including vector ops (`buffer`, `clip`, `intersect`, `difference`, `dissolve`, `sjoin`), geometry conversion (`centroid`, `convex_hull`, `feature_to_point`, `points_to_line`), stats (`spatial_stats`, `morans_i`, `hotspot`, `kernel_density`), interpolation (`interpolate_idw`, `ordinary_kriging`), validation (`validate`, `make_valid`, `validate_topology`), and optimization (`solve_vrp`, `p_median`, `mclp`). + +Important parameter names for common ops: +- `clip` uses `mask` +- `sjoin` uses `right` and `predicate` +- `reproject` uses `from_crs` and `to_crs` +- `dissolve` uses `group_by` + +Use `list_analysis_operations` for advanced analysis catalog endpoints under `/api/analysis/operations`. ## Data Catalog & STAC @@ -115,7 +104,7 @@ GROUP BY r.name ORDER BY count DESC { "steps": [ {"operation": "buffer", "input": "schools", "params": {"distance": 1000}}, - {"operation": "intersection", "params": {"overlay_dataset": "residential_zones"}} + {"operation": "intersect", "params": {"mask": "residential_zones"}} ] } ```