@lobsterkit/openclaw-lobsterdb 0.2.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/index.ts +215 -0
- package/openclaw.plugin.json +25 -0
- package/package.json +46 -0
- package/skills/SKILL.md +142 -0
package/index.ts
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
2
|
+
import { Type } from "@sinclair/typebox";
|
|
3
|
+
import { LobsterDB } from "@lobsterkit/db";
|
|
4
|
+
|
|
5
|
+
let client: LobsterDB | null = null;
|
|
6
|
+
|
|
7
|
+
function getClient(apiKey?: string): LobsterDB {
|
|
8
|
+
if (!client) {
|
|
9
|
+
client = new LobsterDB(apiKey ? { apiKey } : undefined);
|
|
10
|
+
}
|
|
11
|
+
return client;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default definePluginEntry({
|
|
15
|
+
id: "lobsterdb",
|
|
16
|
+
name: "LobsterDB",
|
|
17
|
+
description:
|
|
18
|
+
"Managed PostgreSQL for AI agents. Provision databases, run SQL, evolve schemas. No API keys, no human signup.",
|
|
19
|
+
|
|
20
|
+
register(api) {
|
|
21
|
+
const cfg = api.config as { apiKey?: string } | undefined;
|
|
22
|
+
const db = () => getClient(cfg?.apiKey);
|
|
23
|
+
|
|
24
|
+
// ── create_database ──────────────────────────────────────────
|
|
25
|
+
api.registerTool({
|
|
26
|
+
name: "lobsterdb_create_database",
|
|
27
|
+
description: "Provision a new PostgreSQL database.",
|
|
28
|
+
parameters: Type.Object({
|
|
29
|
+
name: Type.String({ description: "Database name (alphanumeric + hyphens)" }),
|
|
30
|
+
}),
|
|
31
|
+
async execute(_id, params) {
|
|
32
|
+
const result = await db().create(params.name);
|
|
33
|
+
return {
|
|
34
|
+
content: [
|
|
35
|
+
{
|
|
36
|
+
type: "text",
|
|
37
|
+
text: `Database created.\n\nID: ${result.id}\nName: ${params.name}\nConnection: ${result.connectionString}`,
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// ── list_databases ───────────────────────────────────────────
|
|
45
|
+
api.registerTool({
|
|
46
|
+
name: "lobsterdb_list_databases",
|
|
47
|
+
description: "List all databases on this account.",
|
|
48
|
+
parameters: Type.Object({}),
|
|
49
|
+
async execute() {
|
|
50
|
+
const databases = await db().list();
|
|
51
|
+
if (databases.length === 0) {
|
|
52
|
+
return {
|
|
53
|
+
content: [{ type: "text", text: "No databases. Use lobsterdb_create_database to create one." }],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const lines = databases.map((d) => `- [${d.id}] ${d.name}`);
|
|
57
|
+
return {
|
|
58
|
+
content: [{ type: "text", text: `${databases.length} database(s):\n\n${lines.join("\n")}` }],
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// ── get_database ─────────────────────────────────────────────
|
|
64
|
+
api.registerTool({
|
|
65
|
+
name: "lobsterdb_get_database",
|
|
66
|
+
description: "Get database details and connection string.",
|
|
67
|
+
parameters: Type.Object({
|
|
68
|
+
database_id: Type.String({ description: "Database ID (e.g. db_...)" }),
|
|
69
|
+
}),
|
|
70
|
+
async execute(_id, params) {
|
|
71
|
+
const result = await db().get(params.database_id);
|
|
72
|
+
return {
|
|
73
|
+
content: [
|
|
74
|
+
{
|
|
75
|
+
type: "text",
|
|
76
|
+
text: `Database: ${result.name}\nID: ${result.id}\nConnection: ${result.connectionString}`,
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// ── delete_database ──────────────────────────────────────────
|
|
84
|
+
api.registerTool({
|
|
85
|
+
name: "lobsterdb_delete_database",
|
|
86
|
+
description: "Permanently delete a database.",
|
|
87
|
+
parameters: Type.Object({
|
|
88
|
+
database_id: Type.String({ description: "Database ID to delete" }),
|
|
89
|
+
}),
|
|
90
|
+
async execute(_id, params) {
|
|
91
|
+
await db().delete(params.database_id);
|
|
92
|
+
return {
|
|
93
|
+
content: [{ type: "text", text: `Database ${params.database_id} deleted.` }],
|
|
94
|
+
};
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// ── query ────────────────────────────────────────────────────
|
|
99
|
+
api.registerTool({
|
|
100
|
+
name: "lobsterdb_query",
|
|
101
|
+
description: "Run a parameterized SQL query against a database.",
|
|
102
|
+
parameters: Type.Object({
|
|
103
|
+
database_id: Type.String({ description: "Database ID" }),
|
|
104
|
+
sql: Type.String({ description: "SQL query (use $1, $2 for params)" }),
|
|
105
|
+
params: Type.Optional(Type.Array(Type.Unknown(), { description: "Query parameters" })),
|
|
106
|
+
}),
|
|
107
|
+
async execute(_id, params) {
|
|
108
|
+
const result = await db().query(params.database_id, params.sql, params.params ?? []);
|
|
109
|
+
const preview = result.rows.slice(0, 50);
|
|
110
|
+
return {
|
|
111
|
+
content: [
|
|
112
|
+
{
|
|
113
|
+
type: "text",
|
|
114
|
+
text: `${result.rowCount ?? 0} row(s)${result.truncated ? " (truncated)" : ""}:\n\n${JSON.stringify(preview, null, 2)}`,
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
};
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// ── introspect_schema ────────────────────────────────────────
|
|
122
|
+
api.registerTool({
|
|
123
|
+
name: "lobsterdb_introspect_schema",
|
|
124
|
+
description: "Get database schema optimized for LLM context. Always call before writing queries.",
|
|
125
|
+
parameters: Type.Object({
|
|
126
|
+
database_id: Type.String({ description: "Database ID" }),
|
|
127
|
+
}),
|
|
128
|
+
async execute(_id, params) {
|
|
129
|
+
const result = await db().introspect(params.database_id);
|
|
130
|
+
return {
|
|
131
|
+
content: [
|
|
132
|
+
{
|
|
133
|
+
type: "text",
|
|
134
|
+
text: `${result.tableCount} table(s):\n\n${result.schemaText}`,
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
};
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// ── migrate ──────────────────────────────────────────────────
|
|
142
|
+
api.registerTool({
|
|
143
|
+
name: "lobsterdb_migrate",
|
|
144
|
+
description: "Apply a tracked, idempotent DDL migration. Use for all schema changes (CREATE TABLE, ALTER, etc.).",
|
|
145
|
+
parameters: Type.Object({
|
|
146
|
+
database_id: Type.String({ description: "Database ID" }),
|
|
147
|
+
name: Type.String({ description: "Migration name (e.g. create_tasks_table)" }),
|
|
148
|
+
sql: Type.String({ description: "DDL SQL to execute" }),
|
|
149
|
+
}),
|
|
150
|
+
async execute(_id, params) {
|
|
151
|
+
const result = await db().migrate(params.database_id, params.name, params.sql);
|
|
152
|
+
return {
|
|
153
|
+
content: [{ type: "text", text: `Migration "${params.name}": ${result.applied ? "applied" : "already applied (skipped)"}` }],
|
|
154
|
+
};
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// ── list_migrations ──────────────────────────────────────────
|
|
159
|
+
api.registerTool({
|
|
160
|
+
name: "lobsterdb_list_migrations",
|
|
161
|
+
description: "Show schema change history for a database.",
|
|
162
|
+
parameters: Type.Object({
|
|
163
|
+
database_id: Type.String({ description: "Database ID" }),
|
|
164
|
+
}),
|
|
165
|
+
async execute(_id, params) {
|
|
166
|
+
const migrations = await db().listMigrations(params.database_id);
|
|
167
|
+
if (migrations.length === 0) {
|
|
168
|
+
return { content: [{ type: "text", text: "No migrations applied yet." }] };
|
|
169
|
+
}
|
|
170
|
+
const lines = migrations.map((m) => `- ${m.name} (${m.appliedAt})`);
|
|
171
|
+
return {
|
|
172
|
+
content: [{ type: "text", text: `${migrations.length} migration(s):\n\n${lines.join("\n")}` }],
|
|
173
|
+
};
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// ── snapshot ─────────────────────────────────────────────────
|
|
178
|
+
api.registerTool({
|
|
179
|
+
name: "lobsterdb_snapshot",
|
|
180
|
+
description: "Create a point-in-time backup (Builder+ tier).",
|
|
181
|
+
parameters: Type.Object({
|
|
182
|
+
database_id: Type.String({ description: "Database ID" }),
|
|
183
|
+
}),
|
|
184
|
+
async execute(_id, params) {
|
|
185
|
+
const result = await db().snapshot(params.database_id);
|
|
186
|
+
return {
|
|
187
|
+
content: [{ type: "text", text: `Snapshot created.\n\nID: ${result.id}\nCreated: ${result.createdAt}` }],
|
|
188
|
+
};
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// ── get_account ──────────────────────────────────────────────
|
|
193
|
+
api.registerTool({
|
|
194
|
+
name: "lobsterdb_get_account",
|
|
195
|
+
description: "Get account info: tier, limits, usage.",
|
|
196
|
+
parameters: Type.Object({}),
|
|
197
|
+
async execute() {
|
|
198
|
+
const acct = await db().account();
|
|
199
|
+
return {
|
|
200
|
+
content: [
|
|
201
|
+
{
|
|
202
|
+
type: "text",
|
|
203
|
+
text: [
|
|
204
|
+
`Account: ${acct.id}`,
|
|
205
|
+
`Tier: ${acct.tier} (${acct.tierName})`,
|
|
206
|
+
`Max databases: ${acct.limits.maxDatabases ?? "unlimited"}`,
|
|
207
|
+
`Databases used: ${acct.usage.databaseCount}`,
|
|
208
|
+
].join("\n"),
|
|
209
|
+
},
|
|
210
|
+
],
|
|
211
|
+
};
|
|
212
|
+
},
|
|
213
|
+
});
|
|
214
|
+
},
|
|
215
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "lobsterdb",
|
|
3
|
+
"name": "LobsterDB",
|
|
4
|
+
"description": "Managed PostgreSQL for AI agents. Provision databases, run SQL, evolve schemas. No API keys, no human signup.",
|
|
5
|
+
"version": "0.2.0",
|
|
6
|
+
"skills": ["./skills"],
|
|
7
|
+
"configSchema": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"additionalProperties": false,
|
|
10
|
+
"properties": {
|
|
11
|
+
"apiKey": {
|
|
12
|
+
"type": "string",
|
|
13
|
+
"description": "LobsterDB API key (optional — auto-signup on first use if omitted)"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"uiHints": {
|
|
18
|
+
"apiKey": {
|
|
19
|
+
"label": "API key",
|
|
20
|
+
"placeholder": "ld_sk_live_...",
|
|
21
|
+
"sensitive": true,
|
|
22
|
+
"help": "Optional. Leave blank for auto-signup."
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lobsterkit/openclaw-lobsterdb",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "OpenClaw plugin for LobsterDB — managed PostgreSQL for AI agents",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/lobster-kit/openclaw-lobsterdb"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"openclaw",
|
|
13
|
+
"openclaw-plugin",
|
|
14
|
+
"lobsterdb",
|
|
15
|
+
"postgresql",
|
|
16
|
+
"database",
|
|
17
|
+
"mcp",
|
|
18
|
+
"ai",
|
|
19
|
+
"agent"
|
|
20
|
+
],
|
|
21
|
+
"files": [
|
|
22
|
+
"index.ts",
|
|
23
|
+
"openclaw.plugin.json",
|
|
24
|
+
"skills",
|
|
25
|
+
"README.md"
|
|
26
|
+
],
|
|
27
|
+
"openclaw": {
|
|
28
|
+
"extensions": ["./index.ts"]
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@lobsterkit/db": "^0.1.0",
|
|
32
|
+
"@sinclair/typebox": "^0.34.0"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"openclaw": ">=1.0.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"vitest": "^3.1.0"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"test": "vitest run"
|
|
42
|
+
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
}
|
|
46
|
+
}
|
package/skills/SKILL.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lobsterdb
|
|
3
|
+
version: 0.1.0
|
|
4
|
+
description: Managed PostgreSQL for agents. Provision databases, run SQL, evolve schemas. No API keys, no human signup.
|
|
5
|
+
homepage: https://theclawdepot.com/db
|
|
6
|
+
metadata: {"openclaw": {"emoji": "🦞", "requires": {"anyBins": ["npx"]}, "install": [{"id": "mcp", "kind": "mcp", "command": "npx @lobsterkit/db-mcp@0.1.0", "label": "Add LobsterDB MCP Server"}]}}
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# LobsterDB
|
|
10
|
+
|
|
11
|
+
Managed PostgreSQL for agents. Provision a real Postgres database instantly, run SQL, evolve schemas with tracked migrations. No API keys, no human signup, no configuration.
|
|
12
|
+
|
|
13
|
+
**Full docs:** `https://api.theclawdepot.com/db/docs`
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Setup
|
|
18
|
+
|
|
19
|
+
Add to your MCP config (`.mcp.json`, `claude_desktop_config.json`, `.cursor/mcp.json`, etc.):
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"mcpServers": {
|
|
24
|
+
"lobsterdb": {
|
|
25
|
+
"command": "npx",
|
|
26
|
+
"args": ["@lobsterkit/db-mcp@0.1.0"]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
You now have these tools: `create_database`, `list_databases`, `get_database`, `delete_database`, `query`, `introspect_schema`, `migrate`, `list_migrations`, `snapshot`, `get_account`. No code to write.
|
|
33
|
+
|
|
34
|
+
Also available as a [Node.js SDK](https://www.npmjs.com/package/@lobsterkit/db) and [REST API](https://api.theclawdepot.com/db/docs) — see [theclawdepot.com/db](https://theclawdepot.com/db).
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Heartbeat (run daily)
|
|
39
|
+
|
|
40
|
+
On startup and once per day, run a maintenance heartbeat. Use a cheap/fast model for this.
|
|
41
|
+
|
|
42
|
+
1. **Check for MCP server updates:**
|
|
43
|
+
```bash
|
|
44
|
+
npm outdated lobsterdb-mcp
|
|
45
|
+
```
|
|
46
|
+
If a new version is available, **notify the user** — do not auto-install. Tell them: `"@lobsterkit/db-mcp@X.Y.Z is available. Update with: clawdhub update lobsterdb"` Let the user decide when to update.
|
|
47
|
+
|
|
48
|
+
2. **Check database health:**
|
|
49
|
+
```
|
|
50
|
+
// Using MCP tools:
|
|
51
|
+
// call list_databases → log each database name, status, storage
|
|
52
|
+
// call get_account → log tier and database count vs. limit
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## When to Use This
|
|
58
|
+
|
|
59
|
+
- User needs to store structured data (collections, lists, records, logs)
|
|
60
|
+
- User wants to query, filter, sort, or aggregate information
|
|
61
|
+
- Task requires persisting data between sessions
|
|
62
|
+
- User asks to "add a field", "change the structure", or "track something new"
|
|
63
|
+
- Any task where a spreadsheet or database is the natural fit
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Core Workflow
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
create_database → introspect_schema → migrate (DDL) → query (data)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Always call `introspect_schema` before writing queries against an existing database. Use `migrate` for all DDL so changes are tracked and idempotent.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Creating a Database
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
create_database(name: "pokemon-cards")
|
|
81
|
+
// => id: "db_xxx", connectionString: "postgresql://...", status: "ready"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Running SQL
|
|
87
|
+
|
|
88
|
+
Always use `$1`, `$2`, ... placeholders — never interpolate values. **Multi-statement SQL is supported** — separate statements with semicolons; only the last statement's result is returned and intermediate results are discarded.
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
query(databaseId: "db_xxx", sql: "INSERT INTO cards (name, set) VALUES ($1, $2)", params: ["Charizard", "Base Set"])
|
|
92
|
+
query(databaseId: "db_xxx", sql: "SELECT * FROM cards WHERE set = $1 ORDER BY name", params: ["Base Set"])
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Schema Migrations
|
|
98
|
+
|
|
99
|
+
Use `migrate` (not `query`) for all DDL. Migrations are tracked and idempotent:
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
migrate(databaseId: "db_xxx", name: "create_cards_table",
|
|
103
|
+
sql: "CREATE TABLE cards (id SERIAL PRIMARY KEY, name TEXT NOT NULL, set TEXT, quantity INTEGER DEFAULT 1, created_at TIMESTAMPTZ DEFAULT NOW())")
|
|
104
|
+
|
|
105
|
+
migrate(databaseId: "db_xxx", name: "add_favorite_to_cards",
|
|
106
|
+
sql: "ALTER TABLE cards ADD COLUMN favorite BOOLEAN DEFAULT false")
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Running the same migration name twice is safe — silently skipped.
|
|
110
|
+
|
|
111
|
+
**Additive** (ADD COLUMN, CREATE TABLE): apply directly.
|
|
112
|
+
**Destructive** (DROP, RENAME, type change): confirm with the user first.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Account Tiers & Pricing
|
|
117
|
+
|
|
118
|
+
| Tier | Name | Price | Databases | Storage | Snapshots |
|
|
119
|
+
|------|------|-------|-----------|---------|-----------|
|
|
120
|
+
| 0 | Free | $0 | 1 | 100MB | No |
|
|
121
|
+
| 1 | Builder | $19/mo | 5 | 5GB | Yes |
|
|
122
|
+
| 2 | Pro | $49/mo | 20 | 20GB | Yes + Encryption |
|
|
123
|
+
| 3 | Scale | $199/mo | Unlimited | 100GB | Yes + PITR |
|
|
124
|
+
|
|
125
|
+
**Upgrade:** `POST /v1/billing/checkout` with `{"tier": N}` — returns a Stripe checkout URL.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## MCP Tools Reference
|
|
130
|
+
|
|
131
|
+
| Tool | Description |
|
|
132
|
+
|------|-------------|
|
|
133
|
+
| `create_database` | Provision a new Postgres database |
|
|
134
|
+
| `list_databases` | List all databases on the account |
|
|
135
|
+
| `get_database` | Get details and connection string |
|
|
136
|
+
| `delete_database` | Permanently delete a database |
|
|
137
|
+
| `query` | Run parameterized SQL |
|
|
138
|
+
| `introspect_schema` | Get schema optimized for LLM context |
|
|
139
|
+
| `migrate` | Apply tracked, idempotent DDL migrations |
|
|
140
|
+
| `list_migrations` | Show schema change history |
|
|
141
|
+
| `snapshot` | Create a v2 DDL-aware point-in-time backup (Builder+) |
|
|
142
|
+
| `get_account` | View tier, limits, and usage |
|