@hydradb/mcp 1.2.1 → 1.3.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/dist/http.js ADDED
@@ -0,0 +1,427 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * The remotely hostable HTTP transport.
4
+ *
5
+ * This is the server behind a URL like `https://mcp.hydradb.com/mcp`: instead of
6
+ * every user installing and spawning the stdio binary ({@link file://./index.ts}),
7
+ * one process answers MCP over HTTP and each user points their client at the URL.
8
+ *
9
+ * It reuses the whole tool surface unchanged — {@link createHydraDBServer} builds
10
+ * exactly the same server the stdio path does. The only things this file adds are
11
+ * the ones a network endpoint needs and a pipe does not: a Host/Origin
12
+ * allowlist, CORS, and PER-REQUEST tenant credentials (see
13
+ * {@link file://./http-config.ts}), because one hosted process has no single
14
+ * ambient account to run as.
15
+ *
16
+ * Sessions are stateless: MCP's Protocol object binds to one transport, so a
17
+ * shared process serving many independent callers builds a fresh server +
18
+ * transport per request and tears it down when the response closes. That is the
19
+ * transport's documented stateless mode (`sessionIdGenerator: undefined`).
20
+ */
21
+ import cors from "cors";
22
+ import express from "express";
23
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
24
+ import { HydraDB } from "./hydra/index.js";
25
+ import { buildAllowedHosts, jsonRpcError, parseList, parsePort, resolveHttpServerConfig, resolveRequestCredentials, } from "./http-config.js";
26
+ import { logger } from "./logger.js";
27
+ import { introspect, isAccessToken, metadataUrl, protectedResourceMetadata, wwwAuthenticate, } from "./oauth.js";
28
+ import { awaitInFlight, beginShutdown, createHydraDBServer, inFlightCount, } from "./server.js";
29
+ /**
30
+ * The largest request body accepted before parsing.
31
+ *
32
+ * Sized above the tool layer's own ceilings — memory ingest caps `text` at 1M
33
+ * characters (~4 MB as UTF-8 with a JSON envelope), which is the biggest
34
+ * legitimate body — so a valid large ingest is not rejected at the door while an
35
+ * unbounded body cannot exhaust memory. Anything genuinely oversized is still
36
+ * refused by the per-tool checks with a message naming the real limit.
37
+ */
38
+ const MAX_REQUEST_BODY = "8mb";
39
+ /**
40
+ * Startup banners and security warnings go straight to stderr, not through
41
+ * `logger`.
42
+ *
43
+ * `logger` is gated by `HYDRADB_LOG_LEVEL`, which defaults to ERROR, so a
44
+ * `logger.warn` about a public bind would be invisible in the exact default
45
+ * configuration where it matters most. These lines must surface regardless of
46
+ * level — the same reason the deprecated-alias warnings bypass the logger — so
47
+ * they use `console.error` (stderr) directly. Per-request and lifecycle logging
48
+ * still goes through `logger`.
49
+ */
50
+ function banner(message) {
51
+ console.error(`[hydradb-mcp] ${message}`);
52
+ }
53
+ /** The bearer value from an `Authorization` header, scheme-insensitive. */
54
+ function bearerFromHeader(value) {
55
+ const raw = Array.isArray(value) ? value[0] : value;
56
+ if (!raw)
57
+ return undefined;
58
+ const match = /^\s*Bearer\s+(.+)$/i.exec(raw);
59
+ return (match ? match[1] : raw).trim() || undefined;
60
+ }
61
+ /** JSON-RPC error codes used for transport-level failures (spec: -32000 range). */
62
+ const JSONRPC_UNAUTHORIZED = -32001;
63
+ const JSONRPC_BAD_REQUEST = -32602;
64
+ const JSONRPC_INTERNAL_ERROR = -32603;
65
+ const JSONRPC_MISDIRECTED = -32000;
66
+ /**
67
+ * Build the Express app for the HTTP transport.
68
+ *
69
+ * Exported (and taking its config as an argument rather than reading the
70
+ * environment) so tests exercise the exact wiring production runs, against an
71
+ * arbitrary allowlist, with no process-global state.
72
+ */
73
+ export function createHttpApp(config) {
74
+ const { bindAddress, allowedOrigins, allowedHosts } = config;
75
+ const allowsAllOrigins = allowedOrigins.includes("*");
76
+ // OAuth is a per-deployment capability, not a per-request one, so it is
77
+ // resolved once. `null` means every OAuth surface below is absent and this
78
+ // server is byte-for-byte the pre-OAuth one.
79
+ const oauth = config.oauth ?? null;
80
+ const app = express();
81
+ // Off by default so a direct client cannot spoof `X-Forwarded-*`; a
82
+ // deployment behind a known proxy sets TRUST_PROXY to recover the real client
83
+ // IP without trusting every hop. Only logging and `req.protocol` read it — the
84
+ // Host allowlist, not a proxy header, is the DNS-rebinding defence.
85
+ app.set("trust proxy", config.trustProxy);
86
+ // The Host allowlist is the actual defence; the fingerprinting header is noise.
87
+ app.disable("x-powered-by");
88
+ // --- Host header allowlist (runs first, before CORS) ---
89
+ //
90
+ // A DNS-rebinding defence: without it, a page the user visits can point a
91
+ // hostname it controls at 127.0.0.1 and drive a locally bound server. The
92
+ // check is the operator's ALLOWED_HOSTS plus loopback; a hosted deployment
93
+ // adds its public hostname. 421 Misdirected Request is the status for "this
94
+ // server does not answer to that authority".
95
+ app.use((req, res, next) => {
96
+ const host = req.headers.host;
97
+ if (!host || !allowedHosts.has(host.toLowerCase())) {
98
+ logger.warn("rejected request with disallowed Host header", {
99
+ host,
100
+ path: req.path,
101
+ });
102
+ res
103
+ .status(421)
104
+ .json(jsonRpcError(JSONRPC_MISDIRECTED, "Misdirected request"));
105
+ return;
106
+ }
107
+ next();
108
+ });
109
+ // --- CORS ---
110
+ //
111
+ // A distinct error type so the error handler below can tell an origin
112
+ // rejection apart from any other downstream failure and answer it with 403.
113
+ class CorsOriginNotAllowedError extends Error {
114
+ constructor(origin) {
115
+ super(`Origin ${origin} not allowed by CORS`);
116
+ this.origin = origin;
117
+ this.name = "CorsOriginNotAllowedError";
118
+ }
119
+ }
120
+ app.use(cors({
121
+ origin: (origin, callback) => {
122
+ // No Origin header: a non-browser client or a same-origin request.
123
+ // These are not subject to CORS and are allowed through.
124
+ if (!origin)
125
+ return callback(null, true);
126
+ // The opaque `null` origin (sandboxed iframe, file://) is only
127
+ // honoured when the operator lists it explicitly.
128
+ if (origin === "null") {
129
+ return allowedOrigins.includes("null")
130
+ ? callback(null, true)
131
+ : callback(new CorsOriginNotAllowedError("null"));
132
+ }
133
+ if (allowsAllOrigins || allowedOrigins.includes(origin)) {
134
+ return callback(null, true);
135
+ }
136
+ return callback(new CorsOriginNotAllowedError(origin));
137
+ },
138
+ // The client reads the session id and negotiated protocol version off
139
+ // the response; without exposing them a browser cannot complete a session.
140
+ // WWW-Authenticate is the OAuth discovery entry point: a browser-based
141
+ // client (claude.ai) that cannot read it off the 401 never learns where
142
+ // to log in, so the whole flow silently fails for exactly the clients
143
+ // OAuth exists for.
144
+ exposedHeaders: ["Mcp-Session-Id", "Mcp-Protocol-Version", "WWW-Authenticate"],
145
+ allowedHeaders: [
146
+ "Content-Type",
147
+ "Authorization",
148
+ "Mcp-Session-Id",
149
+ "Mcp-Protocol-Version",
150
+ "X-HydraDB-Api-Key",
151
+ "X-HydraDB-Database",
152
+ "X-HydraDB-Collection",
153
+ "X-HydraDB-Graph-Database",
154
+ "X-HydraDB-Graph-Collection",
155
+ ],
156
+ }));
157
+ // Turn a CORS origin rejection into an explicit 403 with a JSON-RPC body,
158
+ // mirroring the 421 the Host check emits. Placed right after cors so any
159
+ // other error still reaches Express's default handler unchanged.
160
+ app.use((err, req, res, next) => {
161
+ if (err instanceof CorsOriginNotAllowedError) {
162
+ logger.warn("rejected request with disallowed Origin", {
163
+ origin: err.origin,
164
+ path: req.path,
165
+ });
166
+ res
167
+ .status(403)
168
+ .json(jsonRpcError(JSONRPC_MISDIRECTED, "Origin not allowed"));
169
+ return;
170
+ }
171
+ next(err);
172
+ });
173
+ app.use(express.json({ limit: MAX_REQUEST_BODY }));
174
+ // --- The MCP endpoint ---
175
+ // Serves MCP at both the root `/` (e.g. https://mcp.hydradb.com) and `/mcp`
176
+ // so clients pointing at either URL connect seamlessly.
177
+ // --- OAuth Protected Resource Metadata (RFC 9728) ---
178
+ //
179
+ // The one document a client needs to get from "401" to "open the browser at
180
+ // the right place". Served at the root form and the `/mcp` path form, since
181
+ // a client tries the path-inserted URL first when the endpoint has a path.
182
+ // Registered only when OAuth is configured, so an unconfigured server has
183
+ // no new routes at all.
184
+ if (oauth) {
185
+ app.get(["/.well-known/oauth-protected-resource", "/.well-known/oauth-protected-resource/mcp"], (_req, res) => {
186
+ res.setHeader("Cache-Control", "public, max-age=300");
187
+ res.setHeader("Access-Control-Allow-Origin", "*");
188
+ res.json(protectedResourceMetadata(oauth));
189
+ });
190
+ }
191
+ app.all(["/", "/mcp"], async (req, res) => {
192
+ // An OAuth access token arrives in the same `Authorization: Bearer` slot
193
+ // an API key does. It is told apart by prefix and exchanged, via the
194
+ // issuer, for the API key and scope the user approved on the consent
195
+ // screen. From there it is indistinguishable from a caller who sent that
196
+ // key directly: the same resolver runs, and the tool layer never learns
197
+ // OAuth exists.
198
+ let identity;
199
+ const bearer = bearerFromHeader(req.headers.authorization);
200
+ if (oauth && isAccessToken(bearer)) {
201
+ const result = await introspect(oauth, bearer);
202
+ if (!result.ok) {
203
+ if (result.reason === "unavailable") {
204
+ logger.error("token introspection unavailable", { detail: result.detail });
205
+ res
206
+ .status(503)
207
+ .json(jsonRpcError(JSONRPC_INTERNAL_ERROR, "Authorization service unavailable"));
208
+ return;
209
+ }
210
+ res.setHeader("WWW-Authenticate", wwwAuthenticate(oauth, "invalid_token", result.reason === "wrong_audience"
211
+ ? "token was not issued for this server"
212
+ : "token is invalid, expired or revoked"));
213
+ res
214
+ .status(401)
215
+ .json(jsonRpcError(JSONRPC_UNAUTHORIZED, "Invalid or expired access token"));
216
+ return;
217
+ }
218
+ const t = result.token;
219
+ identity = {
220
+ apiKey: t.apiKey,
221
+ ...(t.database != null ? { database: t.database } : {}),
222
+ ...(t.collection != null ? { collection: t.collection } : {}),
223
+ ...(t.allowedDatabases ? { allowedDatabases: t.allowedDatabases } : {}),
224
+ ...(t.allowedCollections ? { allowedCollections: t.allowedCollections } : {}),
225
+ };
226
+ // A token is a credential; nothing carrying one may be cached.
227
+ res.setHeader("Cache-Control", "no-store");
228
+ }
229
+ // Who is this request for? On a hosted process the answer lives entirely
230
+ // in the request, so it is resolved here and a missing/incomplete answer
231
+ // is refused before any server is built.
232
+ const resolution = resolveRequestCredentials(req.headers, process.env, identity);
233
+ if (!resolution.ok) {
234
+ // 401 gets a WWW-Authenticate header so a spec-compliant client knows
235
+ // how to authenticate rather than just seeing a bare refusal. With
236
+ // OAuth configured that header is the whole discovery entry point.
237
+ if (resolution.status === 401) {
238
+ res.setHeader("WWW-Authenticate", oauth ? wwwAuthenticate(oauth) : 'Bearer realm="Hydra DB MCP"');
239
+ }
240
+ res
241
+ .status(resolution.status)
242
+ .json(jsonRpcError(resolution.status === 401
243
+ ? JSONRPC_UNAUTHORIZED
244
+ : JSONRPC_BAD_REQUEST, resolution.message));
245
+ return;
246
+ }
247
+ const creds = resolution.credentials;
248
+ try {
249
+ const hydra = new HydraDB({
250
+ token: creds.apiKey,
251
+ database: creds.database,
252
+ collection: creds.collection,
253
+ ...(creds.allowedDatabases ? { allowedDatabases: creds.allowedDatabases } : {}),
254
+ ...(creds.allowedCollections
255
+ ? { allowedCollections: creds.allowedCollections }
256
+ : {}),
257
+ ...(creds.baseUrl != null ? { baseUrl: creds.baseUrl } : {}),
258
+ ...(creds.timeoutSeconds != null
259
+ ? { timeoutSeconds: creds.timeoutSeconds }
260
+ : {}),
261
+ ...(creds.maxRetries != null ? { maxRetries: creds.maxRetries } : {}),
262
+ });
263
+ const server = createHydraDBServer(hydra, creds.graph, { oauthTools: identity != null });
264
+ // Stateless: this pair serves exactly this request and is discarded when
265
+ // the response closes. Tearing them down on `close` — which fires for a
266
+ // clean end AND for the error path below (it sends a response, which then
267
+ // closes) — is what frees the per-request state; without it a long-lived
268
+ // process leaks a server per call. It is the single teardown point, so
269
+ // nothing here double-closes. `close()` returns a promise, and a stray
270
+ // rejection would take the whole process down via `unhandledRejection`, so
271
+ // it is explicitly swallowed.
272
+ const transport = new StreamableHTTPServerTransport({
273
+ sessionIdGenerator: undefined,
274
+ enableJsonResponse: true,
275
+ });
276
+ res.on("close", () => {
277
+ transport.close().catch(() => { });
278
+ server.close().catch(() => { });
279
+ });
280
+ await server.connect(transport);
281
+ await transport.handleRequest(req, res, req.body);
282
+ }
283
+ catch (error) {
284
+ logger.error("error handling MCP request", {
285
+ error: error instanceof Error ? error.message : String(error),
286
+ });
287
+ // Do not close here: the `res.on("close")` handler above owns teardown,
288
+ // and the transport may still be mid-write. Only the transport writes the
289
+ // JSON-RPC body, so this responds solely when nothing has been sent yet
290
+ // AND the socket is still open — a client that aborted mid-request lands
291
+ // here too, and writing to its closed socket is pointless. Ending the
292
+ // response then triggers that one teardown.
293
+ if (!res.headersSent && !res.writableEnded) {
294
+ res
295
+ .status(500)
296
+ .json(jsonRpcError(JSONRPC_INTERNAL_ERROR, "Internal server error"));
297
+ }
298
+ }
299
+ });
300
+ // A liveness probe for load balancers and container orchestrators. It says
301
+ // nothing about Hydra DB reachability on purpose — credentials are per
302
+ // request, so there is no single upstream this endpoint could check.
303
+ app.get("/health", (_req, res) => {
304
+ res.json({ status: "ok", service: "hydradb-mcp" });
305
+ });
306
+ // `express.json` throws on a malformed body (`entity.parse.failed`) or one
307
+ // over the limit (`entity.too.large`). Registered after the routes so it
308
+ // catches those, it keeps every refusal on this server speaking JSON-RPC
309
+ // rather than letting Express answer with its default HTML error page. Any
310
+ // other error falls through to Express's default handler unchanged.
311
+ app.use((err, _req, res, next) => {
312
+ if (err.type === "entity.parse.failed") {
313
+ res
314
+ .status(400)
315
+ .json(jsonRpcError(JSONRPC_BAD_REQUEST, "Request body is not valid JSON"));
316
+ return;
317
+ }
318
+ if (err.type === "entity.too.large") {
319
+ res
320
+ .status(413)
321
+ .json(jsonRpcError(JSONRPC_BAD_REQUEST, `Request body exceeds the ${MAX_REQUEST_BODY} limit`));
322
+ return;
323
+ }
324
+ next(err);
325
+ });
326
+ if (bindAddress === "0.0.0.0" || bindAddress === "::") {
327
+ banner(`WARNING: BIND_ADDRESS=${bindAddress} exposes the server on all network interfaces — ` +
328
+ "set ALLOWED_HOSTS/ALLOWED_ORIGINS and put it behind TLS. See SECURITY.md.");
329
+ }
330
+ if (allowsAllOrigins) {
331
+ banner('WARNING: ALLOWED_ORIGINS contains "*" — any website may call this server. See SECURITY.md.');
332
+ }
333
+ return app;
334
+ }
335
+ /**
336
+ * Wire graceful shutdown for the HTTP server.
337
+ *
338
+ * The in-flight bookkeeping is shared with the stdio path (it is module state in
339
+ * {@link file://./server.js}), so this drains accepted tool calls the same way:
340
+ * stop accepting, close the listener, wait for running handlers, then exit. An
341
+ * ingest cut off mid-write leaves the caller unable to tell whether it
342
+ * committed, which under upsert is not answerable by retrying.
343
+ */
344
+ const SHUTDOWN_GRACE_MS = 10000;
345
+ function installLifecycle(httpServer) {
346
+ let shuttingDown = false;
347
+ const shutdown = async (signal) => {
348
+ if (shuttingDown) {
349
+ logger.warn(`${signal} received again — exiting immediately`);
350
+ process.exit(130);
351
+ }
352
+ shuttingDown = true;
353
+ beginShutdown();
354
+ logger.info(`${signal} received — shutting down`);
355
+ const timer = setTimeout(() => {
356
+ logger.warn(`in-flight work did not finish within ${SHUTDOWN_GRACE_MS}ms — exiting anyway`);
357
+ process.exit(0);
358
+ }, SHUTDOWN_GRACE_MS);
359
+ timer.unref();
360
+ // Stop accepting new connections, then drain the calls already running.
361
+ httpServer.close();
362
+ const pending = inFlightCount();
363
+ if (pending > 0) {
364
+ logger.info(`waiting for ${pending} in-flight tool call(s)`);
365
+ await awaitInFlight();
366
+ }
367
+ clearTimeout(timer);
368
+ process.exit(0);
369
+ };
370
+ process.on("SIGINT", () => void shutdown("SIGINT"));
371
+ process.on("SIGTERM", () => void shutdown("SIGTERM"));
372
+ process.on("unhandledRejection", (reason) => {
373
+ logger.error("unhandled promise rejection", {
374
+ error: reason instanceof Error ? (reason.stack ?? reason.message) : String(reason),
375
+ });
376
+ process.exit(1);
377
+ });
378
+ process.on("uncaughtException", (error) => {
379
+ logger.error("uncaught exception", { error: error.stack ?? error.message });
380
+ process.exit(1);
381
+ });
382
+ }
383
+ function main() {
384
+ const config = resolveHttpServerConfig();
385
+ // A public bind WITH tenant credentials in the env is the one genuinely
386
+ // dangerous combination: every unauthenticated request would run under that
387
+ // account. It is legitimate for a single-tenant self-host, so this warns
388
+ // rather than refuses — but a multi-tenant operator must see it.
389
+ const publicBind = config.bindAddress === "0.0.0.0" || config.bindAddress === "::";
390
+ if (publicBind && (process.env.HYDRADB_API_KEY || process.env.HYDRA_DB_API_KEY)) {
391
+ banner("WARNING: HYDRADB_API_KEY is set while binding publicly — every request that " +
392
+ "sends no `Authorization` header will run under this account. Unset it for a " +
393
+ "multi-tenant deployment so each caller must authenticate. See SECURITY.md.");
394
+ }
395
+ const app = createHttpApp(config);
396
+ if (config.oauth) {
397
+ banner(`OAuth enabled: issuer ${config.oauth.issuer}, resource ${config.oauth.resource} ` +
398
+ `(metadata at ${metadataUrl(config.oauth)})`);
399
+ }
400
+ const httpServer = app
401
+ .listen(config.port, config.bindAddress, () => {
402
+ // Startup banners: on stderr so an operator sees where it bound
403
+ // regardless of HYDRADB_LOG_LEVEL.
404
+ banner(`listening on http://${config.bindAddress}:${config.port}`);
405
+ banner(`allowed origins: ${config.allowedOrigins.length > 0
406
+ ? config.allowedOrigins.join(", ")
407
+ : "(none — cross-origin browser requests will be rejected)"}`);
408
+ })
409
+ .on("error", (error) => {
410
+ logger.error("HTTP server error", { error: error.message });
411
+ process.exit(1);
412
+ });
413
+ installLifecycle(httpServer);
414
+ }
415
+ // Only auto-start when run as a script, so tests can import `createHttpApp`
416
+ // without binding a port. Covers `node dist/http.js`, `tsx src/http.ts`, and
417
+ // the Docker entrypoint.
418
+ const invokedAsScript = import.meta.url === `file://${process.argv[1]}` ||
419
+ process.argv[1]?.endsWith("/http.js") ||
420
+ process.argv[1]?.endsWith("/http.ts");
421
+ if (invokedAsScript) {
422
+ main();
423
+ }
424
+ // Re-exported so callers importing the HTTP entry point get the config helpers
425
+ // from one place.
426
+ export { buildAllowedHosts, parseList, parsePort, resolveHttpServerConfig };
427
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
@@ -15,6 +15,7 @@
15
15
  */
16
16
  import { HydraDBClient } from "@hydradb/sdk";
17
17
  import type { HydraDB as SDK } from "@hydradb/sdk";
18
+ import { GraphResource } from "./graph.js";
18
19
  export type ContextKind = "memory" | "knowledge";
19
20
  /**
20
21
  * Sized to fit inside a typical MCP host's tool timeout rather than outlast it,
@@ -46,6 +47,14 @@ export interface HydraConfig {
46
47
  database: string;
47
48
  /** Collection scope (canonical name for the sub-tenant). */
48
49
  collection?: string;
50
+ /**
51
+ * Databases a per-call `database` override may name. Absent means any.
52
+ * Set from an OAuth grant the user confined to specific databases; the
53
+ * default `database` is always allowed.
54
+ */
55
+ allowedDatabases?: string[];
56
+ /** Collections a per-call `collection` override may name. Absent means any. */
57
+ allowedCollections?: string[];
49
58
  /** Optional base URL override; defaults to the SDK's environment. */
50
59
  baseUrl?: string;
51
60
  /**
@@ -88,6 +97,8 @@ export interface QueryParams {
88
97
  numRelatedChunks?: number;
89
98
  /** Per-call collection override. */
90
99
  collection?: string;
100
+ /** Per-call database override. */
101
+ database?: string;
91
102
  }
92
103
  export interface ConversationTurn {
93
104
  user: string;
@@ -123,6 +134,8 @@ export interface IngestParams {
123
134
  /** Filename to attach when ingesting knowledge text as a document. */
124
135
  filename?: string;
125
136
  collection?: string;
137
+ /** Per-call database override. */
138
+ database?: string;
126
139
  }
127
140
  export interface ListParams {
128
141
  kind?: ContextKind;
@@ -130,16 +143,22 @@ export interface ListParams {
130
143
  page?: number;
131
144
  pageSize?: number;
132
145
  collection?: string;
146
+ /** Per-call database override. */
147
+ database?: string;
133
148
  }
134
149
  export interface InspectParams {
135
150
  id: string;
136
151
  mode?: string;
137
152
  expirySeconds?: number;
138
153
  collection?: string;
154
+ /** Per-call database override. */
155
+ database?: string;
139
156
  }
140
157
  export interface IngestionStatusParams {
141
158
  ids: string | string[];
142
159
  collection?: string;
160
+ /** Per-call database override. */
161
+ database?: string;
143
162
  }
144
163
  export interface RelationsParams {
145
164
  id?: string;
@@ -147,11 +166,15 @@ export interface RelationsParams {
147
166
  limit?: number;
148
167
  cursor?: number;
149
168
  collection?: string;
169
+ /** Per-call database override. */
170
+ database?: string;
150
171
  }
151
172
  export interface DeleteParams {
152
173
  ids: string[];
153
174
  kind: ContextKind;
154
175
  collection?: string;
176
+ /** Per-call database override. */
177
+ database?: string;
155
178
  }
156
179
  export interface CreateDatabaseParams {
157
180
  database: string;
@@ -162,16 +185,45 @@ type ScopeFields = {
162
185
  database: string;
163
186
  collection?: string;
164
187
  };
188
+ /**
189
+ * A per-call `database` named one the connection is not allowed to touch.
190
+ *
191
+ * Written for the agent that reads it as a tool error: it names what IS
192
+ * allowed and where the choice was made, so the agent can either use an
193
+ * allowed database or tell the user to reconnect with wider access, rather
194
+ * than retrying blindly.
195
+ */
196
+ export declare class ScopeNotAllowedError extends Error {
197
+ readonly kind: "database" | "collection";
198
+ readonly requested: string;
199
+ readonly allowed: readonly string[];
200
+ constructor(kind: "database" | "collection", requested: string, allowed: readonly string[]);
201
+ }
202
+ /** Kept for the name callers already import; the database case of the above. */
203
+ export declare const DatabaseNotAllowedError: typeof ScopeNotAllowedError;
204
+ /** Throws unless `database` is one the connection may use. */
205
+ export declare function assertDatabaseAllowed(database: string, allowed: readonly string[] | undefined): void;
206
+ /**
207
+ * Throws unless `collection` is one the connection may use.
208
+ *
209
+ * Separate from the database check because confinement has to cover both:
210
+ * a caller pinned to one database can otherwise step sideways into a
211
+ * collection the consent screen never showed, and `drop_collection` makes
212
+ * that destructive.
213
+ */
214
+ export declare function assertCollectionAllowed(collection: string, allowed: readonly string[] | undefined): void;
165
215
  declare abstract class Resource {
166
216
  protected readonly sdk: HydraDBClient;
167
217
  private readonly database;
168
218
  private readonly collection?;
169
- protected constructor(sdk: HydraDBClient, database: string, collection?: string | undefined);
170
- protected scope(override?: string): ScopeFields;
219
+ private readonly allowedDatabases?;
220
+ private readonly allowedCollections?;
221
+ protected constructor(sdk: HydraDBClient, database: string, collection?: string | undefined, allowedDatabases?: readonly string[] | undefined, allowedCollections?: readonly string[] | undefined);
222
+ protected scope(override?: string, dbOverride?: string): ScopeFields;
171
223
  protected call<T>(path: string, fn: () => Promise<unknown>): Promise<T>;
172
224
  }
173
225
  export declare class ContextResource extends Resource {
174
- constructor(sdk: HydraDBClient, database: string, collection?: string);
226
+ constructor(sdk: HydraDBClient, database: string, collection?: string, allowedDatabases?: readonly string[], allowedCollections?: readonly string[]);
175
227
  /**
176
228
  * The single retrieval entry point (SDK `client.query`).
177
229
  *
@@ -199,7 +251,7 @@ export declare class ContextResource extends Resource {
199
251
  delete(params: DeleteParams, opts?: RequestOptions): Promise<SDK.SourcesMemoryDeleteResponse>;
200
252
  }
201
253
  export declare class DatabasesResource extends Resource {
202
- constructor(sdk: HydraDBClient, database: string, collection?: string);
254
+ constructor(sdk: HydraDBClient, database: string, collection?: string, allowedDatabases?: readonly string[], allowedCollections?: readonly string[]);
203
255
  create(params: CreateDatabaseParams): Promise<SDK.TenantsTenantCreateAcceptedResponse>;
204
256
  delete(database: string): Promise<SDK.TenantsTenantDeleteResponse>;
205
257
  list(): Promise<SDK.TenantsTenantIdsResponse>;
@@ -216,6 +268,19 @@ export declare class DatabasesResource extends Resource {
216
268
  export declare class HydraDB {
217
269
  readonly context: ContextResource;
218
270
  readonly databases: DatabasesResource;
271
+ /** The default database every unscoped call uses. */
272
+ readonly database: string;
273
+ /** Databases a per-call override may name; undefined means any. */
274
+ readonly allowedDatabases?: readonly string[];
275
+ /** Collections a per-call override may name; undefined means any. */
276
+ readonly allowedCollections?: readonly string[];
277
+ /** The default collection every unscoped call uses. */
278
+ readonly collection?: string;
279
+ /**
280
+ * BYOG graph operations. Not backed by the SDK — see ./graph.ts for why —
281
+ * but exposed here so callers reach every HydraDB surface through one object.
282
+ */
283
+ readonly graph: GraphResource;
219
284
  constructor(config: HydraConfig, sdk?: HydraDBClient);
220
285
  }
221
286
  export {};