Skip to content

Commit eda652c

Browse files
authored
Merge pull request #5 from i-norden/ian/dev
Ian/dev
2 parents 5e4dc10 + 2f2daa0 commit eda652c

7 files changed

Lines changed: 75 additions & 33 deletions

File tree

README.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# roteiro-agent
22

3-
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.
3+
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.
44

55
## Installation
66

@@ -41,9 +41,9 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
4141
```json
4242
{
4343
"mcpServers": {
44-
"cairn": {
44+
"roteiro": {
4545
"command": "roteiro-agent",
46-
"args": ["--server-url", "https://your-cairn-instance.com", "--api-key", "cairn_abc123"]
46+
"args": ["--server-url", "https://your-roteiro-instance.com", "--api-key", "roteiro_abc123"]
4747
}
4848
}
4949
}
@@ -56,9 +56,9 @@ Add to `.vscode/mcp.json`:
5656
```json
5757
{
5858
"servers": {
59-
"cairn": {
59+
"roteiro": {
6060
"command": "roteiro-agent",
61-
"args": ["--server-url", "http://localhost:8080", "--api-key", "cairn_abc123"]
61+
"args": ["--server-url", "http://localhost:8080", "--api-key", "roteiro_abc123"]
6262
}
6363
}
6464
}
@@ -71,9 +71,9 @@ Add to `.mcp.json`:
7171
```json
7272
{
7373
"mcpServers": {
74-
"cairn": {
74+
"roteiro": {
7575
"command": "roteiro-agent",
76-
"args": ["--server-url", "http://localhost:8080", "--api-key", "cairn_abc123"]
76+
"args": ["--server-url", "http://localhost:8080", "--api-key", "roteiro_abc123"]
7777
}
7878
}
7979
}
@@ -105,6 +105,7 @@ Add to `.mcp.json`:
105105
| `compute_route_matrix` | Origin-destination time/distance matrix |
106106
| `compute_service_area` | Distance-based service area polygons |
107107
| `list_operations` | Available geoprocessing operations |
108+
| `list_analysis_operations` | Available advanced analysis operations |
108109
| `browse_catalog` | Browse the built-in data catalog |
109110
| `browse_catalog_enhanced` | Browse enhanced catalog with filters |
110111
| `get_catalog_entry` | Get enhanced catalog entry by ID |

SKILL.md

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -43,28 +43,17 @@ Queries must be SELECT-only (read-only).
4343

4444
## Geoprocessing Operations
4545

46-
Use `run_process` for single operations or `run_pipeline` for chains. Available operations include:
47-
48-
| Operation | Description | Key Parameters |
49-
|-----------|-------------|----------------|
50-
| `buffer` | Create buffer around features | `distance` (meters) |
51-
| `clip` | Clip features by a mask | `clip_dataset` |
52-
| `simplify` | Reduce geometry complexity | `tolerance` |
53-
| `reproject` | Change coordinate system | `target_crs` (e.g. "EPSG:4326") |
54-
| `centroid` | Calculate centroids ||
55-
| `convex_hull` | Convex hull of features ||
56-
| `intersection` | Intersect two datasets | `overlay_dataset` |
57-
| `union` | Union two datasets | `overlay_dataset` |
58-
| `difference` | Subtract one dataset from another | `overlay_dataset` |
59-
| `sjoin` | Spatial join | `join_dataset`, `predicate` |
60-
| `dissolve` | Merge features by attribute | `by` (field name) |
61-
| `voronoi` | Voronoi polygons ||
62-
| `spatial_stats` | Descriptive statistics ||
63-
| `morans_i` | Spatial autocorrelation | `attribute` |
64-
| `hotspot` | Getis-Ord Gi* hotspot analysis | `attribute` |
65-
| `kernel_density` | Density estimation | `bandwidth`, `cell_size` |
66-
67-
Use `list_operations` to get the full list with parameter schemas.
46+
Use `run_process` for single operations or `run_pipeline` for chains.
47+
48+
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`).
49+
50+
Important parameter names for common ops:
51+
- `clip` uses `mask`
52+
- `sjoin` uses `right` and `predicate`
53+
- `reproject` uses `from_crs` and `to_crs`
54+
- `dissolve` uses `group_by`
55+
56+
Use `list_analysis_operations` for advanced analysis catalog endpoints under `/api/analysis/operations`.
6857

6958
## Data Catalog & STAC
7059

