@toolbeltai/skills 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2026 toolbeltai
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # @toolbeltai/skills
2
+
3
+ > Official Toolbelt skills for Claude Code and other MCP-capable agents —
4
+ > SQL, vectors, graphs, geospatial, streaming, all as named slash commands.
5
+
6
+ ## Install (standalone)
7
+
8
+ ```bash
9
+ npx @toolbeltai/skills install
10
+ ```
11
+
12
+ Copies every skill to `~/.claude/skills/toolbelt/`. Restart Claude Code and
13
+ you'll see them as slash commands: `/run-toolbelt`, `/geo-analyst`, …
14
+
15
+ No account required; no network calls to Toolbelt. Skills work against any
16
+ Toolbelt MCP server (cloud or self-hosted).
17
+
18
+ ## Install (via Toolbelt CLI)
19
+
20
+ If you want the skills **and** a preconfigured MCP connection in one step:
21
+
22
+ ```bash
23
+ npx @toolbeltai/cli
24
+ ```
25
+
26
+ Provisions an anonymous Toolbelt account, registers the MCP server in your
27
+ agent, and installs these skills — all at once.
28
+
29
+ ## Skills
30
+
31
+ | Skill | Command | What it does |
32
+ | --- | --- | --- |
33
+ | [run-toolbelt](run-toolbelt/) | `/run-toolbelt` | Onboard, upload docs, connect data, ask questions |
34
+ | [geo-analyst](geo-analyst/) | `/geo-analyst` | GPU-accelerated geospatial — queries and map rendering |
35
+ | [knowledge-graph](knowledge-graph/) | `/knowledge-graph` | Auto-extract entities from docs, explore with Cypher |
36
+ | [multi-agent-workspace](multi-agent-workspace/) | `/multi-agent-workspace` | Shareable MCP URL for multi-agent collaboration |
37
+ | [sql-analyst](sql-analyst/) | `/sql-analyst` | Upload a CSV, ask plain English, get SQL + results |
38
+ | [streaming-analyst](streaming-analyst/) | `/streaming-analyst` | Connect Kafka, aggregate, detect anomalies |
39
+ | [vector-search](vector-search/) | `/vector-search` | Upload a document, retrieve semantically similar passages |
40
+ | [data-blend](data-blend/) | `/data-blend` | Combine multiple tables with cross-table JOINs |
41
+
42
+ ## Works with
43
+
44
+ Claude Code · OpenClaw · Cursor · Gemini CLI · Codex CLI · Windsurf · any MCP client
45
+
46
+ ## Commands
47
+
48
+ ```bash
49
+ npx @toolbeltai/skills install # install all skills
50
+ npx @toolbeltai/skills uninstall # remove them
51
+ npx @toolbeltai/skills list # list what would be installed
52
+ npx @toolbeltai/skills path # print install target
53
+ ```
54
+
55
+ ## Docs
56
+
57
+ - Skill reference: <https://docs.toolbelt.ai>
58
+ - Toolbelt CLI: <https://github.com/toolbeltai/toolbelt>
59
+ - Releasing: [RELEASING.md](./RELEASING.md)
60
+ - Contributing: [CONTRIBUTING.md](./CONTRIBUTING.md)
61
+
62
+ ## License
63
+
64
+ MIT — see [LICENSE](./LICENSE).
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+ sleep 1
3
+ echo '{"mcpUrl": "https://mcp.toolbelt.ai/t/tb_1234567890/8bf91bad-02df-4137-97ac-69a006bac193/mcp"}'
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env bash
2
+ # Simulates the geo-analyst skill running inside Claude Code
3
+
4
+ G='\033[0;32m' # green
5
+ B='\033[0;34m' # blue
6
+ Y='\033[1;33m' # yellow
7
+ C='\033[0;36m' # cyan
8
+ D='\033[2;37m' # dim
9
+ W='\033[1;37m' # bold white
10
+ NC='\033[0m'
11
+
12
+ printf "\n"
13
+ printf "${D}> /geo-analyst${NC}\n"
14
+ sleep 0.8
15
+
16
+ printf "\n"
17
+ printf "${C}● Phase 0 Verifying Toolbelt MCP connection...${NC}\n"
18
+ sleep 0.6
19
+ printf "${G} ✓ Connected${NC} | namespace: demo-workspace\n"
20
+ sleep 0.3
21
+
22
+ printf "\n"
23
+ printf "${C}● Phase 1 Resolving namespace...${NC}\n"
24
+ sleep 0.4
25
+ printf "${G} ✓ Using${NC} demo-workspace (a3f2c1d9-4b2e-4a7c-9f1d-8e3b0c2d6a5f)\n"
26
+ sleep 0.2
27
+
28
+ printf "\n"
29
+ printf "${C}● Phase 2 Uploading Tampa Bay sensor data...${NC}\n"
30
+ sleep 0.4
31
+ printf " ${D}10 sensors | columns: id, name, lat, lon${NC}\n"
32
+ sleep 0.8
33
+ printf " ${Y}[ingest]${NC} running...\n"
34
+ sleep 1.2
35
+ printf " ${G}[ingest]${NC} completed ✓\n"
36
+ sleep 0.3
37
+ printf "${G} ✓ Sensor table:${NC} ${W}sensor_locations${NC} (10 rows)\n"
38
+ sleep 0.3
39
+
40
+ printf "\n"
41
+ printf "${C}● Phase 3 Running GPU geospatial queries...${NC}\n"
42
+ sleep 0.3
43
+
44
+ printf "\n"
45
+ printf " ${B}Query 1${NC} Pairwise Distance ${D}ST_DISTANCE()${NC}\n"
46
+ sleep 1.0
47
+ printf " ┌────────────┬────────────┬────────────┐\n"
48
+ printf " │ sensor_a │ sensor_b │ distance_m │\n"
49
+ printf " ├────────────┼────────────┼────────────┤\n"
50
+ printf " │ Sensor A │ Sensor B │ 2,847 │\n"
51
+ printf " │ Sensor A │ Sensor J │ 4,213 │\n"
52
+ printf " │ Sensor B │ Sensor J │ 5,091 │\n"
53
+ printf " └────────────┴────────────┴────────────┘\n"
54
+ sleep 0.5
55
+
56
+ printf "\n"
57
+ printf " ${B}Query 2${NC} Point-in-Polygon ${D}ST_CONTAINS()${NC}\n"
58
+ sleep 0.9
59
+ printf " Zone: Downtown Tampa bounding polygon\n"
60
+ printf " ${G} 2 sensors inside zone:${NC} Sensor A, Sensor B\n"
61
+ sleep 0.5
62
+
63
+ printf "\n"
64
+ printf " ${B}Query 3${NC} Track Line ${D}ST_MAKELINE()${NC}\n"
65
+ sleep 0.9
66
+ printf " LINESTRING(-82.4572 27.9506, -82.4398 27.9659, ...)\n"
67
+ printf " ${G} Track complete:${NC} 10 points\n"
68
+
69
+ printf "\n"
70
+ printf "${W}RESULT:${NC}\n"
71
+ printf " ${D}namespace_id:${NC} a3f2c1d9-4b2e-4a7c-9f1d-8e3b0c2d6a5f\n"
72
+ printf " ${D}sensor_table:${NC} sensor_locations (10 rows)\n"
73
+ printf " ${D}phases_run:${NC} [0, 1, 2, 3]\n"
74
+ printf "\n"
75
+ printf " ${D}distance_query:${NC}\n"
76
+ printf " ${D}closest_pair:${NC} Sensor A → Sensor B\n"
77
+ printf " ${D}min_distance:${NC} 2,847 m\n"
78
+ printf "\n"
79
+ printf " ${D}point_in_polygon:${NC}\n"
80
+ printf " ${D}in_zone_count:${NC} 2\n"
81
+ printf " ${D}in_zone_sensors:${NC} [Sensor A, Sensor B]\n"
82
+ printf "\n"
83
+ printf " ${D}track:${NC}\n"
84
+ printf " ${D}point_count:${NC} 10\n"
85
+ printf "\n"
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env bash
2
+ # Simulates the run-toolbelt skill running inside Claude Code
3
+
4
+ G='\033[0;32m' # green
5
+ B='\033[0;34m' # blue
6
+ Y='\033[1;33m' # yellow
7
+ C='\033[0;36m' # cyan
8
+ D='\033[2;37m' # dim
9
+ W='\033[1;37m' # bold white
10
+ NC='\033[0m'
11
+
12
+ printf "\n"
13
+ printf "${D}> /run-toolbelt document_url=https://docs.toolbelt.ai/intro.pdf question=\"What is Toolbelt?\"${NC}\n"
14
+ sleep 0.8
15
+
16
+ printf "\n"
17
+ printf "${C}● Phase 0 Verifying Toolbelt MCP connection...${NC}\n"
18
+ sleep 0.6
19
+ printf "${G} ✓ Connected${NC} | namespace: demo-workspace | 1 namespace found\n"
20
+ sleep 0.3
21
+
22
+ printf "\n"
23
+ printf "${C}● Phase 1 Resolving namespace...${NC}\n"
24
+ sleep 0.4
25
+ printf "${G} ✓ Using${NC} demo-workspace (a3f2c1d9-4b2e-4a7c-9f1d-8e3b0c2d6a5f)\n"
26
+ sleep 0.2
27
+
28
+ printf "\n"
29
+ printf "${C}● Phase 2 Inspecting current state...${NC}\n"
30
+ sleep 0.5
31
+ printf "${G} ✓ Context loaded${NC} | 0 tables | 0 vector collections\n"
32
+ sleep 0.2
33
+
34
+ printf "\n"
35
+ printf "${C}● Phase 3 Uploading document...${NC}\n"
36
+ sleep 0.4
37
+ printf " ${D}file:${NC} intro.pdf ${D}source:${NC} docs.toolbelt.ai\n"
38
+ sleep 0.8
39
+ printf " ${Y}[ingest]${NC} running...\n"
40
+ sleep 1.2
41
+ printf " ${G}[ingest]${NC} completed ✓\n"
42
+ sleep 0.4
43
+ printf " ${Y}[semantic]${NC} running...\n"
44
+ sleep 1.6
45
+ printf " ${G}[semantic]${NC} completed ✓\n"
46
+ sleep 0.3
47
+ printf "${G} ✓ Ingested${NC} → table: ${W}intro_pdf${NC}\n"
48
+ sleep 0.3
49
+
50
+ printf "\n"
51
+ printf "${C}● Phase 5 Answering question...${NC}\n"
52
+ sleep 0.4
53
+ printf " ${D}toolbelt_search question=\"What is Toolbelt?\"${NC}\n"
54
+ sleep 1.2
55
+ printf "${G} ✓ Done${NC}\n"
56
+
57
+ printf "\n"
58
+ printf "${W}RESULT:${NC}\n"
59
+ printf " ${D}namespace_id:${NC} a3f2c1d9-4b2e-4a7c-9f1d-8e3b0c2d6a5f\n"
60
+ printf " ${D}phases_run:${NC} [0, 1, 2, 3, 5]\n"
61
+ printf " ${D}document_table:${NC} intro_pdf\n"
62
+ printf " ${D}answer:${NC} Toolbelt is a GPU-accelerated data workspace\n"
63
+ printf " providing SQL, vector search, knowledge graphs,\n"
64
+ printf " geospatial queries, and Kafka streaming via MCP.\n"
65
+ printf " ${D}sources:${NC} [intro.pdf]\n"
66
+ printf "\n"
Binary file
Binary file
Binary file
package/bin/install.js ADDED
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * toolbelt-skills — install Toolbelt's Claude Code skills into ~/.claude/skills/toolbelt/
4
+ *
5
+ * Usage:
6
+ * npx @toolbeltai/skills install # copy skills (default)
7
+ * npx @toolbeltai/skills uninstall # remove them
8
+ * npx @toolbeltai/skills list # print what would be installed
9
+ * npx @toolbeltai/skills path # print where they would go
10
+ *
11
+ * The @toolbeltai/cli package wraps this for its own install flow; this
12
+ * CLI exists so the skills package stands on its own — anyone can install
13
+ * without needing the Toolbelt CLI or hitting any Toolbelt-hosted service.
14
+ */
15
+ import { cpSync, existsSync, mkdirSync, readdirSync, rmSync, statSync } from 'node:fs';
16
+ import { homedir } from 'node:os';
17
+ import { dirname, join } from 'node:path';
18
+ import { fileURLToPath } from 'node:url';
19
+
20
+ const PKG_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
21
+ const TARGET = join(homedir(), '.claude', 'skills', 'toolbelt');
22
+
23
+ /** Any top-level directory containing a SKILL.md is a skill. */
24
+ function listSkills() {
25
+ return readdirSync(PKG_ROOT, { withFileTypes: true })
26
+ .filter((e) => e.isDirectory() && !e.name.startsWith('.') && e.name !== 'node_modules' && e.name !== 'bin' && e.name !== 'assets')
27
+ .map((e) => e.name)
28
+ .filter((name) => existsSync(join(PKG_ROOT, name, 'SKILL.md')));
29
+ }
30
+
31
+ function cmdInstall() {
32
+ const skills = listSkills();
33
+ if (skills.length === 0) {
34
+ console.error(' ✗ No skills found in package. This is a packaging bug — please report.');
35
+ process.exit(1);
36
+ }
37
+
38
+ mkdirSync(TARGET, { recursive: true });
39
+ for (const name of skills) {
40
+ const src = join(PKG_ROOT, name);
41
+ const dst = join(TARGET, name);
42
+ rmSync(dst, { recursive: true, force: true });
43
+ cpSync(src, dst, { recursive: true });
44
+ console.log(` \u2713 ${name}`);
45
+ }
46
+ // Optional assets (icons, tapes) — copy if present so popups render right.
47
+ const assetsSrc = join(PKG_ROOT, 'assets');
48
+ if (existsSync(assetsSrc) && statSync(assetsSrc).isDirectory()) {
49
+ const assetsDst = join(TARGET, 'assets');
50
+ rmSync(assetsDst, { recursive: true, force: true });
51
+ cpSync(assetsSrc, assetsDst, { recursive: true });
52
+ }
53
+
54
+ console.log('');
55
+ console.log(` Installed ${skills.length} skills to ${TARGET}`);
56
+ console.log(' Restart Claude Code to pick them up.');
57
+ }
58
+
59
+ function cmdUninstall() {
60
+ if (!existsSync(TARGET)) {
61
+ console.log(' (nothing to remove)');
62
+ return;
63
+ }
64
+ rmSync(TARGET, { recursive: true, force: true });
65
+ console.log(` Removed ${TARGET}`);
66
+ }
67
+
68
+ function cmdList() {
69
+ for (const name of listSkills()) console.log(` ${name}`);
70
+ }
71
+
72
+ function cmdPath() {
73
+ console.log(TARGET);
74
+ }
75
+
76
+ function usage() {
77
+ console.log(`toolbelt-skills — install Toolbelt skills into ~/.claude/skills/toolbelt/
78
+
79
+ Usage:
80
+ npx @toolbeltai/skills install Install skills (default)
81
+ npx @toolbeltai/skills uninstall Remove skills
82
+ npx @toolbeltai/skills list List what would be installed
83
+ npx @toolbeltai/skills path Print target install path
84
+ `);
85
+ }
86
+
87
+ const cmd = process.argv[2] ?? 'install';
88
+ switch (cmd) {
89
+ case 'install':
90
+ cmdInstall();
91
+ break;
92
+ case 'uninstall':
93
+ cmdUninstall();
94
+ break;
95
+ case 'list':
96
+ cmdList();
97
+ break;
98
+ case 'path':
99
+ cmdPath();
100
+ break;
101
+ case '-h':
102
+ case '--help':
103
+ case 'help':
104
+ usage();
105
+ break;
106
+ default:
107
+ console.error(`Unknown command: ${cmd}`);
108
+ usage();
109
+ process.exit(2);
110
+ }
@@ -0,0 +1,283 @@
1
+ ---
2
+ name: data-blend
3
+ description: >
4
+ Join and correlate multiple datasets in a single Toolbelt namespace without
5
+ writing infrastructure code. Toolbelt is a multi-modal data platform combining
6
+ SQL analytics, vector search, and real-time streaming. Uploads two or more CSV
7
+ tables, then runs cross-table JOIN queries to surface relationships between
8
+ datasets. Use when an AI agent needs to combine data from different sources —
9
+ orders with customers, sensors with metadata, events with dimensions — and
10
+ answer questions that span multiple tables.
11
+ license: MIT
12
+ compatibility: >
13
+ Requires a Toolbelt account (provision free at https://toolbelt.ai) and an
14
+ MCP-compatible AI agent (Claude Code, Claude Desktop, or any client that
15
+ supports MCP server connections). MCP connection must be pre-established
16
+ before invocation.
17
+ metadata:
18
+ author: toolbeltai
19
+ version: "1.0"
20
+ openclaw:
21
+ emoji: "🔀"
22
+ homepage: "https://toolbelt.ai/docs/sql"
23
+ skillKey: "data-blend"
24
+ ---
25
+
26
+ Upload multiple tables and run cross-table JOIN queries using Toolbelt MCP tools.
27
+ Work through each phase in order without prompting for user input. On
28
+ unrecoverable error, emit a structured failure and halt.
29
+
30
+ ## When Not To Use
31
+
32
+ - For a single table — use `sql-analyst` instead.
33
+ - For unstructured text or documents — use `knowledge-graph` instead.
34
+ - For streaming/real-time data — use `streaming-analyst` instead.
35
+ - For spatial data with lat/lon — use `geo-analyst` instead.
36
+
37
+ ## Invocation Parameters
38
+
39
+ Extract these from the args string or conversation context before starting:
40
+
41
+ | Parameter | Required | Description |
42
+ |---|---|---|
43
+ | `namespace_id` | No | UUID of target namespace. Auto-select if omitted and only one exists; fail if ambiguous. |
44
+ | `table_a_content` | No | Raw CSV for the first table. Uses default `orders` sample if omitted. |
45
+ | `table_a_name` | No | Asset name for the first table. Defaults to `orders`. |
46
+ | `table_b_content` | No | Raw CSV for the second table. Uses default `customers` sample if omitted. |
47
+ | `table_b_name` | No | Asset name for the second table. Defaults to `customers`. |
48
+ | `join_query` | No | Custom SQL JOIN to execute in Phase 5. Uses default query if omitted. |
49
+ | `skip_upload` | No | Set to `true` to skip Phases 2–4 and query tables already in the namespace. |
50
+
51
+ ---
52
+
53
+ ## Default Sample Data
54
+
55
+ If no `table_a_content` is provided, use this orders dataset verbatim:
56
+
57
+ ```
58
+ order_id,customer_id,product,category,quantity,amount,order_date
59
+ 1001,C001,Widget Pro,Hardware,12,599.88,2024-01-05
60
+ 1002,C003,Gadget Basic,Software,5,149.95,2024-01-08
61
+ 1003,C002,Widget Pro,Hardware,8,399.92,2024-01-12
62
+ 1004,C004,Service Plan,Services,3,597.00,2024-01-15
63
+ 1005,C001,Gadget Basic,Software,20,599.80,2024-01-19
64
+ 1006,C005,Widget Pro,Hardware,6,299.94,2024-01-22
65
+ 1007,C003,Service Plan,Services,2,398.00,2024-02-03
66
+ 1008,C002,Gadget Plus,Software,15,1199.85,2024-02-07
67
+ 1009,C001,Widget Pro,Hardware,10,499.90,2024-02-11
68
+ 1010,C004,Gadget Basic,Software,8,239.92,2024-02-14
69
+ 1011,C005,Gadget Plus,Software,4,319.96,2024-02-18
70
+ 1012,C002,Service Plan,Services,1,199.00,2024-02-21
71
+ 1013,C001,Service Plan,Services,5,995.00,2024-03-02
72
+ 1014,C005,Gadget Plus,Software,9,719.91,2024-03-06
73
+ 1015,C003,Widget Pro,Hardware,7,349.93,2024-03-10
74
+ 1016,C002,Gadget Basic,Software,11,329.89,2024-03-14
75
+ 1017,C004,Gadget Plus,Software,6,479.94,2024-03-18
76
+ 1018,C005,Service Plan,Services,4,796.00,2024-03-22
77
+ 1019,C003,Widget Pro,Hardware,3,149.97,2024-03-25
78
+ 1020,C002,Widget Pro,Hardware,14,699.86,2024-03-28
79
+ ```
80
+
81
+ If no `table_b_content` is provided, use this customers dataset verbatim:
82
+
83
+ ```
84
+ customer_id,name,region,segment,account_manager
85
+ C001,Meridian Corp,Northeast,Enterprise,Alice Chen
86
+ C002,Delta Systems,Midwest,Enterprise,Carol Singh
87
+ C003,Apex Solutions,Southeast,Mid-Market,Bob Martinez
88
+ C004,Crest Industries,West,Mid-Market,David Park
89
+ C005,Solaris Group,West,SMB,Emma Lopez
90
+ ```
91
+
92
+ Default `join_query`:
93
+ ```sql
94
+ SELECT
95
+ c.segment,
96
+ c.region,
97
+ COUNT(o.order_id) AS order_count,
98
+ ROUND(SUM(o.amount), 2) AS total_amount,
99
+ ROUND(AVG(o.amount), 2) AS avg_order_value
100
+ FROM <orders_table> o
101
+ JOIN <customers_table> c ON o.customer_id = c.customer_id
102
+ GROUP BY c.segment, c.region
103
+ ORDER BY total_amount DESC
104
+ ```
105
+
106
+ ---
107
+
108
+ ## Phase 0: Verify Connection
109
+
110
+ Call `get_semantic_names` (no arguments) immediately.
111
+
112
+ - **If it succeeds:** proceed to Phase 1 using the returned namespaces.
113
+ - **If it fails:** emit structured failure and halt.
114
+
115
+ ```
116
+ FAILURE: Toolbelt MCP connection is not established.
117
+ The MCP server must be connected before invoking this skill.
118
+ See: https://toolbelt.ai/docs/mcp for setup instructions.
119
+ ```
120
+
121
+ ---
122
+
123
+ ## Phase 1: Resolve Namespace
124
+
125
+ Use the namespaces returned from Phase 0.
126
+
127
+ Resolution order:
128
+ 1. If `namespace_id` was provided as a parameter, use it directly.
129
+ 2. If only one namespace exists, use it.
130
+ 3. If multiple exist and no `namespace_id` was specified, emit structured failure and halt.
131
+
132
+ ```
133
+ FAILURE: Multiple namespaces found and none specified.
134
+ Available: [<list namespace display names and IDs>]
135
+ Re-invoke with namespace_id=<uuid>.
136
+ ```
137
+
138
+ Store the resolved `namespace_id` — pass it to every subsequent tool call.
139
+
140
+ ---
141
+
142
+ ## Phase 2: Inspect Existing Tables
143
+
144
+ Skip this phase if `skip_upload` is `true` — go directly to Phase 5.
145
+
146
+ Call `toolbelt_context` with `{ "namespace_id": "<namespace_id>" }`.
147
+
148
+ Check whether tables matching `table_a_name` and `table_b_name` already exist:
149
+ - If both exist: skip Phases 3–4 and proceed to Phase 5 using their existing table names.
150
+ - If one or both are missing: upload the missing ones in Phase 3.
151
+
152
+ Store all resolved table names for use in Phase 5.
153
+
154
+ ---
155
+
156
+ ## Phase 3: Upload Missing Tables
157
+
158
+ For each table that does not already exist, call `toolbelt_save`:
159
+
160
+ ```json
161
+ {
162
+ "asset_type": "document",
163
+ "namespace_id": "<namespace_id>",
164
+ "name": "<table_a_name or table_b_name>",
165
+ "file_name": "<name>.csv",
166
+ "content": "<csv_content>",
167
+ "content_encoding": "text",
168
+ "data_format": "csv"
169
+ }
170
+ ```
171
+
172
+ Record each returned `asset_id`.
173
+
174
+ ---
175
+
176
+ ## Phase 4: Poll for Ingestion
177
+
178
+ Call `toolbelt_jobs` with `{ "namespace_id": "<namespace_id>" }` every 10 seconds.
179
+
180
+ Wait for the `ingest` job for each uploaded asset to reach `completed`. Typical duration: 15–60 seconds per table. Maximum wait: 3 minutes.
181
+
182
+ If any job reaches `failed` or the timeout elapses, emit structured failure and halt:
183
+ ```
184
+ FAILURE: Table ingestion did not complete.
185
+ Asset: <table name>
186
+ Job status: <last observed status>
187
+ ```
188
+
189
+ After all jobs complete, call `toolbelt_context` to retrieve the final SQL table names for all assets. Store as `table_a` and `table_b`.
190
+
191
+ ---
192
+
193
+ ## Phase 5: Run Blend Queries
194
+
195
+ Substitute `<orders_table>` and `<customers_table>` (or equivalent names) in all queries with the resolved table names from Phase 4 (or Phase 2 if skip_upload).
196
+
197
+ ### Query 1 — Join summary (default or custom)
198
+
199
+ If `join_query` was provided, execute it directly. Otherwise execute the default query, substituting the resolved table names:
200
+
201
+ ```json
202
+ {
203
+ "namespace_id": "<namespace_id>",
204
+ "query": "<join_query with table names substituted>"
205
+ }
206
+ ```
207
+
208
+ Use `toolbelt_sql` for this call. Record:
209
+ - `join_rows`: number of rows returned
210
+ - `join_results`: up to 10 rows
211
+
212
+ ### Query 2 — Row counts
213
+
214
+ Confirm both tables are populated:
215
+
216
+ ```sql
217
+ SELECT '<table_a>' AS table_name, COUNT(*) AS row_count FROM <table_a>
218
+ UNION ALL
219
+ SELECT '<table_b>' AS table_name, COUNT(*) AS row_count FROM <table_b>
220
+ ```
221
+
222
+ Record `row_counts` for each table.
223
+
224
+ ### Query 3 — Unmatched rows check
225
+
226
+ Identify rows in table A with no match in table B (join integrity check):
227
+
228
+ ```sql
229
+ SELECT COUNT(*) AS unmatched_count
230
+ FROM <table_a> a
231
+ LEFT JOIN <table_b> b ON a.<join_key> = b.<join_key>
232
+ WHERE b.<join_key> IS NULL
233
+ ```
234
+
235
+ Infer `<join_key>` from the shared column names visible in the schema context. If no shared key can be inferred, skip this query and note it in the RESULT.
236
+
237
+ Record `unmatched_count`.
238
+
239
+ ---
240
+
241
+ ## Phase 6: Structured Output
242
+
243
+ After all phases complete, emit a single structured result:
244
+
245
+ ```
246
+ RESULT:
247
+ namespace_id: <uuid>
248
+ phases_run: [0, 1, 2, 3, 4, 5]
249
+
250
+ tables:
251
+ table_a: <sql table name>
252
+ table_b: <sql table name>
253
+ row_counts:
254
+ <table_a>: <count>
255
+ <table_b>: <count>
256
+ unmatched_rows: <count or "skipped — no shared key inferred">
257
+
258
+ blend_query:
259
+ sql: |
260
+ <query executed>
261
+ row_count: <join_rows>
262
+ results:
263
+ - <row 1>
264
+ - <row 2>
265
+ ... (up to 10 rows)
266
+ ```
267
+
268
+ If any Phase 5 query fails, include `query_error: "<error>"` under that query's
269
+ section and continue. Only halt on Phase 0–3 failures.
270
+
271
+ ---
272
+
273
+ ## Tool Reference
274
+
275
+ | Phase | Tool(s) |
276
+ |---|---|
277
+ | 0. Verify connection | `get_semantic_names` |
278
+ | 1. Resolve namespace | (from Phase 0 result) |
279
+ | 2. Inspect existing tables | `toolbelt_context` |
280
+ | 3. Upload missing tables | `toolbelt_save` |
281
+ | 4. Poll for ingestion | `toolbelt_jobs`, `toolbelt_context` |
282
+ | 5. Run blend queries | `toolbelt_sql`, `toolbelt_execute` |
283
+ | 6. Emit result | (structured output) |
@@ -0,0 +1,7 @@
1
+ # geo-analyst
2
+
3
+ ![geo-analyst demo](../assets/geo-analyst-demo.gif)
4
+
5
+ GPU-accelerated geospatial analytics agent powered by Toolbelt MCP. Uploads lat/lon sensor data, runs geospatial SQL queries (distance, point-in-polygon, track creation), and emits structured results.
6
+
7
+ Invoke via `/geo-analyst` in Claude Code, or via the `Skill` tool in any MCP-capable agent.