@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 +21 -0
- package/README.md +64 -0
- package/assets/demos/fake-curl.sh +3 -0
- package/assets/demos/geo-analyst-demo.sh +85 -0
- package/assets/demos/run-toolbelt-demo.sh +66 -0
- package/assets/geo-analyst-demo.gif +0 -0
- package/assets/run-toolbelt-demo.gif +0 -0
- package/assets/signup-demo.gif +0 -0
- package/bin/install.js +110 -0
- package/data-blend/SKILL.md +283 -0
- package/geo-analyst/README.md +7 -0
- package/geo-analyst/SKILL.md +241 -0
- package/knowledge-graph/SKILL.md +354 -0
- package/multi-agent-workspace/SKILL.md +217 -0
- package/package.json +51 -0
- package/run-toolbelt/README.md +122 -0
- package/run-toolbelt/SKILL.md +238 -0
- package/sql-analyst/SKILL.md +232 -0
- package/streaming-analyst/SKILL.md +351 -0
- package/vector-search/SKILL.md +259 -0
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,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
|
+

|
|
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.
|