@hydradb/mcp 1.2.0 → 1.2.2
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/CHANGELOG.md +158 -0
- package/README.md +172 -2
- package/dist/config.d.ts +40 -0
- package/dist/config.js +40 -3
- package/dist/config.js.map +1 -1
- package/dist/cypher.d.ts +51 -0
- package/dist/cypher.js +145 -0
- package/dist/cypher.js.map +1 -0
- package/dist/descriptions.d.ts +55 -0
- package/dist/descriptions.js +170 -13
- package/dist/descriptions.js.map +1 -1
- package/dist/http-config.d.ts +160 -0
- package/dist/http-config.js +253 -0
- package/dist/http-config.js.map +1 -0
- package/dist/http.d.ts +31 -0
- package/dist/http.js +350 -0
- package/dist/http.js.map +1 -0
- package/dist/hydra/client.d.ts +36 -2
- package/dist/hydra/client.js +46 -14
- package/dist/hydra/client.js.map +1 -1
- package/dist/hydra/errors.d.ts +14 -0
- package/dist/hydra/errors.js +16 -0
- package/dist/hydra/errors.js.map +1 -1
- package/dist/hydra/graph.d.ts +82 -0
- package/dist/hydra/graph.js +217 -0
- package/dist/hydra/graph.js.map +1 -0
- package/dist/hydra/index.d.ts +3 -1
- package/dist/hydra/index.js +2 -1
- package/dist/hydra/index.js.map +1 -1
- package/dist/server.d.ts +7 -1
- package/dist/server.js +387 -12
- package/dist/server.js.map +1 -1
- package/dist/tool-names.d.ts +5 -0
- package/dist/tool-names.js +15 -0
- package/dist/tool-names.js.map +1 -1
- package/package.json +10 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,163 @@ All notable changes to this project are documented here.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [1.2.2] - 2026-08-19
|
|
11
|
+
|
|
12
|
+
### Added — per-call database and collection scope overrides
|
|
13
|
+
|
|
14
|
+
Every context tool (`hydradb_query`, `hydradb_ingest`, `hydradb_list`,
|
|
15
|
+
`hydradb_inspect`, `hydradb_delete`, `hydradb_status`, and deprecated aliases)
|
|
16
|
+
now accepts optional `database` and `collection` arguments directly in its payload.
|
|
17
|
+
This allows multi-tenant SaaS agents to dynamically switch tenant/collection scopes
|
|
18
|
+
per request within a single persistent MCP session, while falling back to the session
|
|
19
|
+
defaults when omitted.
|
|
20
|
+
|
|
21
|
+
### Added — remote HTTP transport (hosted server)
|
|
22
|
+
|
|
23
|
+
The server can now run as a long-lived **HTTP endpoint** that many clients reach
|
|
24
|
+
at one URL (`https://mcp.hydradb.com`), alongside the existing stdio (`npx`) binary.
|
|
25
|
+
This is what a hosted deployment runs, and what `npm run start:http` or the new
|
|
26
|
+
Docker image runs locally — nothing to install per user.
|
|
27
|
+
|
|
28
|
+
The tool surface is unchanged: the HTTP server builds the exact same server as
|
|
29
|
+
stdio via `createHydraDBServer`. Only connection and auth are new.
|
|
30
|
+
|
|
31
|
+
- **Root endpoint and backward-compatible alias.** The server serves the MCP
|
|
32
|
+
Streamable HTTP transport directly on `/` and `/mcp`.
|
|
33
|
+
- **Per-request, multi-tenant credentials.** A hosted process has no single
|
|
34
|
+
ambient account, so each request selects its tenant with headers —
|
|
35
|
+
`Authorization: Bearer <api-key>` (or `X-HydraDB-Api-Key`) and
|
|
36
|
+
`X-HydraDB-Database`, with optional `X-HydraDB-Collection` and
|
|
37
|
+
`X-HydraDB-Graph-*`. A request that authenticates nobody is refused (`401`);
|
|
38
|
+
one that names no database is `400`. A single-tenant self-host may instead set
|
|
39
|
+
`HYDRADB_API_KEY`/`HYDRADB_DATABASE` in the environment and clients send no
|
|
40
|
+
credentials — the header path falls back to it.
|
|
41
|
+
- **Safe-by-default network posture.** Binds loopback unless `BIND_ADDRESS` is
|
|
42
|
+
set; a `Host` allowlist (`ALLOWED_HOSTS`, loopback always) answers stray
|
|
43
|
+
authorities with `421`; CORS is closed until `ALLOWED_ORIGINS` lists an
|
|
44
|
+
origin. `Base URL`/timeout/retries stay operator-only env settings and are
|
|
45
|
+
never read from a request header.
|
|
46
|
+
- New: `src/http.ts` (Express app + lifecycle), `src/http-config.ts`
|
|
47
|
+
(operator + per-request config resolution), `Dockerfile`, `.dockerignore`,
|
|
48
|
+
`npm run start:http` / `dev:http`, and a `hydradb-mcp-http` bin. Runtime deps
|
|
49
|
+
`express` and `cors` were added.
|
|
50
|
+
|
|
51
|
+
### Added — graph (BYOG) tools
|
|
52
|
+
|
|
53
|
+
Hydra DB's **graph database** offering is now reachable from the MCP. Previously
|
|
54
|
+
this server exposed only the application offering (memory and knowledge); the
|
|
55
|
+
property graphs users model and own end to end, queried in Cypher, had no client
|
|
56
|
+
surface at all despite being [documented](https://docs.hydradb.com/essentials/v2/graph-collections-byog)
|
|
57
|
+
and live.
|
|
58
|
+
|
|
59
|
+
Three new tools, all additive — nothing about the existing six changed:
|
|
60
|
+
|
|
61
|
+
| Tool | Annotation | What it does |
|
|
62
|
+
|---|---|---|
|
|
63
|
+
| `hydradb_graph_query` | `destructiveHint` | Cypher, reads and writes alike |
|
|
64
|
+
| `hydradb_graph_collections` | `readOnlyHint` | List the graphs in a graph database |
|
|
65
|
+
| `hydradb_graph_admin` | `destructiveHint` | Create a graph database; drop a collection or database |
|
|
66
|
+
|
|
67
|
+
This covers [Neo4j's MCP server](https://github.com/neo4j-contrib/mcp-neo4j)
|
|
68
|
+
`read_neo4j_cypher` and `write_neo4j_cypher`, plus the graph database lifecycle
|
|
69
|
+
its Aura server covers. Two of its capabilities are deliberately **not**
|
|
70
|
+
reproduced:
|
|
71
|
+
|
|
72
|
+
**One Cypher tool, not a read/write pair.** Neo4j splits `read_neo4j_cypher`
|
|
73
|
+
from `write_neo4j_cypher` so a host can auto-approve one and gate the other.
|
|
74
|
+
That split is only sound if the read/write classifier is right on every query,
|
|
75
|
+
and any classifier over Cypher text is a heuristic — Neo4j's own is a substring
|
|
76
|
+
scan that refuses `MATCH (p:Person) WHERE p.name = "CREATE something" RETURN
|
|
77
|
+
p.name`, a query HydraDB accepts and that mutates nothing. Rather than ship a
|
|
78
|
+
tool whose contract ("this one never writes") rests on a heuristic, there is one
|
|
79
|
+
tool, annotated destructive, and the host gates the whole graph surface. No
|
|
80
|
+
classifier ships at all.
|
|
81
|
+
|
|
82
|
+
**No schema tool.** Neo4j's `get_neo4j_schema` runs `CALL apoc.meta.schema()`,
|
|
83
|
+
which HydraDB rejects outright. A derived equivalent is not part of the product,
|
|
84
|
+
so none ships. Callers discover a collection's structure the same way they query
|
|
85
|
+
it — `MATCH (n) UNWIND labels(n) AS l RETURN l, count(*) AS c ORDER BY l` — and
|
|
86
|
+
the tool description says so.
|
|
87
|
+
|
|
88
|
+
Also worth recording: **`EXPLAIN` is not a preview.** `EXPLAIN MATCH (p:Person)
|
|
89
|
+
RETURN p` returns live rows rather than a plan, so it is documented as something
|
|
90
|
+
not to reach for.
|
|
91
|
+
|
|
92
|
+
The 256 KiB body cap IS enforced before upload, since the remote `413` arrives
|
|
93
|
+
only after the whole oversized batch has been sent — that is a transport fact
|
|
94
|
+
the client owns, not a rule about what Cypher means.
|
|
95
|
+
|
|
96
|
+
Registered by default, with one switch: `HYDRADB_MCP_GRAPH_TOOLS=0` withholds
|
|
97
|
+
all three, for memory-only users who do not want the extra tool definitions in
|
|
98
|
+
every conversation.
|
|
99
|
+
|
|
100
|
+
**The client does not inspect your Cypher.** No read/write classification, no
|
|
101
|
+
read-only mode, no local pre-rejection of unsupported constructs. All of those
|
|
102
|
+
would put a second, worse implementation of the server's rules inside a client,
|
|
103
|
+
able only to agree with the server or to be wrong — and being wrong means
|
|
104
|
+
refusing a query HydraDB would have run. The server rejects unsupported
|
|
105
|
+
constructs before executing anything (verified: a query mixing `CREATE` with a
|
|
106
|
+
procedure call leaves the node count unchanged) and its messages are more
|
|
107
|
+
specific than the ones this server used to produce. Queries are sent verbatim.
|
|
108
|
+
|
|
109
|
+
Withholding the tools is the only lockdown offered, because it is the only one
|
|
110
|
+
that is actually a guarantee.
|
|
111
|
+
|
|
112
|
+
New configuration: `HYDRADB_GRAPH_DATABASE` (falls back to `HYDRADB_DATABASE`)
|
|
113
|
+
and `HYDRADB_GRAPH_COLLECTION` (defaults to `default`). A graph database is a
|
|
114
|
+
different namespace from the memory database, so every graph tool also accepts
|
|
115
|
+
`database` and `collection` per call.
|
|
116
|
+
|
|
117
|
+
### Internal
|
|
118
|
+
|
|
119
|
+
- `HydraDB.graph` is a hand-rolled HTTP path, not an SDK call: `@hydradb/sdk`
|
|
120
|
+
at the pinned 2.1.2 has no `byog` resource, so the endpoints are unreachable
|
|
121
|
+
through it. It sits behind the same wrapper surface, unwraps the same envelope
|
|
122
|
+
by shape and raises the same `HydraWrapperError`, so callers cannot tell which
|
|
123
|
+
methods go through the SDK. When the SDK grows a `byog` resource, that one file
|
|
124
|
+
is replaced and nothing above it changes. The exact SDK pin is unaffected —
|
|
125
|
+
there is no generated name to be insulated from yet.
|
|
126
|
+
- `responseError()` added to the wrapper's error module so failures from the
|
|
127
|
+
non-SDK path are formatted through the same code as SDK failures, keeping the
|
|
128
|
+
error code, the server's message and the request id.
|
|
129
|
+
## [1.2.1] - 2026-08-17
|
|
130
|
+
|
|
131
|
+
### Fixed
|
|
132
|
+
|
|
133
|
+
Two parameters added in 1.2.0 could not succeed under any input. Both failed as a
|
|
134
|
+
remote 400 on a call the caller had already committed to, so the whole tool call
|
|
135
|
+
was lost to a value that had a working form.
|
|
136
|
+
|
|
137
|
+
- **`operator` on `hydradb_query` failed on every call that set it.** Hydra DB
|
|
138
|
+
honours an operator only when the request also asks for keyword retrieval, and
|
|
139
|
+
this server never sent `query_by` — so the parameter answered
|
|
140
|
+
`400 INVALID_INPUT: operator is only valid with query_by=text` whatever it was
|
|
141
|
+
set to. An operator now carries the retrieval method it requires, and the one
|
|
142
|
+
self-contradicting combination (an operator with explicitly hybrid retrieval)
|
|
143
|
+
is refused here rather than sent out to fail. The cost of that pairing is now
|
|
144
|
+
in the parameter description instead of being discoverable by 400: setting an
|
|
145
|
+
operator turns off the hybrid semantic search this tool otherwise runs, so the
|
|
146
|
+
query matches literal words rather than the concept. The description also stops
|
|
147
|
+
calling `or` the default, which invited callers to pass it redundantly and lose
|
|
148
|
+
semantic matching for nothing. The hybrid-only `alpha` weighting is no longer
|
|
149
|
+
sent on those queries, where there are no two retrieval lanes to weigh.
|
|
150
|
+
- **`observation_date` on `hydradb_ingest` documented a format the API
|
|
151
|
+
rejects.** The description asked for an RFC3339 date, which is a date-*time*: a
|
|
152
|
+
model that followed it sent `2026-08-17T00:00:00Z` and lost the entire ingest to
|
|
153
|
+
`400 INVALID_INPUT: … is not a valid ISO-8601 date (want YYYY-MM-DD)`, on a
|
|
154
|
+
value that would have worked as `2026-08-17`. The description and the README now
|
|
155
|
+
state `YYYY-MM-DD` with a worked example, and the schema carries the pattern so
|
|
156
|
+
the constraint reaches the model choosing the value. A date-time is the form a
|
|
157
|
+
model reaches for when writing a date in JSON, so it is accepted and kept as its
|
|
158
|
+
date part — trimmed textually, which keeps the day the caller wrote where
|
|
159
|
+
converting to UTC would move `2026-08-17T23:00:00-08:00` to the 18th and record
|
|
160
|
+
a date nobody named. Anything that is not a date is rejected before the request
|
|
161
|
+
goes out. The deprecated `hydra_db_store` alias held verbatim copies of this
|
|
162
|
+
blurb and the `metadata` one; both now reference the shared text, which is how
|
|
163
|
+
the fix stays fixed.
|
|
164
|
+
|
|
8
165
|
## [1.2.0] - 2026-08-14
|
|
9
166
|
|
|
10
167
|
### ⚠️ Migration required if you call the old tool names
|
|
@@ -162,6 +319,7 @@ so neither the caller nor the user learned anything had gone wrong.
|
|
|
162
319
|
- Canonical HydraDB tool vocabulary (`hydradb_*`), with the previous `hydra_db_*`
|
|
163
320
|
names kept as deprecated aliases.
|
|
164
321
|
|
|
322
|
+
[1.2.1]: https://github.com/hydra-db/hydradb-mcp/releases/tag/v1.2.1
|
|
165
323
|
[1.2.0]: https://github.com/hydra-db/hydradb-mcp/releases/tag/v1.2.0
|
|
166
324
|
[1.1.1]: https://github.com/hydra-db/hydradb-mcp/releases/tag/v1.1.1
|
|
167
325
|
[1.1.0]: https://github.com/hydra-db/hydradb-mcp/releases/tag/v1.1.0
|
package/README.md
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
MCP (Model Context Protocol) server for [Hydra DB](https://hydradb.com), the state-of-the-art agentic memory. Provides tools for storing, recalling, and managing memories with knowledge-graph enriched context.
|
|
4
4
|
|
|
5
|
+
Run it two ways, same tools either way:
|
|
6
|
+
|
|
7
|
+
- **Local (stdio)** — the `npx @hydradb/mcp` binary each client spawns. No server to operate; credentials live in the client's config. This is the default and everything below the [Configuration](#configuration) section documents it.
|
|
8
|
+
- **Remote (HTTP)** — one hosted process behind a URL like `https://mcp.hydradb.com` that many clients point at, with nothing to install. See [**Remote / hosted server**](#remote--hosted-server).
|
|
9
|
+
|
|
5
10
|
## Available Tools
|
|
6
11
|
|
|
7
12
|
| Tool | What it does |
|
|
@@ -16,6 +21,44 @@ MCP (Model Context Protocol) server for [Hydra DB](https://hydradb.com), the sta
|
|
|
16
21
|
Ids flow between these: `hydradb_query` and `hydradb_list` emit them;
|
|
17
22
|
`hydradb_inspect`, `hydradb_delete` and `hydradb_status` accept them.
|
|
18
23
|
|
|
24
|
+
### Graph tools (Cypher)
|
|
25
|
+
|
|
26
|
+
Hydra DB also runs **property graphs you model and own end to end**, queried in [Cypher](https://docs.hydradb.com/essentials/v2/graph-collections-byog). This is a different product surface from the memory and knowledge above, and nothing crosses between them: `hydradb_query` cannot see graph data, and `hydradb_graph_query` cannot see memories.
|
|
27
|
+
|
|
28
|
+
| Tool | What it does |
|
|
29
|
+
|---|---|
|
|
30
|
+
| `hydradb_graph_query` | Run Cypher — reads **and** writes |
|
|
31
|
+
| `hydradb_graph_collections` | List the graphs in a graph database |
|
|
32
|
+
| `hydradb_graph_admin` | Create a graph database; drop a collection or a database |
|
|
33
|
+
|
|
34
|
+
`hydradb_graph_query` is annotated `destructiveHint`, because it runs arbitrary Cypher and `DELETE` is as reachable through it as `MATCH`. There is deliberately **no** separate read-only Cypher tool and **no read-only mode**: both would mean classifying Cypher text client-side to decide what to refuse, which is a heuristic — a promise the server can keep and a client cannot. This server does not inspect your query at all; it sends it and reports what HydraDB says. To lock the graph surface down, withhold the tools (below) — that is a real guarantee.
|
|
35
|
+
|
|
36
|
+
```jsonc
|
|
37
|
+
// Everyone Alice knows within four hops
|
|
38
|
+
{"query": "MATCH (a:Person {name:$n})-[:KNOWS*1..4]->(r) RETURN DISTINCT r.name AS name",
|
|
39
|
+
"params": {"n": "Alice"}}
|
|
40
|
+
|
|
41
|
+
// Bulk load, re-runnable after a failure
|
|
42
|
+
{"query": "UNWIND $rows AS row MERGE (p:Person {ext_id: row.ext_id}) SET p += row",
|
|
43
|
+
"params": {"rows": [{"ext_id": "a", "name": "Alice"}]}}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Differences from Neo4j** worth knowing before you write Cypher. Each is rejected *before* execution, so a rejected query changes nothing and fails identically on retry:
|
|
47
|
+
|
|
48
|
+
- Procedure calls (`CALL db.*`, `CALL apoc.*`) are rejected **by the server**, before it executes anything. `CALL { ... }` subqueries are fine. There is no schema tool and no `apoc.meta.schema()`; to learn a collection's structure, query it — `MATCH (n) UNWIND labels(n) AS l RETURN l, count(*) AS c ORDER BY l`.
|
|
49
|
+
- `LOAD CSV` is rejected — pass data through `params` instead.
|
|
50
|
+
- Existence checks are bare pattern predicates (`WHERE (p)-[:KNOWS]->()`); `EXISTS { ... }` and `exists()` are not accepted.
|
|
51
|
+
- `shortestPath` belongs in `RETURN`/`WITH`, not `MATCH p = ...`, and must be directed.
|
|
52
|
+
- `EXPLAIN`/`PROFILE` **execute** the query rather than planning it — do not use them to preview one.
|
|
53
|
+
|
|
54
|
+
Collections auto-create on first write, so there is no create-collection call. Requests are capped at 256 KiB (enforced locally, before upload) and large result sets are truncated server-side — paginate with `ORDER BY ... SKIP $offset LIMIT $limit`.
|
|
55
|
+
|
|
56
|
+
To turn the graph tools off entirely:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
HYDRADB_MCP_GRAPH_TOOLS=0 # withhold all three
|
|
60
|
+
```
|
|
61
|
+
|
|
19
62
|
### Deprecated aliases
|
|
20
63
|
|
|
21
64
|
The previous `hydra_db_*` tool names are **no longer registered by default** as
|
|
@@ -46,7 +89,7 @@ chunks with their source id, a relevance score, and knowledge-graph context.
|
|
|
46
89
|
| `mode` | string | No | `fast`, `thinking` (default), or `auto` |
|
|
47
90
|
| `detail` | string | No | `compact` (default) trims each chunk; `full` returns them whole |
|
|
48
91
|
| `graph_context` | boolean | No | Include knowledge-graph relations (default: true) |
|
|
49
|
-
| `operator` | string | No | `or
|
|
92
|
+
| `operator` | string | No | `or`, `and`, or `phrase`. Switches the query to keyword retrieval (`query_by=text`), which is the only mode Hydra DB accepts an operator on — semantic matching is off for that query. Unset (the default) is hybrid semantic search |
|
|
50
93
|
| `source_ids` | array | No | Restrict the search to these sources |
|
|
51
94
|
| `metadata_filters` | object | No | Exact-match filters over stored metadata |
|
|
52
95
|
| `num_related_chunks` | number | No | Adjacent chunks to attach per match (0-5, default: 0) |
|
|
@@ -67,7 +110,7 @@ or `turns`.
|
|
|
67
110
|
| `infer` | boolean | No | Extract insights and graph entities (default: true) |
|
|
68
111
|
| `is_markdown` | boolean | No | Chunk on markdown structure (default: false) |
|
|
69
112
|
| `metadata` | object | No | Key/value metadata, matchable later via `metadata_filters` |
|
|
70
|
-
| `observation_date` | string | No | When the fact was true (
|
|
113
|
+
| `observation_date` | string | No | When the fact was true, as `YYYY-MM-DD` (e.g. `2026-07-04`), vs when it was stored |
|
|
71
114
|
| `user_name` | string | No | What to call the user, used with `turns` (default: `User`) |
|
|
72
115
|
|
|
73
116
|
\* Passing both is an error; passing neither is an error.
|
|
@@ -145,6 +188,14 @@ Checks whether ingested sources have finished indexing.
|
|
|
145
188
|
| `HYDRADB_TIMEOUT_SECONDS` | Per-attempt request timeout | `30` |
|
|
146
189
|
| `HYDRADB_MAX_RETRIES` | Retries per request (0 disables) | `2` |
|
|
147
190
|
| `HYDRADB_MCP_LEGACY_TOOLS` | Register the deprecated `hydra_db_*` tools | *off* |
|
|
191
|
+
| `HYDRADB_GRAPH_DATABASE` | Default graph database for the Cypher tools | `HYDRADB_DATABASE` |
|
|
192
|
+
| `HYDRADB_GRAPH_COLLECTION` | Default graph collection | `default` |
|
|
193
|
+
| `HYDRADB_MCP_GRAPH_TOOLS` | Register the graph tools (`0` withholds them) | *on* |
|
|
194
|
+
|
|
195
|
+
A graph database is a **different namespace** from the memory database: the same
|
|
196
|
+
name can exist as both, and Cypher aimed at the wrong one reads an empty graph
|
|
197
|
+
rather than failing. Every graph tool also takes `database` and `collection`
|
|
198
|
+
per call, overriding these defaults.
|
|
148
199
|
|
|
149
200
|
The legacy `HYDRA_DB_*` names — `HYDRA_DB_API_KEY`, `HYDRA_DB_TENANT_ID`,
|
|
150
201
|
`HYDRA_DB_SUB_TENANT_ID`, `HYDRA_DB_BASE_URL`, `HYDRA_DB_LOG_LEVEL` — remain
|
|
@@ -230,6 +281,118 @@ To partition data, set the `HYDRADB_COLLECTION` environment variable:
|
|
|
230
281
|
}
|
|
231
282
|
```
|
|
232
283
|
|
|
284
|
+
## Remote / hosted server
|
|
285
|
+
|
|
286
|
+
Everything above spawns the server locally over stdio. The same server also runs
|
|
287
|
+
as a long-lived **HTTP endpoint** that many clients reach at one URL — nothing to
|
|
288
|
+
install or update per user. This is what powers a hosted deployment like
|
|
289
|
+
`https://mcp.hydradb.com`, and what you run yourself with `npm run start:http`
|
|
290
|
+
or the Docker image.
|
|
291
|
+
|
|
292
|
+
The tool surface is identical; only how a client connects and authenticates
|
|
293
|
+
changes.
|
|
294
|
+
|
|
295
|
+
### Point a client at a URL
|
|
296
|
+
|
|
297
|
+
MCP clients that support a remote (`streamable-http`) server take a URL and
|
|
298
|
+
headers instead of a command:
|
|
299
|
+
|
|
300
|
+
```jsonc
|
|
301
|
+
{
|
|
302
|
+
"mcpServers": {
|
|
303
|
+
"hydradb": {
|
|
304
|
+
"url": "https://mcp.hydradb.com",
|
|
305
|
+
"headers": {
|
|
306
|
+
"Authorization": "Bearer YOUR_HYDRADB_API_KEY",
|
|
307
|
+
"X-HydraDB-Database": "your-database"
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
Every request carries its own credentials, so one hosted process serves any
|
|
315
|
+
number of independent users. The headers a request may send:
|
|
316
|
+
|
|
317
|
+
| Header | Maps to | Required |
|
|
318
|
+
| --- | --- | --- |
|
|
319
|
+
| `Authorization: Bearer <key>` | Hydra DB API key (`X-HydraDB-Api-Key` also accepted) | Yes\* |
|
|
320
|
+
| `X-HydraDB-Database` | Default database (tenant scope) | Yes\* |
|
|
321
|
+
| `X-HydraDB-Collection` | Default collection (sub-tenant); defaults to `hydra-db-mcp` | No |
|
|
322
|
+
| `X-HydraDB-Graph-Database` | Default graph database for the Cypher tools; defaults to the request's database | No |
|
|
323
|
+
| `X-HydraDB-Graph-Collection` | Default graph collection; defaults to `default` | No |
|
|
324
|
+
|
|
325
|
+
\* Unless the server was started with `HYDRADB_API_KEY` / `HYDRADB_DATABASE` in
|
|
326
|
+
its environment (single-tenant self-host, below), in which case a request may
|
|
327
|
+
omit them and fall back to the server's own credentials. A request that supplies
|
|
328
|
+
neither a header nor a server-side default is refused: `401` with no key, `400`
|
|
329
|
+
with a key but no database.
|
|
330
|
+
|
|
331
|
+
Every tool additionally accepts optional `database` and `collection` parameters
|
|
332
|
+
directly in its arguments (e.g. `hydradb_query` with `{"query": "...", "database": "tenant_b"}`),
|
|
333
|
+
allowing multi-tenant agents to switch tenant scope per tool call while falling
|
|
334
|
+
back to the session defaults when omitted.
|
|
335
|
+
|
|
336
|
+
`Base URL`, request timeout and retry count are
|
|
337
|
+
**operator** settings read from the server's environment and are never taken
|
|
338
|
+
from a request header.
|
|
339
|
+
|
|
340
|
+
### Run the HTTP server
|
|
341
|
+
|
|
342
|
+
**Node:**
|
|
343
|
+
|
|
344
|
+
```bash
|
|
345
|
+
npm ci && npm run build
|
|
346
|
+
# Single-tenant: the server holds one account; clients send no credentials.
|
|
347
|
+
HYDRADB_API_KEY=your-key HYDRADB_DATABASE=your-database npm run start:http
|
|
348
|
+
# Multi-tenant: no account in the env; every client sends its own headers.
|
|
349
|
+
BIND_ADDRESS=0.0.0.0 ALLOWED_HOSTS=mcp.hydradb.com npm run start:http
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
**Docker:**
|
|
353
|
+
|
|
354
|
+
```bash
|
|
355
|
+
docker build -t hydradb-mcp .
|
|
356
|
+
# Single-tenant
|
|
357
|
+
docker run -p 8080:8080 -e HYDRADB_API_KEY=your-key -e HYDRADB_DATABASE=your-database hydradb-mcp
|
|
358
|
+
# Multi-tenant (clients authenticate per request)
|
|
359
|
+
docker run -p 8080:8080 -e ALLOWED_HOSTS=mcp.hydradb.com hydradb-mcp
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
The primary MCP endpoint is `/` (with `/mcp` supported as an alias); `GET /health` is an unauthenticated liveness probe.
|
|
363
|
+
The image binds `0.0.0.0` inside the container (the host controls exposure with
|
|
364
|
+
`-p`) and runs as an unprivileged user.
|
|
365
|
+
|
|
366
|
+
### Server environment variables
|
|
367
|
+
|
|
368
|
+
These configure the HTTP process itself (the stdio server ignores them). All
|
|
369
|
+
the `HYDRADB_*` variables from [Environment Variables](#environment-variables)
|
|
370
|
+
also apply — as the single-tenant default and as operator settings.
|
|
371
|
+
|
|
372
|
+
| Variable | Description | Default |
|
|
373
|
+
| --- | --- | --- |
|
|
374
|
+
| `PORT` | Port to listen on | `8080` |
|
|
375
|
+
| `BIND_ADDRESS` | Interface to bind (`0.0.0.0` to accept off-host) | `127.0.0.1` |
|
|
376
|
+
| `ALLOWED_HOSTS` | Extra `Host` headers to accept (comma-separated); loopback always allowed | *(loopback only)* |
|
|
377
|
+
| `ALLOWED_ORIGINS` | CORS origins for browser clients (comma-separated; `*` allows any) | *(none)* |
|
|
378
|
+
| `TRUST_PROXY` | Express `trust proxy` when behind a reverse proxy: a hop count, `true`, or a subnet preset (`loopback`) | *off* |
|
|
379
|
+
|
|
380
|
+
### Security
|
|
381
|
+
|
|
382
|
+
The defaults are safe for local use and must be widened deliberately for a
|
|
383
|
+
public deployment — see [SECURITY.md](SECURITY.md):
|
|
384
|
+
|
|
385
|
+
- **Bind loopback by default.** `BIND_ADDRESS` stays `127.0.0.1` until you set
|
|
386
|
+
otherwise; `0.0.0.0` exposes the server on every interface and logs a warning.
|
|
387
|
+
- **Host allowlist.** Requests whose `Host` is not loopback or in `ALLOWED_HOSTS`
|
|
388
|
+
get `421 Misdirected Request` — a DNS-rebinding defence. Add your public
|
|
389
|
+
hostname when binding publicly.
|
|
390
|
+
- **CORS is closed by default.** No cross-origin browser request is accepted
|
|
391
|
+
until you list its origin in `ALLOWED_ORIGINS`. Non-browser clients (no
|
|
392
|
+
`Origin` header) are unaffected.
|
|
393
|
+
- **Terminate TLS in front.** Run the server behind a reverse proxy / load
|
|
394
|
+
balancer that handles HTTPS; do not expose plain HTTP to the internet.
|
|
395
|
+
|
|
233
396
|
## How It Works
|
|
234
397
|
|
|
235
398
|
The server talks to Hydra DB through the generated [`@hydradb/sdk`](https://www.npmjs.com/package/@hydradb/sdk)
|
|
@@ -257,6 +420,13 @@ For development with auto-reload:
|
|
|
257
420
|
HYDRADB_API_KEY=your-key HYDRADB_DATABASE=your-database npm run dev
|
|
258
421
|
```
|
|
259
422
|
|
|
423
|
+
To run the HTTP transport locally (see [Remote / hosted server](#remote--hosted-server)):
|
|
424
|
+
|
|
425
|
+
```bash
|
|
426
|
+
HYDRADB_API_KEY=your-key HYDRADB_DATABASE=your-database npm run dev:http
|
|
427
|
+
# then: curl localhost:8080/health
|
|
428
|
+
```
|
|
429
|
+
|
|
260
430
|
## Testing
|
|
261
431
|
|
|
262
432
|
```bash
|
package/dist/config.d.ts
CHANGED
|
@@ -14,9 +14,31 @@ export interface HydraDBConfig {
|
|
|
14
14
|
baseUrl?: string;
|
|
15
15
|
timeoutSeconds?: number;
|
|
16
16
|
maxRetries?: number;
|
|
17
|
+
graph: GraphConfig;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Scope and gating for the BYOG graph tools.
|
|
21
|
+
*
|
|
22
|
+
* A graph database is a DIFFERENT namespace from the memory database: the same
|
|
23
|
+
* name can exist as both, and a Cypher query aimed at the wrong one silently
|
|
24
|
+
* reads an empty graph rather than failing. So the graph scope is configured
|
|
25
|
+
* separately, and every graph tool also takes a per-call override.
|
|
26
|
+
*/
|
|
27
|
+
export interface GraphConfig {
|
|
28
|
+
/** Whether the graph tools are registered at all. */
|
|
29
|
+
enabled: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Default graph database. Falls back to HYDRADB_DATABASE — convenient when
|
|
32
|
+
* one name is used for both, and harmless otherwise because the tools accept
|
|
33
|
+
* `database` per call.
|
|
34
|
+
*/
|
|
35
|
+
database: string;
|
|
36
|
+
/** Default graph collection. */
|
|
37
|
+
collection: string;
|
|
17
38
|
}
|
|
18
39
|
export type EnvSource = Record<string, string | undefined>;
|
|
19
40
|
export type WarnFn = (message: string) => void;
|
|
41
|
+
export declare const DEFAULT_COLLECTION = "hydra-db-mcp";
|
|
20
42
|
/**
|
|
21
43
|
* Read a canonical `HYDRADB_*` variable, falling back to the legacy `HYDRA_DB_*`
|
|
22
44
|
* spelling with a once-per-process warning. Exported so every variable this
|
|
@@ -26,3 +48,21 @@ export type WarnFn = (message: string) => void;
|
|
|
26
48
|
*/
|
|
27
49
|
export declare function readEnv(env: EnvSource, canonical: string, deprecated: string, warn?: WarnFn): string | undefined;
|
|
28
50
|
export declare function resolveConfig(env?: EnvSource, warn?: WarnFn): HydraDBConfig;
|
|
51
|
+
/**
|
|
52
|
+
* Graph scope and gating, resolved on its own.
|
|
53
|
+
*
|
|
54
|
+
* Separate from `resolveConfig` because it must be resolvable WITHOUT an API key
|
|
55
|
+
* or a memory database: tests construct a server around an injected client and
|
|
56
|
+
* never set those, and making the graph tools depend on them would mean the
|
|
57
|
+
* graph surface could not be exercised without a full credential set.
|
|
58
|
+
*/
|
|
59
|
+
export declare function resolveGraphConfig(env?: EnvSource, fallbackDatabase?: string): GraphConfig;
|
|
60
|
+
/**
|
|
61
|
+
* Numeric overrides are ignored rather than fatal when malformed.
|
|
62
|
+
*
|
|
63
|
+
* A typo'd timeout should not stop the server from starting — falling back to
|
|
64
|
+
* the built-in default keeps it running, and the alternative (exit 1 on a
|
|
65
|
+
* cosmetic env var) is worse than the misconfiguration.
|
|
66
|
+
*/
|
|
67
|
+
export declare function positiveInt(raw: string | undefined): number | undefined;
|
|
68
|
+
export declare function nonNegativeInt(raw: string | undefined): number | undefined;
|
package/dist/config.js
CHANGED
|
@@ -7,7 +7,26 @@
|
|
|
7
7
|
* deprecated alias emits exactly one stderr warning per process naming the
|
|
8
8
|
* canonical replacement.
|
|
9
9
|
*/
|
|
10
|
-
const
|
|
10
|
+
const DEFAULT_GRAPH_COLLECTION = "default";
|
|
11
|
+
/**
|
|
12
|
+
* Env flags accept the usual spellings, not just `1`.
|
|
13
|
+
*
|
|
14
|
+
* Someone writing `HYDRADB_MCP_GRAPH_TOOLS=false` means it, and silently
|
|
15
|
+
* ignoring that would register tools an operator believed they had withheld.
|
|
16
|
+
* An unrecognised value falls back rather than throwing, so a typo cannot stop
|
|
17
|
+
* the server starting.
|
|
18
|
+
*/
|
|
19
|
+
function flag(raw, fallback) {
|
|
20
|
+
if (raw == null || raw.trim() === "")
|
|
21
|
+
return fallback;
|
|
22
|
+
const value = raw.trim().toLowerCase();
|
|
23
|
+
if (["1", "true", "yes", "on"].includes(value))
|
|
24
|
+
return true;
|
|
25
|
+
if (["0", "false", "no", "off"].includes(value))
|
|
26
|
+
return false;
|
|
27
|
+
return fallback;
|
|
28
|
+
}
|
|
29
|
+
export const DEFAULT_COLLECTION = "hydra-db-mcp";
|
|
11
30
|
// Process-lifetime dedupe so each deprecated alias warns at most once.
|
|
12
31
|
const warnedOnce = new Set();
|
|
13
32
|
function defaultWarn(message) {
|
|
@@ -61,6 +80,24 @@ export function resolveConfig(env = process.env, warn = defaultWarn) {
|
|
|
61
80
|
baseUrl,
|
|
62
81
|
...(timeoutSeconds != null ? { timeoutSeconds } : {}),
|
|
63
82
|
...(maxRetries != null ? { maxRetries } : {}),
|
|
83
|
+
graph: resolveGraphConfig(env, database),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Graph scope and gating, resolved on its own.
|
|
88
|
+
*
|
|
89
|
+
* Separate from `resolveConfig` because it must be resolvable WITHOUT an API key
|
|
90
|
+
* or a memory database: tests construct a server around an injected client and
|
|
91
|
+
* never set those, and making the graph tools depend on them would mean the
|
|
92
|
+
* graph surface could not be exercised without a full credential set.
|
|
93
|
+
*/
|
|
94
|
+
export function resolveGraphConfig(env = process.env, fallbackDatabase = "") {
|
|
95
|
+
return {
|
|
96
|
+
enabled: flag(env.HYDRADB_MCP_GRAPH_TOOLS, true),
|
|
97
|
+
// Not `readEnv` — these are new names with no legacy spelling to alias,
|
|
98
|
+
// and inventing a deprecated one would be noise.
|
|
99
|
+
database: env.HYDRADB_GRAPH_DATABASE?.trim() || fallbackDatabase,
|
|
100
|
+
collection: env.HYDRADB_GRAPH_COLLECTION?.trim() || DEFAULT_GRAPH_COLLECTION,
|
|
64
101
|
};
|
|
65
102
|
}
|
|
66
103
|
/**
|
|
@@ -70,11 +107,11 @@ export function resolveConfig(env = process.env, warn = defaultWarn) {
|
|
|
70
107
|
* the built-in default keeps it running, and the alternative (exit 1 on a
|
|
71
108
|
* cosmetic env var) is worse than the misconfiguration.
|
|
72
109
|
*/
|
|
73
|
-
function positiveInt(raw) {
|
|
110
|
+
export function positiveInt(raw) {
|
|
74
111
|
const value = Number(raw);
|
|
75
112
|
return raw != null && Number.isInteger(value) && value > 0 ? value : undefined;
|
|
76
113
|
}
|
|
77
|
-
function nonNegativeInt(raw) {
|
|
114
|
+
export function nonNegativeInt(raw) {
|
|
78
115
|
const value = Number(raw);
|
|
79
116
|
return raw != null && Number.isInteger(value) && value >= 0 ? value : undefined;
|
|
80
117
|
}
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/cypher.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rendering and local limits for the BYOG (Bring Your Own Graph) tool.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately contains NO Cypher analysis. An earlier version lexed the query
|
|
5
|
+
* to classify reads vs writes and to pre-reject constructs the server refuses,
|
|
6
|
+
* which meant a second, worse implementation of the server's own rules living
|
|
7
|
+
* in a client: it could only ever agree with the server or be wrong, and being
|
|
8
|
+
* wrong meant refusing a query HydraDB would have run.
|
|
9
|
+
*
|
|
10
|
+
* The server is the authority on what Cypher is valid and permitted. It rejects
|
|
11
|
+
* unsupported constructs before executing anything — verified: a query mixing
|
|
12
|
+
* CREATE with a procedure call leaves the node count unchanged — and its
|
|
13
|
+
* messages are more specific than the ones this file used to produce.
|
|
14
|
+
*
|
|
15
|
+
* What is left is the work a client genuinely owns: turning the server's row
|
|
16
|
+
* objects into something readable, and the two limits that are cheaper to check
|
|
17
|
+
* here than to discover from a remote error.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* The documented request-body ceiling for `POST /byog/query`.
|
|
21
|
+
*
|
|
22
|
+
* Enforced before the request goes out. The server answers an oversize body
|
|
23
|
+
* with 413 — but only after the whole thing has been uploaded, which on a bulk
|
|
24
|
+
* load is the slowest possible way to learn the batch was too big.
|
|
25
|
+
*/
|
|
26
|
+
export declare const MAX_BODY_BYTES: number;
|
|
27
|
+
/** Collection names the server accepts. Rejecting locally names the rule. */
|
|
28
|
+
export declare const COLLECTION_PATTERN: RegExp;
|
|
29
|
+
type Row = Record<string, unknown>;
|
|
30
|
+
/**
|
|
31
|
+
* One returned value, rendered the way a graph user reads it.
|
|
32
|
+
*
|
|
33
|
+
* A node comes back as a flat object mixing its properties with `id` and
|
|
34
|
+
* `labels`; dumping that as raw JSON makes the caller do the separating. This
|
|
35
|
+
* renders `(:Person {name: "Alice"})` instead, which is both shorter and the
|
|
36
|
+
* notation the query was written in.
|
|
37
|
+
*/
|
|
38
|
+
export declare function renderValue(value: unknown): string;
|
|
39
|
+
/**
|
|
40
|
+
* Rows as a table, bounded.
|
|
41
|
+
*
|
|
42
|
+
* A traversal can return far more than the caller can use, and unlike the
|
|
43
|
+
* memory tools there is no server-side relevance ranking to lean on — the query
|
|
44
|
+
* asked for exactly this. So the ceiling is on the rendering, and what was
|
|
45
|
+
* dropped is stated rather than silently cut.
|
|
46
|
+
*/
|
|
47
|
+
export declare function renderRows(rows: Row[], opts?: {
|
|
48
|
+
maxRows?: number;
|
|
49
|
+
maxChars?: number;
|
|
50
|
+
}): string;
|
|
51
|
+
export {};
|