hyperdb-mcp 0.7.2 → 1.0.0-rc.1
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/README.md +154 -48
- package/bin.js +105 -19
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# hyperdb-mcp
|
|
2
2
|
|
|
3
|
-
> **Note:** This crate is AI-assisted but human-directed — much of the code was written by AI coding assistants under close review, with the design and engineering trade-offs decided by an experienced developer.
|
|
3
|
+
> **Note:** This crate is AI-assisted but human-directed — much of the code was written by AI coding assistants under close review, with the design and engineering trade-offs decided by an experienced developer. As of 1.0.0 the public API is stable and follows [semantic versioning](https://semver.org/), so breaking changes require a major release.
|
|
4
4
|
|
|
5
5
|
An MCP (Model Context Protocol) server that turns the Hyper columnar database into an instant SQL analytics engine. Data flows in from other MCP plugins or files, lands in Hyper automatically, and becomes queryable with SQL — no setup, no schema files, no database management.
|
|
6
6
|
|
|
@@ -17,13 +17,14 @@ LLMs are powerful at reasoning but cannot natively crunch millions of rows. This
|
|
|
17
17
|
Unlike flat-text memory systems that store blobs and retrieve by similarity search, HyperDB gives LLMs **structured, queryable long-term memory**. The persistent database survives across sessions — anything the LLM stores there can be JOINed, filtered, aggregated, and reasoned over with full SQL in any future conversation.
|
|
18
18
|
|
|
19
19
|
This means an LLM can:
|
|
20
|
+
|
|
20
21
|
- **Accumulate knowledge over time** — store reference tables, project decisions, user preferences, learned facts
|
|
21
22
|
- **Cross-reference across sessions** — JOIN today's analysis against historical data from last week
|
|
22
23
|
- **Answer complex recall questions** — "Which projects had budget overruns in Q1?" is a SQL query, not a fuzzy text search
|
|
23
24
|
- **Build on prior work** — load yesterday's cleaned dataset and extend it without re-processing from scratch
|
|
24
25
|
- **Maintain structured context** — store relationship graphs, timelines, or decision logs as proper tables with typed columns
|
|
25
26
|
|
|
26
|
-
The
|
|
27
|
+
The local database is ephemeral scratch space (think: a whiteboard). The persistent database is long-term memory (think: a filing cabinet you can query). Multiple AI clients sharing the same daemon see the same persistent data — so Claude Code, Cursor, and VS Code Copilot can all read from and contribute to the same knowledge base.
|
|
27
28
|
|
|
28
29
|
**Table or key-value store?** For a handful of small facts, notes, or flags, prefer the built-in key-value store (`kv_set` with `persist: true`) over `CREATE TABLE` + `load_data` — it needs no schema and no DDL. Reach for a real table when you need typed columns, JOINs, or aggregation. See [Working with both databases](#working-with-both-databases) for the `persist` / `database` mechanics that apply to both paths.
|
|
29
30
|
|
|
@@ -48,9 +49,9 @@ The ephemeral database is scratch space (think: a whiteboard). The persistent da
|
|
|
48
49
|
- **Smart schema inference** — exact (Arrow/Parquet), structural (JSON), heuristic (CSV) with full-file numeric widening
|
|
49
50
|
- **Pre-ingest file inspection** — `inspect_file` dry-runs the same inference without touching Hyper so LLMs can build safe schema overrides in one shot
|
|
50
51
|
- **Partial schema overrides** — supply just the columns you want to correct (e.g. `{"population":"BIGINT"}`) — the rest keep their inferred type
|
|
51
|
-
- **Rich resource surface** —
|
|
52
|
+
- **Rich resource surface** — database overview, per-table JSON and CSV samples, and one JSON + one CSV resource per table so LLMs can orient themselves via `resources/list` without any tool calls
|
|
52
53
|
- **Saved queries** — register named read-only SQL with `save_query`; each query becomes `hyper://queries/{name}/definition` (metadata) + `hyper://queries/{name}/result` (live re-run). Persisted in the persistent attachment, session-only when `--ephemeral-only`
|
|
53
|
-
- **Key-value scratchpad** — lightweight `kv_set` / `kv_get` / `kv_list` / `kv_delete` / `kv_pop` / `kv_size` / `kv_clear` / `kv_list_stores` store for small notes and state without a `CREATE TABLE`. Ephemeral by default (lost on restart); pass `persist: true` (or `database: "persistent"`) to make a store durable across sessions
|
|
54
|
+
- **Key-value scratchpad** — lightweight `kv_set` / `kv_set_many` / `kv_get` / `kv_list` / `kv_delete` / `kv_pop` / `kv_size` / `kv_clear` / `kv_list_stores` store for small notes and state without a `CREATE TABLE`. Ephemeral by default (lost on restart); pass `persist: true` (or `database: "persistent"`) to make a store durable across sessions
|
|
54
55
|
- **Live resource-update notifications** — MCP clients can `resources/subscribe` to any `hyper://...` URI; the server fires `notifications/resources/updated` after every ingest, DDL, watcher event, or saved-query mutation
|
|
55
56
|
|
|
56
57
|
---
|
|
@@ -76,6 +77,7 @@ The npm package bundles both the `hyperdb-mcp` binary and the `hyperd` database
|
|
|
76
77
|
`nvm` (Node Version Manager) makes it easy to install and switch between Node.js versions.
|
|
77
78
|
|
|
78
79
|
**macOS / Linux** ([nvm-sh/nvm](https://github.com/nvm-sh/nvm)):
|
|
80
|
+
|
|
79
81
|
```bash
|
|
80
82
|
# install nvm if you don't have it
|
|
81
83
|
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
|
|
@@ -87,6 +89,7 @@ node --version # should report v22.x.x or newer
|
|
|
87
89
|
```
|
|
88
90
|
|
|
89
91
|
**Windows** ([coreybutler/nvm-windows](https://github.com/coreybutler/nvm-windows)): download the installer, then in a new shell:
|
|
92
|
+
|
|
90
93
|
```powershell
|
|
91
94
|
nvm install lts
|
|
92
95
|
nvm use lts
|
|
@@ -118,12 +121,16 @@ export HYPERD_PATH="$PWD/.hyperd/current" # or pass via your MCP config
|
|
|
118
121
|
`hyperdb-bootstrap` also has a library API if you'd rather wire the
|
|
119
122
|
download into your own build script — see its
|
|
120
123
|
[README](../hyperdb-bootstrap/README.md). If you already have `hyperd`
|
|
121
|
-
elsewhere (Tableau Hyper API for C++/Python/Java ships one),
|
|
122
|
-
`HYPERD_PATH`
|
|
124
|
+
elsewhere (Tableau Hyper API for C++/Python/Java ships one), set
|
|
125
|
+
`HYPERD_PATH` to either the executable or its containing directory.
|
|
126
|
+
When that variable is absent or non-UTF-8, the runtime walks upward from its
|
|
127
|
+
current directory for `.hyperd/current/hyperd`; it does not perform a general
|
|
128
|
+
`PATH` lookup.
|
|
123
129
|
|
|
124
130
|
### MCP Client Configuration
|
|
125
131
|
|
|
126
132
|
Each AI tool reads MCP server config from a different file but uses the same JSON shape. The base config block using npx (recommended):
|
|
133
|
+
|
|
127
134
|
```json
|
|
128
135
|
{
|
|
129
136
|
"mcpServers": {
|
|
@@ -137,6 +144,7 @@ Each AI tool reads MCP server config from a different file but uses the same JSO
|
|
|
137
144
|
```
|
|
138
145
|
|
|
139
146
|
Or if you built from source:
|
|
147
|
+
|
|
140
148
|
```json
|
|
141
149
|
{
|
|
142
150
|
"mcpServers": {
|
|
@@ -152,17 +160,24 @@ Or if you built from source:
|
|
|
152
160
|
```
|
|
153
161
|
|
|
154
162
|
By default, persistent storage lives at the platform data dir (`~/Library/Application Support/hyperdb/workspace.hyper` on macOS, `~/.local/share/hyperdb/workspace.hyper` on Linux, `%APPDATA%\hyperdb\workspace.hyper` on Windows). To use a custom path:
|
|
163
|
+
|
|
155
164
|
```json
|
|
156
165
|
"args": ["--persistent-db", "/path/to/my-project.hyper"]
|
|
157
166
|
```
|
|
158
167
|
|
|
159
|
-
Multiple MCP clients can point at the **same** persistent file simultaneously
|
|
168
|
+
Multiple MCP clients can point at the **same** persistent file simultaneously
|
|
169
|
+
when they reuse the shared `hyperd` daemon; Hyper's MVCC transaction isolation
|
|
170
|
+
coordinates their connections. A separate private `hyperd`, Tableau, or another
|
|
171
|
+
process trying to attach the same file can instead receive contextual
|
|
172
|
+
`RESOURCE_BUSY`. See [Operating Modes](#operating-modes) and
|
|
173
|
+
[Error Handling](#error-handling).
|
|
160
174
|
|
|
161
175
|
#### Claude Code / AI Suite
|
|
162
176
|
|
|
163
177
|
Create or edit `~/.claude/.mcp.json` (global) or `.mcp.json` in the project root (project-scoped). Use the base config block above.
|
|
164
178
|
|
|
165
179
|
After adding the config:
|
|
180
|
+
|
|
166
181
|
1. Start a new Claude Code session. You'll be prompted to approve the server on first use.
|
|
167
182
|
2. **Auto-approve tools (optional):** Add `"mcp__HyperDB__*"` to the `permissions.allow` array in `~/.claude/settings.json`.
|
|
168
183
|
|
|
@@ -182,7 +197,7 @@ Any tool that supports the MCP stdio transport can use this server. Point it at
|
|
|
182
197
|
|
|
183
198
|
## Operating Modes
|
|
184
199
|
|
|
185
|
-
Each session has **two databases**:
|
|
200
|
+
Each session has **two databases**: the ephemeral **local** primary (scratch space — always created fresh per session, deleted on exit) and a **persistent** database (queryable long-term memory — stored at the platform-default location or a path you supply, survives indefinitely). Unqualified SQL targets local; the durable database is reachable as the `"persistent"` alias. Additional `.hyper` files are **attached databases** under user-chosen aliases.
|
|
186
201
|
|
|
187
202
|
### Hyper engine
|
|
188
203
|
|
|
@@ -197,15 +212,15 @@ The shared daemon is the bigger win for users running multiple AI clients (Claud
|
|
|
197
212
|
|
|
198
213
|
| Mode | Flag | Behavior |
|
|
199
214
|
|---|---|---|
|
|
200
|
-
| **Default** | *(none)* | Ephemeral
|
|
215
|
+
| **Default** | *(none)* | Ephemeral local database in `$TMPDIR/hyperdb-mcp-<pid>-<n>/scratch.hyper` + persistent attachment at the platform data dir (e.g. `~/Library/Application Support/hyperdb/workspace.hyper` on macOS). |
|
|
201
216
|
| **Custom persistent path** | `--persistent-db <PATH>` | Same as default but the persistent file lives at `<PATH>`. The deprecated `--workspace <PATH>` is accepted as an alias with a stderr warning. |
|
|
202
|
-
| **Ephemeral-only** | `--ephemeral-only` | No persistent attachment; the session has only the
|
|
217
|
+
| **Ephemeral-only** | `--ephemeral-only` | No persistent attachment; the session has only the local database plus any user-attached databases via `attach_database`. Saved queries fall back to in-memory storage and disappear when the session ends. |
|
|
203
218
|
|
|
204
219
|
`HYPERDB_PERSISTENT_DB` overrides the default persistent path the same way `--persistent-db` does.
|
|
205
220
|
|
|
206
221
|
### Working with both databases
|
|
207
222
|
|
|
208
|
-
Tool calls default to the
|
|
223
|
+
Tool calls default to the local database — that's the LLM's ephemeral scratch space for exploratory work that doesn't need to outlive the session. To store data in long-term memory (the persistent database), there are two ways to reach it:
|
|
209
224
|
|
|
210
225
|
**1. Per-tool `database` parameter** (preferred for ergonomic LLM workflows):
|
|
211
226
|
|
|
@@ -222,7 +237,12 @@ describe({ database: "persistent" })
|
|
|
222
237
|
sample({ table: "customers", database: "persistent" })
|
|
223
238
|
```
|
|
224
239
|
|
|
225
|
-
The `database` parameter is available on `query`, `execute`, `load_data`, `load_file`, `load_files`, `watch_directory`, `describe`, `sample`, `chart`, `export`, and `set_table_metadata`. The shorthand `persist: true` (sugar for `database: "persistent"`) is available on `load_data`, `load_file`, `load_files`, and `watch_directory`.
|
|
240
|
+
The `database` parameter is available on `query`, `execute`, `load_data`, `load_file`, `load_files`, `watch_directory`, `describe`, `sample`, `chart`, `export`, and `set_table_metadata`. The shorthand `persist: true` (sugar for `database: "persistent"`) is available on `load_data`, `load_file`, `load_files`, and `watch_directory`. Read tools generally accept a read-only user attachment; write tools require a writable one. The exception is the KV family: every `kv_*` call to a user attachment requires it to be writable because the backing table may need initialization.
|
|
241
|
+
|
|
242
|
+
Every successful database-routed response includes the canonical
|
|
243
|
+
`resolved_database`: `"local"`, `"persistent"`, or the lowercase attached
|
|
244
|
+
alias after precedence is applied. An explicit `database` wins over
|
|
245
|
+
`persist: true`; `copy_query` additionally retains `target_database`.
|
|
226
246
|
|
|
227
247
|
(`query_data` and `query_file` are one-shot tools that materialize the inline data into their own temp table and query it — they do not accept a `database` parameter because the data isn't in a persisted database to begin with.)
|
|
228
248
|
|
|
@@ -237,7 +257,12 @@ CREATE TABLE "persistent"."public"."revenue_2026" AS
|
|
|
237
257
|
SELECT region, SUM(amount) FROM scratch_orders GROUP BY region;
|
|
238
258
|
```
|
|
239
259
|
|
|
240
|
-
**
|
|
260
|
+
**Metadata catalogs:** local and persistent tables share the persistent
|
|
261
|
+
`_table_catalog`, keyed by table name across their union. `set_table_metadata`
|
|
262
|
+
therefore targets an existing catalog entry rather than re-checking that the
|
|
263
|
+
table exists in a selected local/persistent database. Each writable
|
|
264
|
+
user-attached database has its own per-database catalog; read-only attachments
|
|
265
|
+
cannot be metadata targets.
|
|
241
266
|
|
|
242
267
|
**Detach safety:** `detach_database` rejects with `InvalidArgument` if any active watcher targets the alias — call `unwatch_directory` first. This prevents the watcher's pool from silently writing into a now-detached file (or worse, the wrong file if the alias is later re-attached to a different path).
|
|
243
268
|
|
|
@@ -255,7 +280,29 @@ hyperdb-mcp daemon # Run as a daemon explicitly (rarely needed)
|
|
|
255
280
|
|
|
256
281
|
State files live at `~/.hyperdb/` by default (override with `HYPERDB_STATE_DIR`).
|
|
257
282
|
|
|
258
|
-
|
|
283
|
+
For installation and configuration diagnostics that also work before MCP can start, use the native doctor command:
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
hyperdb-mcp doctor
|
|
287
|
+
hyperdb-mcp doctor --json
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Doctor is side-effect-free: it creates no directories, does not start a daemon
|
|
291
|
+
or `hyperd`, and does not open or create a database. Its native executable, MCP build,
|
|
292
|
+
and compiled Rust API identities are authoritative; npm wrapper/platform
|
|
293
|
+
details are bounded, optional launcher-reported provenance. A live daemon is
|
|
294
|
+
attributed only after a fresh `STATUS` response is verified. Reports contain
|
|
295
|
+
local paths; review them before sharing.
|
|
296
|
+
|
|
297
|
+
**Port discovery.** MCP auto-spawn discovers a live daemon first, then scans
|
|
298
|
+
upward from **7485** across 16 candidates before starting one at the selected
|
|
299
|
+
exact port. Setting `HYPERDB_DAEMON_PORT` pins auto-spawn to one candidate.
|
|
300
|
+
In contrast, a manually launched foreground `hyperdb-mcp daemon` never scans:
|
|
301
|
+
`--port <PORT>` binds that exact port, while an omitted `--port` binds the
|
|
302
|
+
configured/base port exactly. The health port doubles as a single-instance
|
|
303
|
+
lock and identity check: clients send `PING` and require a
|
|
304
|
+
`PONG hyperdb-mcp <version>` reply before trusting a daemon, so an unrelated
|
|
305
|
+
process is not mistaken for HyperDB.
|
|
259
306
|
|
|
260
307
|
**Staying resident.** By default the daemon never idle-shuts-down — keeping `hyperd` warm means the next tool call connects immediately instead of triggering a "restarting, please retry" round-trip. To opt into auto-shutdown (e.g. on CI), pass `--idle-timeout <SECS>` or set `HYPERDB_DAEMON_IDLE_TIMEOUT`.
|
|
261
308
|
|
|
@@ -273,7 +320,7 @@ If hyperd repeatedly fails to start (3 attempts within 60 seconds — e.g., misc
|
|
|
273
320
|
|
|
274
321
|
| Flag | Behavior |
|
|
275
322
|
|---|---|
|
|
276
|
-
| `--read-only` |
|
|
323
|
+
| `--read-only` | Guards `execute`, all four `load_*` tools, `watch_directory`, saved-query mutations, `set_table_metadata`, `copy_query`, `kv_set`, `kv_set_many`, `kv_delete`, `kv_pop`, `kv_clear`, and writable/create attachment. Read-only attachment, `unwatch_directory`, and export (including `.hyper`) stay available. See [Read-Only Mode](#read-only-mode). |
|
|
277
324
|
|
|
278
325
|
---
|
|
279
326
|
|
|
@@ -312,11 +359,11 @@ query_file(path: '/tmp/sales.parquet', sql: 'SELECT TOP 10 * FROM sales ORDER BY
|
|
|
312
359
|
| `table_name` | string | no | Table name — defaults to filename stem |
|
|
313
360
|
| `schema` | object | no | Partial column-name → type map (see [Schema Overrides](#schema-overrides)) |
|
|
314
361
|
|
|
315
|
-
###
|
|
362
|
+
### Database Tools
|
|
316
363
|
|
|
317
364
|
#### `load_data`
|
|
318
365
|
|
|
319
|
-
Load inline data into a named
|
|
366
|
+
Load inline data into a named local, persistent, or attached-database table.
|
|
320
367
|
|
|
321
368
|
```
|
|
322
369
|
load_data(table: 'customers', data: '[{"id":1,"name":"Alice"},...]')
|
|
@@ -332,7 +379,7 @@ load_data(table: 'customers', data: '[{"id":1,"name":"Alice"},...]')
|
|
|
332
379
|
|
|
333
380
|
#### `load_file`
|
|
334
381
|
|
|
335
|
-
Load a file into a named
|
|
382
|
+
Load a file into a named local, persistent, or attached-database table.
|
|
336
383
|
|
|
337
384
|
```
|
|
338
385
|
load_file(table: 'orders', path: '/tmp/orders.csv')
|
|
@@ -346,14 +393,14 @@ load_file(table: 'orders', path: '/tmp/orders.csv')
|
|
|
346
393
|
| `schema` | object | no | Partial column-name → type map (see [Schema Overrides](#schema-overrides)) |
|
|
347
394
|
|
|
348
395
|
When you're unsure of the right types — or recovering from a previous
|
|
349
|
-
`SCHEMA_MISMATCH` — call [`inspect_file`](#
|
|
396
|
+
`SCHEMA_MISMATCH` — call [`inspect_file`](#inspect_file) first. It reports the
|
|
350
397
|
exact schema `load_file` would use plus per-column `min` / `max` / `null_count`
|
|
351
398
|
so you can build a minimal, correct override in one shot.
|
|
352
399
|
|
|
353
400
|
#### `load_iceberg`
|
|
354
401
|
|
|
355
402
|
Load an [Apache Iceberg](https://iceberg.apache.org/) table into a named
|
|
356
|
-
|
|
403
|
+
local table. Pass the absolute path to the Iceberg table root (the
|
|
357
404
|
directory containing `metadata/` and `data/`); hyperd's native Iceberg
|
|
358
405
|
reader derives the schema and resolves the snapshot.
|
|
359
406
|
|
|
@@ -374,7 +421,7 @@ Iceberg table metadata.
|
|
|
374
421
|
|
|
375
422
|
#### `query`
|
|
376
423
|
|
|
377
|
-
Run a **read-only** SQL query against
|
|
424
|
+
Run a **read-only** SQL query against local (default), persistent, or an attached database. Accepts `SELECT`, `WITH`, `EXPLAIN`, `SHOW`, `VALUES`. For DDL/DML use `execute`.
|
|
378
425
|
|
|
379
426
|
```
|
|
380
427
|
query(sql: 'SELECT c.name, SUM(o.amount) FROM orders o JOIN customers c ON o.customer_id = c.id GROUP BY c.name')
|
|
@@ -397,6 +444,7 @@ execute(sql: [
|
|
|
397
444
|
```
|
|
398
445
|
|
|
399
446
|
Validation rules enforced before any SQL hits the server:
|
|
447
|
+
|
|
400
448
|
- Array must be non-empty; no element may be empty / whitespace-only / comment-only.
|
|
401
449
|
- No element may be read-only — use `query` for SELECT/WITH/EXPLAIN.
|
|
402
450
|
- DDL and DML cannot be mixed in one batch (Hyper aborts mixed transactions with SQLSTATE 0A000).
|
|
@@ -405,7 +453,7 @@ Validation rules enforced before any SQL hits the server:
|
|
|
405
453
|
|
|
406
454
|
#### `describe`
|
|
407
455
|
|
|
408
|
-
List all
|
|
456
|
+
List all tables in the selected database with their schemas, column types, and row counts.
|
|
409
457
|
|
|
410
458
|
#### `sample`
|
|
411
459
|
|
|
@@ -510,23 +558,24 @@ delete_query(name: 'top_5_customers')
|
|
|
510
558
|
Returns `{ "deleted": true }` when the query existed, `{ "deleted": false }`
|
|
511
559
|
when it did not (no error on unknown names). Disabled in read-only mode.
|
|
512
560
|
|
|
513
|
-
### Key-Value
|
|
561
|
+
### Key-Value Scratchpad
|
|
514
562
|
|
|
515
563
|
Lightweight named scratchpad for stashing a value under `store` + `key` and
|
|
516
564
|
recalling it later — remember a variable, a summary, a JSON config, or a
|
|
517
565
|
work-queue entry without creating a table or running `load_data`.
|
|
518
566
|
|
|
519
|
-
> **Stores default to the
|
|
567
|
+
> **Stores default to the local database and are LOST on server restart.**
|
|
520
568
|
> Pass `database="persistent"` (or `persist=true`) to make a store durable
|
|
521
569
|
> across restarts, or an attached alias to target that database. Each database
|
|
522
570
|
> has its own isolated set of stores; a store in one database is invisible from
|
|
523
571
|
> another.
|
|
524
572
|
|
|
525
|
-
|
|
573
|
+
Nine tools cover the surface:
|
|
526
574
|
|
|
527
575
|
| Tool | Purpose | Parameters |
|
|
528
576
|
|---|---|---|
|
|
529
577
|
| `kv_set` | Write/overwrite a value (upsert) | `store`, `key`, `value`, `database`, `persist` |
|
|
578
|
+
| `kv_set_many` | Atomically write an `entries` batch, optionally skipping existing keys | `store`, `entries`, `overwrite`, `database`, `persist` |
|
|
530
579
|
| `kv_get` | Read a value by store + key (`value` is null when absent, not an error) | `store`, `key`, `database`, `persist` |
|
|
531
580
|
| `kv_delete` | Remove one key (`{deleted: true/false}`, no error on unknown key) | `store`, `key`, `database`, `persist` |
|
|
532
581
|
| `kv_list` | List all keys in a store, sorted ascending | `store`, `database`, `persist` |
|
|
@@ -541,7 +590,13 @@ kv_get(store: 'session', key: 'last_report')
|
|
|
541
590
|
```
|
|
542
591
|
|
|
543
592
|
Key properties:
|
|
544
|
-
|
|
593
|
+
|
|
594
|
+
- **Read-only mode** — the five mutators (`kv_set`, `kv_set_many`, `kv_delete`, `kv_pop`, `kv_clear`) are disabled and return `READ_ONLY_VIOLATION`; the global guard leaves the four readers (`kv_get`, `kv_list`, `kv_size`, `kv_list_stores`) available.
|
|
595
|
+
- **Attached-database access** — every attached target must have been attached
|
|
596
|
+
with `writable=true`, even for readers, because a KV call may need to
|
|
597
|
+
initialize its backing table. The global `--read-only` guard still blocks
|
|
598
|
+
only the five mutators; an allowed reader can use local/persistent storage but
|
|
599
|
+
cannot use a read-only user attachment.
|
|
545
600
|
- **Pop order** — `kv_pop` removes and returns the **lowest-keyed** entry in lexicographic key order (not insertion order), making a store usable as a simple work queue.
|
|
546
601
|
- **No store registry** — a store that becomes empty simply **drops out** of `kv_list_stores`; there is no separate registry of store names.
|
|
547
602
|
- **Backing table** — values live in `_hyperdb_kv_store(store_name, key, value)`, which is indexless (Hyper has no indexes) and hidden from `describe` by its `_hyperdb_` prefix, but is directly queryable — e.g. `LEFT JOIN` it to enrich an analytical table (always filter on `kv.store_name`). Uniqueness of `(store_name, key)` is enforced by the tool layer's upsert, atomic within a single server process. See the `hyper://schema/kv` resource for the schema and join pattern.
|
|
@@ -564,13 +619,16 @@ export(sql: 'SELECT ...', path: '~/Desktop/analysis.hyper', format: 'hyper')
|
|
|
564
619
|
| `path` | string | yes | Output file path |
|
|
565
620
|
| `format` | string | yes | `"csv"`, `"parquet"`, `"iceberg"`, `"arrow_ipc"`, or `"hyper"` |
|
|
566
621
|
|
|
567
|
-
The `"hyper"` format produces a `.hyper` file that opens directly in **Tableau
|
|
622
|
+
The `"hyper"` format produces a `.hyper` file that opens directly in **Tableau
|
|
623
|
+
Desktop**. It does not mutate the source database; it creates or replaces the
|
|
624
|
+
destination and materializes every user table from the selected source into it.
|
|
568
625
|
|
|
569
626
|
### Visualization
|
|
570
627
|
|
|
571
628
|
#### `chart`
|
|
572
629
|
|
|
573
|
-
Render a
|
|
630
|
+
Render a bounded quick diagnostic from a SQL query. This convenience tool is
|
|
631
|
+
for inspecting or sharing one chart, not for dashboard/layout composition.
|
|
574
632
|
|
|
575
633
|
```
|
|
576
634
|
chart(sql: 'SELECT product, SUM(revenue) as total FROM sales GROUP BY product', chart_type: 'bar', x: 'product', y: 'total', title: 'Revenue by Product')
|
|
@@ -579,17 +637,46 @@ chart(sql: 'SELECT product, SUM(revenue) as total FROM sales GROUP BY product',
|
|
|
579
637
|
| Parameter | Type | Required | Description |
|
|
580
638
|
|-----------|------|----------|-------------|
|
|
581
639
|
| `sql` | string | yes | Read-only SQL query returning the data to plot |
|
|
640
|
+
| `database` | string | no | Route SQL to `local` (default), `persistent`, or an attached alias |
|
|
582
641
|
| `chart_type` | string | yes | `bar`, `line`, `scatter`, or `histogram` |
|
|
583
642
|
| `x` | string | yes* | X-axis column (for histogram, the value column) |
|
|
584
643
|
| `y` | string | yes* | Y-axis column (not required for histogram) |
|
|
585
644
|
| `series` | string | no | Grouping column for multi-series plots |
|
|
645
|
+
| `color_map` | object | no | Map series names to hex colors such as `{"East":"#e41a1c"}` |
|
|
646
|
+
| `label_points` | bool | no | Label line/scatter points by series and suppress their legend |
|
|
586
647
|
| `title` | string | no | Chart title |
|
|
587
648
|
| `format` | string | no | `png` (default) or `svg` |
|
|
588
649
|
| `width` | int | no | Pixels (default 800, clamped 200..4096) |
|
|
589
650
|
| `height` | int | no | Pixels (default 480, clamped 150..4096) |
|
|
590
651
|
| `bins` | int | no | Histogram bins (default 20, clamped 1..500) |
|
|
591
|
-
|
|
592
|
-
|
|
652
|
+
| `output_path` | string | no | Destination file; parent directories are created |
|
|
653
|
+
| `inline` | bool | no | Return image bytes inline (default `true`) |
|
|
654
|
+
| `overwrite` | bool | no | Permit replacing `output_path` (default `true`) |
|
|
655
|
+
| `bar_orientation` | string | no | `vertical` (default) or `horizontal`; bars only |
|
|
656
|
+
| `label_values` | bool | no | Draw each original y scalar beside its bar |
|
|
657
|
+
| `show_legend` | bool | no | Show series legend (default `true`) |
|
|
658
|
+
| `y_scale` | string | no | `linear` (default) or positive `log`; no log histograms |
|
|
659
|
+
| `x_as_category` | bool | no | Force even categorical spacing on line/scatter x values |
|
|
660
|
+
| `x_range` / `y_range` | number pair | no | Explicit finite, strictly increasing bounds |
|
|
661
|
+
|
|
662
|
+
With neither path nor delivery override, the PNG (or requested SVG) is returned
|
|
663
|
+
inline and no file is written. `output_path` means write plus inline; set
|
|
664
|
+
`inline=false` for disk-only output, with an auto-generated temp path when no
|
|
665
|
+
path is supplied. Explicit `format` and the path extension must agree. The
|
|
666
|
+
result ends with a stats JSON block containing `resolved_database` and, when
|
|
667
|
+
written, `output_path`.
|
|
668
|
+
|
|
669
|
+
Line/scatter DATE, TIMESTAMP, and TIMESTAMPTZ x columns use proportional
|
|
670
|
+
temporal spacing automatically; TEXT is categorical. Set `x_as_category=true`
|
|
671
|
+
only when even spacing is deliberate. Bars always treat x as categorical.
|
|
672
|
+
Horizontal rankings preserve SQL row order with the first row at the top.
|
|
673
|
+
Long or Unicode labels are accepted but not auto-sized, so increase width or
|
|
674
|
+
height when needed.
|
|
675
|
+
|
|
676
|
+
All explicit ranges must be finite, strictly increasing, and representable.
|
|
677
|
+
Log y values and bounds must also be positive and the explicit range must
|
|
678
|
+
contain every plotted value. Log bars begin at the effective positive lower
|
|
679
|
+
bound, never zero.
|
|
593
680
|
|
|
594
681
|
### Incremental Ingest
|
|
595
682
|
|
|
@@ -611,6 +698,7 @@ unwatch_directory(path: '/tmp/inbox')
|
|
|
611
698
|
On success, both files are deleted. On failure, both are moved to `failed/` with a `.error` JSON file.
|
|
612
699
|
|
|
613
700
|
Key properties:
|
|
701
|
+
|
|
614
702
|
- **One directory, one table, append mode** — files must match the target schema.
|
|
615
703
|
- **Initial sweep** — pre-existing `.ready` files are processed immediately.
|
|
616
704
|
- **Read-only mode** — `watch_directory` is blocked; `unwatch_directory` is always allowed.
|
|
@@ -620,27 +708,32 @@ Key properties:
|
|
|
620
708
|
|
|
621
709
|
#### `status`
|
|
622
710
|
|
|
623
|
-
Returns
|
|
711
|
+
Returns MCP/native/API installation identity, daemon and Hyper connection facts,
|
|
712
|
+
`default_database: "local"`, persistent-path state, table/row/disk statistics,
|
|
713
|
+
read-only state, attachments, and active watchers. A full response has
|
|
714
|
+
`engine_busy: false`. When `engine_busy: true`, the prompt response is partial:
|
|
715
|
+
SQL-dependent statistics are intentionally omitted and `hyperd_running: false`
|
|
716
|
+
is inconclusive. Retry `status` after the in-progress operation completes.
|
|
624
717
|
|
|
625
718
|
---
|
|
626
719
|
|
|
627
720
|
## MCP Resources
|
|
628
721
|
|
|
629
|
-
The server exposes
|
|
722
|
+
The server exposes local and persistent database state as MCP **Resources**, discoverable via
|
|
630
723
|
`resources/list`. Each resource advertises its own MIME type so clients
|
|
631
724
|
can route it appropriately (LLM context vs. file download vs. chart).
|
|
632
725
|
|
|
633
726
|
| URI | MIME | Content |
|
|
634
727
|
|-----|------|---------|
|
|
635
|
-
| `hyper://workspace` | `application/json` |
|
|
728
|
+
| `hyper://workspace` | `application/json` | Local/persistent state, table count, total rows, disk usage |
|
|
636
729
|
| `hyper://tables` | `application/json` | Full list of tables with schemas and row counts |
|
|
637
|
-
| `hyper://readme` | `text/markdown` |
|
|
730
|
+
| `hyper://readme` | `text/markdown` | Database overview as markdown: table catalog, related resources per table, and tool hints for a cold-started LLM |
|
|
638
731
|
| `hyper://tables/{name}/schema` | `application/json` | Columns, types, nullability, and row count for one table |
|
|
639
732
|
| `hyper://tables/{name}/sample` | `application/json` | First 5 rows of a table as JSON, with schema |
|
|
640
733
|
| `hyper://tables/{name}/csv-sample` | `text/csv` | First 20 rows of a table as CSV, header-first |
|
|
641
734
|
| `hyper://queries/{name}/definition` | `application/json` | Stored SQL + metadata for a saved query |
|
|
642
735
|
| `hyper://queries/{name}/result` | `application/json` | Live result of a saved query — re-runs on every read |
|
|
643
|
-
| `hyper://schema/kv` | `text/plain` | KV scratchpad schema:
|
|
736
|
+
| `hyper://schema/kv` | `text/plain` | KV scratchpad schema: backing table and `LEFT JOIN` pattern, local-vs-persistent durability, global read-only guards, and writable user-attachment requirement (including readers) |
|
|
644
737
|
|
|
645
738
|
Resource templates (discoverable via `resources/templates/list`):
|
|
646
739
|
|
|
@@ -670,8 +763,8 @@ of mutation:
|
|
|
670
763
|
| `load_data` / `load_file` (replace mode) | `hyper://workspace`, `hyper://tables`, `hyper://readme`, per-table schema + sample + csv-sample | Yes |
|
|
671
764
|
| `load_data` / `load_file` (append mode) | Same per-table + summary URIs | No ¹ |
|
|
672
765
|
| `watch_directory` ingest of a `.ready` pair | Same per-table + summary URIs | No ¹ |
|
|
673
|
-
| `execute` (INSERT / UPDATE / DELETE) |
|
|
674
|
-
| `execute` (CREATE / DROP / ALTER / TRUNCATE / RENAME) |
|
|
766
|
+
| `execute` (INSERT / UPDATE / DELETE) | Database-summary URIs | No |
|
|
767
|
+
| `execute` (CREATE / DROP / ALTER / TRUNCATE / RENAME) | Database-summary URIs | Yes |
|
|
675
768
|
| `save_query` | (none per-URI) | Yes — two new `hyper://queries/{name}/...` resources |
|
|
676
769
|
| `delete_query` | `hyper://queries/{name}/definition`, `hyper://queries/{name}/result` | Yes — two resources disappeared |
|
|
677
770
|
|
|
@@ -706,8 +799,8 @@ Four guided analytical workflows registered as MCP **Prompts**.
|
|
|
706
799
|
hyperdb-mcp --persistent-db ~/analytics.hyper --read-only
|
|
707
800
|
```
|
|
708
801
|
|
|
709
|
-
- **Allowed:** `query`, `query_data`, `query_file`, `describe`, `sample`, `inspect_file`, `status`, `export`, and the KV readers `kv_get`, `kv_list`, `kv_size`, `kv_list_stores`
|
|
710
|
-
- **Blocked:** `execute`, `load_data`, `load_file`, `watch_directory`, `save_query`, `delete_query`,
|
|
802
|
+
- **Allowed:** `query`, `query_data`, `query_file`, `describe`, `sample`, `inspect_file`, `status`, `chart`, `export` in every format including Hyper, read-only `attach_database`, `detach_database`, `list_attached_databases`, `unwatch_directory`, `get_readme`, and the KV readers `kv_get`, `kv_list`, `kv_size`, `kv_list_stores`
|
|
803
|
+
- **Blocked:** `execute`, `load_data`, `load_file`, `load_files`, `load_iceberg`, `watch_directory`, `save_query`, `delete_query`, `set_table_metadata`, `copy_query`, `kv_set`, `kv_set_many`, `kv_delete`, `kv_pop`, and `kv_clear` — return `READ_ONLY_VIOLATION`. `attach_database` is also guarded when `writable: true` or `on_missing: "create"`; ordinary read-only attachment remains available.
|
|
711
804
|
- **Resources, prompts, and resource subscriptions** work normally — read-only clients can still subscribe to `hyper://...` URIs and receive notifications when other (non-read-only) connections mutate state
|
|
712
805
|
|
|
713
806
|
The `query` tool also enforces read-only at the SQL level — only `SELECT`/`WITH`/`EXPLAIN`/`SHOW`/`VALUES` are accepted.
|
|
@@ -823,6 +916,7 @@ Both statements run inside a single Hyper transaction — they commit together o
|
|
|
823
916
|
The Hyper Rust API supports `BEGIN` / `COMMIT` / `ROLLBACK` plus an RAII `Transaction` guard (see [`docs/TRANSACTIONS.md`](../docs/TRANSACTIONS.md)). The MCP `execute` tool surfaces this as the `sql` array shape: pass multiple statements and they run atomically.
|
|
824
917
|
|
|
825
918
|
Hyper-specific limits worth remembering when batching:
|
|
919
|
+
|
|
826
920
|
- **DDL after DML in the same transaction is rejected** with SQLSTATE 0A000. The `execute` tool catches this up front — mixing CREATE/DROP/ALTER with INSERT/UPDATE/DELETE in one batch is rejected with an actionable error.
|
|
827
921
|
- **DDL is auto-committed** even inside a transaction. `execute` rejects multi-element all-DDL batches because the "atomic" promise can't be honored — issue each DDL call as its own one-element array.
|
|
828
922
|
- **After any error inside a transaction**, the connection enters aborted state and only ROLLBACK is accepted next. The `execute` tool handles this for you — on any per-statement failure the wrapper issues ROLLBACK before surfacing the error.
|
|
@@ -837,7 +931,8 @@ Full reference: [Data Cloud SQL Reference](https://developer.salesforce.com/docs
|
|
|
837
931
|
hyperdb-mcp [OPTIONS] [COMMAND]
|
|
838
932
|
|
|
839
933
|
Commands:
|
|
840
|
-
daemon Run
|
|
934
|
+
daemon Run a foreground daemon managing a shared hyperd process
|
|
935
|
+
doctor Inspect identities/configuration without starting Hyper
|
|
841
936
|
|
|
842
937
|
Options:
|
|
843
938
|
--persistent-db <PATH> Path to the persistent .hyper file. Defaults to the platform
|
|
@@ -847,8 +942,8 @@ Options:
|
|
|
847
942
|
the HYPERDB_PERSISTENT_DB env var.
|
|
848
943
|
--ephemeral-only Skip the persistent attachment entirely. Disables save_query
|
|
849
944
|
persistence (queries fall back to session storage).
|
|
850
|
-
--read-only
|
|
851
|
-
|
|
945
|
+
--read-only Guard all load/mutation tools and writable/create attachment;
|
|
946
|
+
read-only attachment, unwatch, and all export formats stay allowed
|
|
852
947
|
--no-daemon Disable the shared daemon and spawn a private hyperd
|
|
853
948
|
|
|
854
949
|
Deprecated:
|
|
@@ -856,19 +951,21 @@ Deprecated:
|
|
|
856
951
|
stderr warning, and will be removed in a future release.
|
|
857
952
|
|
|
858
953
|
Daemon subcommand:
|
|
859
|
-
hyperdb-mcp daemon Start the
|
|
954
|
+
hyperdb-mcp daemon Start foreground on the configured/base port exactly
|
|
860
955
|
hyperdb-mcp daemon stop Gracefully stop the running daemon
|
|
861
956
|
hyperdb-mcp daemon status Show running daemon info
|
|
862
|
-
hyperdb-mcp daemon --port <PORT>
|
|
863
|
-
|
|
957
|
+
hyperdb-mcp daemon --port <PORT> Bind this exact health/lock port; foreground
|
|
958
|
+
startup never performs the auto-spawn scan.
|
|
864
959
|
hyperdb-mcp daemon --idle-timeout <SECS> Opt into idle shutdown after SECS idle.
|
|
865
960
|
When omitted, the daemon stays resident.
|
|
866
961
|
|
|
867
962
|
Environment:
|
|
868
|
-
HYPERD_PATH
|
|
963
|
+
HYPERD_PATH Hyperd executable or containing directory; when absent or
|
|
964
|
+
non-UTF-8, walk upward for .hyperd/current/hyperd (no PATH lookup)
|
|
869
965
|
HYPERDB_PERSISTENT_DB Override the default persistent-db path
|
|
870
966
|
HYPERDB_STATE_DIR Override daemon state directory (default ~/.hyperdb/)
|
|
871
|
-
HYPERDB_DAEMON_PORT Pin
|
|
967
|
+
HYPERDB_DAEMON_PORT Pin auto-spawn discovery to one health/lock candidate;
|
|
968
|
+
foreground startup binds this configured/base port exactly
|
|
872
969
|
HYPERDB_DAEMON_IDLE_TIMEOUT Opt into idle shutdown (seconds); default: stay resident
|
|
873
970
|
```
|
|
874
971
|
|
|
@@ -887,6 +984,7 @@ Errors include a machine-readable code and a suggestion:
|
|
|
887
984
|
| `SQL_ERROR` | Invalid SQL | Fix the query |
|
|
888
985
|
| `TABLE_NOT_FOUND` | Table doesn't exist | Use `describe` to list tables |
|
|
889
986
|
| `READ_ONLY_VIOLATION` | Mutating op in read-only mode | Use `query_*` / `inspect_file`, or restart without `--read-only` |
|
|
987
|
+
| `RESOURCE_BUSY` | The reserved persistent attachment hit file contention (SQLSTATE 55006) | Run `hyperdb-mcp doctor`; compare client/daemon identities; close the possible owner (Hyper, Tableau, or another process), or copy/select another `.hyper` file; retry |
|
|
890
988
|
| `CONNECTION_LOST` | `hyperd` crashed or wire protocol desynchronized | Retry — the server tears down the engine and reconnects on the next call |
|
|
891
989
|
|
|
892
990
|
Server-returned errors include a machine-readable `code`, a `message`, and a
|
|
@@ -895,6 +993,12 @@ an overflow names the workflow directly: "call `inspect_file`, then retry with
|
|
|
895
993
|
a partial schema override", so the LLM does not need to infer the recovery
|
|
896
994
|
steps from the SQLSTATE alone.
|
|
897
995
|
|
|
996
|
+
`RESOURCE_BUSY` is contextual: only contention while attaching the configured
|
|
997
|
+
persistent file gets this classification. The error preserves the effective
|
|
998
|
+
path, raw Hyper diagnostic, and SQLSTATE; unrelated `55006` SQL errors remain
|
|
999
|
+
`SQL_ERROR`. Doctor compares evidence but does not claim which possible owner
|
|
1000
|
+
holds the file and never kills a process.
|
|
1001
|
+
|
|
898
1002
|
---
|
|
899
1003
|
|
|
900
1004
|
## Troubleshooting
|
|
@@ -903,7 +1007,9 @@ steps from the SQLSTATE alone.
|
|
|
903
1007
|
|
|
904
1008
|
**Server registered but tools not callable (Claude Code)** — Add `"mcp__HyperDB__*"` to the `permissions.allow` array in `~/.claude/settings.json`.
|
|
905
1009
|
|
|
906
|
-
**hyperd not found** — Set `HYPERD_PATH` in the MCP server's `env` config
|
|
1010
|
+
**hyperd not found** — Set `HYPERD_PATH` in the MCP server's `env` config to
|
|
1011
|
+
the executable or its containing directory, or install it under an ancestor's
|
|
1012
|
+
`.hyperd/current/` directory. The runtime does not search the general `PATH`.
|
|
907
1013
|
|
|
908
1014
|
---
|
|
909
1015
|
|
package/bin.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright (c) 2026, Salesforce, Inc. All rights reserved.
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
4
4
|
|
|
5
|
-
const {
|
|
5
|
+
const { spawnSync } = require('child_process')
|
|
6
6
|
const { join, dirname } = require('path')
|
|
7
7
|
const { existsSync } = require('fs')
|
|
8
8
|
|
|
@@ -39,20 +39,39 @@ function findBinary() {
|
|
|
39
39
|
|
|
40
40
|
// Try resolving from the installed platform package
|
|
41
41
|
try {
|
|
42
|
-
const
|
|
42
|
+
const packagePath = require.resolve(`${pkg}/package.json`)
|
|
43
|
+
const pkgDir = dirname(packagePath)
|
|
43
44
|
const bin = join(pkgDir, getBinaryName())
|
|
44
|
-
if (existsSync(bin)) return { bin, dir: pkgDir }
|
|
45
|
+
if (existsSync(bin)) return { bin, dir: pkgDir, pkg, packagePath }
|
|
45
46
|
} catch (_) {}
|
|
46
47
|
|
|
47
48
|
// Fallback: binary in platform subdirectory (local dev / assemble-npm.sh)
|
|
48
49
|
const platformDir = pkg.replace('hyperdb-mcp-', '')
|
|
49
50
|
const subdir = join(__dirname, platformDir)
|
|
50
51
|
const subdirBin = join(subdir, getBinaryName())
|
|
51
|
-
|
|
52
|
+
const sourcePackagePath = join(subdir, 'package.json')
|
|
53
|
+
if (existsSync(subdirBin)) {
|
|
54
|
+
return {
|
|
55
|
+
bin: subdirBin,
|
|
56
|
+
dir: subdir,
|
|
57
|
+
pkg,
|
|
58
|
+
packagePath: sourcePackagePath,
|
|
59
|
+
}
|
|
60
|
+
}
|
|
52
61
|
|
|
53
62
|
// Fallback: binary in same directory
|
|
54
63
|
const localBin = join(__dirname, getBinaryName())
|
|
55
|
-
if (existsSync(localBin))
|
|
64
|
+
if (existsSync(localBin)) {
|
|
65
|
+
return {
|
|
66
|
+
bin: localBin,
|
|
67
|
+
dir: __dirname,
|
|
68
|
+
pkg,
|
|
69
|
+
// The manifest sits next to the binary in this branch — not in the
|
|
70
|
+
// platform subdirectory `sourcePackagePath` points at (that path does
|
|
71
|
+
// not exist here), so recompute it relative to __dirname.
|
|
72
|
+
packagePath: join(__dirname, 'package.json'),
|
|
73
|
+
}
|
|
74
|
+
}
|
|
56
75
|
|
|
57
76
|
throw new Error(
|
|
58
77
|
`Could not find hyperdb-mcp binary for ${platform}-${arch}. ` +
|
|
@@ -60,24 +79,91 @@ function findBinary() {
|
|
|
60
79
|
)
|
|
61
80
|
}
|
|
62
81
|
|
|
63
|
-
|
|
82
|
+
function packageIdentity(packagePath, fallbackName) {
|
|
83
|
+
let manifest = {}
|
|
84
|
+
try {
|
|
85
|
+
manifest = require(packagePath)
|
|
86
|
+
} catch (_) {}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
name: typeof manifest.name === 'string' ? manifest.name : fallbackName,
|
|
90
|
+
version: typeof manifest.version === 'string' ? manifest.version : null,
|
|
91
|
+
package_path: packagePath,
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function buildLauncherInfo({ wrapper, platform, executable_path }) {
|
|
96
|
+
return {
|
|
97
|
+
wrapper: {
|
|
98
|
+
name: wrapper.name,
|
|
99
|
+
version: wrapper.version ?? null,
|
|
100
|
+
package_path: wrapper.package_path,
|
|
101
|
+
},
|
|
102
|
+
platform: {
|
|
103
|
+
name: platform.name,
|
|
104
|
+
version: platform.version ?? null,
|
|
105
|
+
package_path: platform.package_path,
|
|
106
|
+
},
|
|
107
|
+
executable_path,
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function prepareLauncherEnvironment({
|
|
112
|
+
inherited_env,
|
|
113
|
+
configured_hyperd,
|
|
114
|
+
bundled_hyperd,
|
|
115
|
+
launcher_info,
|
|
116
|
+
}) {
|
|
117
|
+
const env = { ...inherited_env }
|
|
118
|
+
if (!configured_hyperd && bundled_hyperd !== undefined) {
|
|
119
|
+
env.HYPERD_PATH = bundled_hyperd
|
|
120
|
+
}
|
|
121
|
+
env.HYPERDB_MCP_LAUNCHER_INFO = JSON.stringify(launcher_info)
|
|
122
|
+
return env
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function launch({ executable_path, args, env, spawnSync }) {
|
|
126
|
+
const result = spawnSync(executable_path, args, {
|
|
127
|
+
stdio: 'inherit',
|
|
128
|
+
env,
|
|
129
|
+
})
|
|
64
130
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const hyperd = join(dir, getHyperdName())
|
|
68
|
-
if (existsSync(hyperd)) {
|
|
69
|
-
process.env.HYPERD_PATH = hyperd
|
|
131
|
+
if (result.error) {
|
|
132
|
+
throw result.error
|
|
70
133
|
}
|
|
134
|
+
|
|
135
|
+
return result.status ?? 1
|
|
71
136
|
}
|
|
72
137
|
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
138
|
+
function main() {
|
|
139
|
+
const { bin, dir, pkg, packagePath } = findBinary()
|
|
140
|
+
const configuredHyperd = process.env.HYPERD_PATH
|
|
141
|
+
|
|
142
|
+
// Point hyperdb-mcp at the bundled hyperd if not already set
|
|
143
|
+
const bundledHyperd = join(dir, getHyperdName())
|
|
144
|
+
const launcherInfo = buildLauncherInfo({
|
|
145
|
+
wrapper: packageIdentity(join(__dirname, 'package.json'), 'hyperdb-mcp'),
|
|
146
|
+
platform: packageIdentity(packagePath, pkg),
|
|
147
|
+
executable_path: bin,
|
|
148
|
+
})
|
|
149
|
+
const env = prepareLauncherEnvironment({
|
|
150
|
+
inherited_env: process.env,
|
|
151
|
+
configured_hyperd: configuredHyperd,
|
|
152
|
+
bundled_hyperd: existsSync(bundledHyperd) ? bundledHyperd : undefined,
|
|
153
|
+
launcher_info: launcherInfo,
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
// Spawn the MCP server, inheriting stdio for MCP protocol communication
|
|
157
|
+
return launch({
|
|
158
|
+
executable_path: bin,
|
|
159
|
+
args: process.argv.slice(2),
|
|
160
|
+
env,
|
|
161
|
+
spawnSync,
|
|
162
|
+
})
|
|
163
|
+
}
|
|
78
164
|
|
|
79
|
-
if (
|
|
80
|
-
|
|
165
|
+
if (require.main === module) {
|
|
166
|
+
process.exit(main())
|
|
81
167
|
}
|
|
82
168
|
|
|
83
|
-
|
|
169
|
+
module.exports = { buildLauncherInfo, prepareLauncherEnvironment, launch }
|
package/package.json
CHANGED
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
"engines": {
|
|
30
30
|
"node": ">= 21"
|
|
31
31
|
},
|
|
32
|
-
"version": "0.
|
|
32
|
+
"version": "1.0.0-rc.1",
|
|
33
33
|
"optionalDependencies": {
|
|
34
|
-
"hyperdb-mcp-darwin-arm64": "0.
|
|
35
|
-
"hyperdb-mcp-linux-x64-gnu": "0.
|
|
36
|
-
"hyperdb-mcp-win32-x64-msvc": "0.
|
|
34
|
+
"hyperdb-mcp-darwin-arm64": "1.0.0-rc.1",
|
|
35
|
+
"hyperdb-mcp-linux-x64-gnu": "1.0.0-rc.1",
|
|
36
|
+
"hyperdb-mcp-win32-x64-msvc": "1.0.0-rc.1"
|
|
37
37
|
}
|
|
38
38
|
}
|