@@ -115,7 +104,7 @@ GROUP BY r.name ORDER BY count DESC
115104
{
116105
"steps": [
117106
{"operation": "buffer", "input": "schools", "params": {"distance": 1000}},
118-
{"operation": "intersection", "params": {"overlay_dataset": "residential_zones"}}
107+
{"operation": "intersect", "params": {"mask": "residential_zones"}}
119108
]
120109
}
121110
```

main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//
55
// Usage:
66
//
7-
// roteiro-agent --server-url http://localhost:8080 --api-key cairn_abc123
7+
// roteiro-agent --server-url http://localhost:8080 --api-key roteiro_abc123
88
package main
99

1010
import (

mcp/client.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,18 @@ func (c *Client) ListOperations() (json.RawMessage, error) {
410410
return json.RawMessage(body), nil
411411
}
412412

413+
// ListAnalysisOperations calls GET /api/analysis/operations.
414+
func (c *Client) ListAnalysisOperations() (json.RawMessage, error) {
415+
body, code, err := c.get("/api/analysis/operations", nil)
416+
if err != nil {
417+
return nil, err
418+
}
419+
if code != http.StatusOK {
420+
return nil, fmt.Errorf("GET /api/analysis/operations returned %d: %s", code, truncate(body, 500))
421+
}
422+
return json.RawMessage(body), nil
423+
}
424+
413425
// GetDatasetSchema calls GET /api/datasets/{name}/schema.
414426
func (c *Client) GetDatasetSchema(name string) (json.RawMessage, error) {
415427
body, code, err := c.get("/api/datasets/"+url.PathEscape(name)+"/schema", nil)

mcp/handlers.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ func HandleToolCall(client *Client, name string, args json.RawMessage) (string,
6666
return handleComputeServiceArea(client, params)
6767
case "list_operations":
6868
return handleListOperations(client)
69+
case "list_analysis_operations":
70+
return handleListAnalysisOperations(client)
6971
case "browse_catalog":
7072
return handleBrowseCatalog(client, params)
7173
case "browse_catalog_enhanced":
@@ -514,6 +516,14 @@ func handleListOperations(client *Client) (string, error) {
514516
return formatJSON(data), nil
515517
}
516518

519+
func handleListAnalysisOperations(client *Client) (string, error) {
520+
data, err := client.ListAnalysisOperations()
521+
if err != nil {
522+
return "", err
523+
}
524+
return formatJSON(data), nil
525+
}
526+
517527
// requireString extracts a required string parameter.
518528
func requireString(params map[string]interface{}, key string) (string, error) {
519529
v, ok := params[key]

mcp/server_test.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ func TestToolsList(t *testing.T) {
9898
"diff_datasets", "execute_sql", "list_spatial_tables", "get_duckdb_info",
9999
"list_duckdb_datasets", "geocode", "reverse_geocode", "compute_route",
100100
"compute_isochrone", "compute_route_matrix", "compute_service_area",
101-
"list_operations", "browse_catalog", "browse_catalog_enhanced",
101+
"list_operations", "list_analysis_operations", "browse_catalog", "browse_catalog_enhanced",
102102
"get_catalog_entry", "list_catalog_categories", "list_catalog_tags", "import_from_catalog", "browse_stac_catalog",
103103
"browse_stac_collections", "browse_stac_items", "import_stac_asset",
104104
"search_stac",
@@ -109,6 +109,31 @@ func TestToolsList(t *testing.T) {
109109
}
110110
}
111111

112+
func TestToolsCall_ListAnalysisOperations(t *testing.T) {
113+
mux := http.NewServeMux()
114+
mux.HandleFunc("GET /api/analysis/operations", func(w http.ResponseWriter, r *http.Request) {
115+
w.Header().Set("Content-Type", "application/json")
116+
fmt.Fprint(w, `{"operations":[{"id":"topology","name":"Topology Analysis"}]}`)
117+
})
118+
srv := testServer(t, mux)
119+
120+
resp := sendRequest(t, srv, "tools/call", 22, map[string]interface{}{
121+
"name": "list_analysis_operations",
122+
"arguments": map[string]interface{}{},
123+
})
124+
125+
if resp.Error != nil {
126+
t.Fatalf("unexpected error: %v", resp.Error)
127+
}
128+
result, _ := resp.Result.(map[string]interface{})
129+
content, _ := result["content"].([]interface{})
130+
first, _ := content[0].(map[string]interface{})
131+
text, _ := first["text"].(string)
132+
if !strings.Contains(text, "Topology Analysis") {
133+
t.Errorf("response should contain operation name, got: %s", text)
134+
}
135+
}
136+
112137
func TestToolsCall_ListDatasets(t *testing.T) {
113138
mux := http.NewServeMux()
114139
mux.HandleFunc("GET /datasets", func(w http.ResponseWriter, r *http.Request) {

mcp/tools.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ func AllTools() []Tool {
108108
},
109109
{
110110
Name: "run_process",
111-
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.",
111+
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.",
112112
InputSchema: InputSchema{
113113
Type: "object",
114114
Properties: map[string]PropertySchema{
@@ -303,6 +303,11 @@ func AllTools() []Tool {
303303
Description: "List all available geoprocessing operations with their parameter schemas. Useful for discovering what operations are supported and what parameters they accept.",
304304
InputSchema: InputSchema{Type: "object"},
305305
},
306+
{
307+
Name: "list_analysis_operations",
308+
Description: "List all available advanced analysis operations from /api/analysis/operations.",
309+
InputSchema: InputSchema{Type: "object"},
310+
},
306311
{
307312
Name: "browse_catalog",
308313
Description: "Browse the built-in data catalog to discover available datasets for import. Supports text search and category filtering.",

0 commit comments

Comments
 (0)