@hydradb/mcp 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +167 -0
- package/README.md +92 -34
- package/dist/adapters.d.ts +27 -16
- package/dist/adapters.js +58 -54
- package/dist/adapters.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.js +27 -1
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +43 -3
- package/dist/context.js +374 -34
- package/dist/context.js.map +1 -1
- package/dist/descriptions.d.ts +57 -28
- package/dist/descriptions.js +203 -49
- package/dist/descriptions.js.map +1 -1
- package/dist/hydra/client.d.ts +69 -8
- package/dist/hydra/client.js +102 -20
- package/dist/hydra/client.js.map +1 -1
- package/dist/hydra/errors.js +91 -6
- package/dist/hydra/errors.js.map +1 -1
- package/dist/hydra/index.d.ts +2 -2
- package/dist/hydra/index.js +1 -1
- package/dist/hydra/index.js.map +1 -1
- package/dist/index.js +97 -10
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +18 -1
- package/dist/server.js +998 -106
- package/dist/server.js.map +1 -1
- package/dist/tool-names.d.ts +2 -1
- package/dist/tool-names.js +2 -0
- package/dist/tool-names.js.map +1 -1
- package/dist/types.d.ts +8 -51
- package/dist/types.js +6 -5
- package/dist/types.js.map +1 -1
- package/package.json +11 -6
package/dist/hydra/client.d.ts
CHANGED
|
@@ -16,6 +16,29 @@
|
|
|
16
16
|
import { HydraDBClient } from "@hydradb/sdk";
|
|
17
17
|
import type { HydraDB as SDK } from "@hydradb/sdk";
|
|
18
18
|
export type ContextKind = "memory" | "knowledge";
|
|
19
|
+
/**
|
|
20
|
+
* Sized to fit inside a typical MCP host's tool timeout rather than outlast it,
|
|
21
|
+
* so a stalled call fails with a HydraDB diagnostic the caller can act on
|
|
22
|
+
* instead of a generic host-side timeout carrying no information.
|
|
23
|
+
*/
|
|
24
|
+
export declare const DEFAULT_TIMEOUT_SECONDS = 30;
|
|
25
|
+
export declare const DEFAULT_MAX_RETRIES = 2;
|
|
26
|
+
/**
|
|
27
|
+
* Per-call transport controls, separate from the domain params.
|
|
28
|
+
*
|
|
29
|
+
* `signal` carries the host's cancellation into the SDK. Without it a cancelled
|
|
30
|
+
* MCP tool call leaves the outbound HTTP request in flight: the caller has given
|
|
31
|
+
* up, and the process keeps working and keeps retrying on its behalf.
|
|
32
|
+
*/
|
|
33
|
+
export interface RequestOptions {
|
|
34
|
+
signal?: AbortSignal;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Retrieval accepts a third corpus selector the write/list paths do not: `all`,
|
|
38
|
+
* which searches memories and knowledge together. Only `context.query` takes it
|
|
39
|
+
* — ingesting or deleting "all" is meaningless, so those stay `ContextKind`.
|
|
40
|
+
*/
|
|
41
|
+
export type QueryKind = ContextKind | "all";
|
|
19
42
|
export interface HydraConfig {
|
|
20
43
|
/** Bearer token (the HydraDB API key). */
|
|
21
44
|
token: string;
|
|
@@ -25,16 +48,35 @@ export interface HydraConfig {
|
|
|
25
48
|
collection?: string;
|
|
26
49
|
/** Optional base URL override; defaults to the SDK's environment. */
|
|
27
50
|
baseUrl?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Per-attempt deadline. The SDK's own default is 60s, which combined with its
|
|
53
|
+
* 2 retries means a persistently failing endpoint can occupy a caller for
|
|
54
|
+
* ~3 minutes with no output — far longer than any MCP host waits, so in
|
|
55
|
+
* practice the host times out first and the caller gets a generic error with
|
|
56
|
+
* no HydraDB diagnostic while this process keeps retrying.
|
|
57
|
+
*/
|
|
58
|
+
timeoutSeconds?: number;
|
|
59
|
+
/** Retries per call. The SDK defaults to 2; set explicitly so it is a choice. */
|
|
60
|
+
maxRetries?: number;
|
|
28
61
|
}
|
|
29
62
|
export interface QueryParams {
|
|
30
63
|
query: string;
|
|
31
|
-
kind?:
|
|
64
|
+
kind?: QueryKind;
|
|
32
65
|
operator?: "or" | "and" | "phrase";
|
|
33
66
|
maxResults?: number;
|
|
34
67
|
mode?: "fast" | "thinking" | "auto";
|
|
35
68
|
graphContext?: boolean;
|
|
36
69
|
alpha?: number;
|
|
37
70
|
recencyBias?: number;
|
|
71
|
+
/**
|
|
72
|
+
* Restrict retrieval to these source ids. A hard pre-filter: the server
|
|
73
|
+
* returns nothing rather than widening when none match.
|
|
74
|
+
*/
|
|
75
|
+
ids?: string[];
|
|
76
|
+
/** Exact-match filters over stored metadata. No ranges, no partial matches. */
|
|
77
|
+
metadataFilters?: Record<string, unknown>;
|
|
78
|
+
/** Adjacent chunks pulled in alongside each match, for surrounding context. */
|
|
79
|
+
numRelatedChunks?: number;
|
|
38
80
|
/** Per-call collection override. */
|
|
39
81
|
collection?: string;
|
|
40
82
|
}
|
|
@@ -56,6 +98,19 @@ export interface IngestParams {
|
|
|
56
98
|
/** Passed through only when `infer` is truthy (host-owned default text). */
|
|
57
99
|
customInstructions?: string;
|
|
58
100
|
upsert?: boolean;
|
|
101
|
+
/**
|
|
102
|
+
* Tenant metadata stored alongside the memory, and matchable later via
|
|
103
|
+
* `metadataFilters` on query.
|
|
104
|
+
*
|
|
105
|
+
* Accepted by the backend (`domain/memories/models.go`) but absent from the
|
|
106
|
+
* generated SDK request type, which is why the wrapper carries it explicitly
|
|
107
|
+
* inside the memory item rather than as a typed field.
|
|
108
|
+
*/
|
|
109
|
+
metadata?: Record<string, unknown>;
|
|
110
|
+
/** Document-level metadata, matchable via `additional_metadata`. */
|
|
111
|
+
additionalMetadata?: Record<string, unknown>;
|
|
112
|
+
/** When the fact was true, as opposed to when it was stored (RFC3339 date). */
|
|
113
|
+
observationDate?: string;
|
|
59
114
|
/** Filename to attach when ingesting knowledge text as a document. */
|
|
60
115
|
filename?: string;
|
|
61
116
|
collection?: string;
|
|
@@ -109,19 +164,25 @@ declare abstract class Resource {
|
|
|
109
164
|
export declare class ContextResource extends Resource {
|
|
110
165
|
constructor(sdk: HydraDBClient, database: string, collection?: string);
|
|
111
166
|
/** The single retrieval entry point (SDK `client.query`). */
|
|
112
|
-
query(params: QueryParams): Promise<SDK.SearchV2RetrievalResult>;
|
|
113
|
-
/**
|
|
114
|
-
|
|
167
|
+
query(params: QueryParams, opts?: RequestOptions): Promise<SDK.SearchV2RetrievalResult>;
|
|
168
|
+
/**
|
|
169
|
+
* Ingest a memory or knowledge item (SDK `context.ingest`, multipart).
|
|
170
|
+
*
|
|
171
|
+
* `async` so the knowledge-path validation below surfaces as a rejection.
|
|
172
|
+
* Every other failure in this wrapper rejects, and a caller that only handles
|
|
173
|
+
* `.catch()` would otherwise see this one escape as a synchronous throw.
|
|
174
|
+
*/
|
|
175
|
+
ingest(params: IngestParams, opts?: RequestOptions): Promise<SDK.IngestionV2SourceUploadResponse>;
|
|
115
176
|
/** List memories or knowledge sources (SDK `context.list`). */
|
|
116
|
-
list(params?: ListParams): Promise<SDK.ListV2SourceListResponse>;
|
|
177
|
+
list(params?: ListParams, opts?: RequestOptions): Promise<SDK.ListV2SourceListResponse>;
|
|
117
178
|
/** Fetch a source's content (SDK `context.inspect`; was "fetch content"). */
|
|
118
|
-
inspect(params: InspectParams): Promise<SDK.FetchV2SourceFetchResponse>;
|
|
179
|
+
inspect(params: InspectParams, opts?: RequestOptions): Promise<SDK.FetchV2SourceFetchResponse>;
|
|
119
180
|
/** Per-source indexing progress (SDK `context.status`). */
|
|
120
|
-
ingestionStatus(params: IngestionStatusParams): Promise<SDK.IngestionV2BatchProcessingStatus>;
|
|
181
|
+
ingestionStatus(params: IngestionStatusParams, opts?: RequestOptions): Promise<SDK.IngestionV2BatchProcessingStatus>;
|
|
121
182
|
/** Knowledge-graph relations (SDK `context.relations`). */
|
|
122
183
|
relations(params?: RelationsParams): Promise<SDK.GraphGraphRelationsResponse>;
|
|
123
184
|
/** Delete memories or knowledge sources (SDK `context.delete`). */
|
|
124
|
-
delete(params: DeleteParams): Promise<SDK.SourcesMemoryDeleteResponse>;
|
|
185
|
+
delete(params: DeleteParams, opts?: RequestOptions): Promise<SDK.SourcesMemoryDeleteResponse>;
|
|
125
186
|
}
|
|
126
187
|
export declare class DatabasesResource extends Resource {
|
|
127
188
|
constructor(sdk: HydraDBClient, database: string, collection?: string);
|
package/dist/hydra/client.js
CHANGED
|
@@ -17,8 +17,34 @@ import { Buffer } from "node:buffer";
|
|
|
17
17
|
import { HydraDBClient } from "@hydradb/sdk";
|
|
18
18
|
import { unwrap } from "./envelope.js";
|
|
19
19
|
import { translateError } from "./errors.js";
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Sized to fit inside a typical MCP host's tool timeout rather than outlast it,
|
|
22
|
+
* so a stalled call fails with a HydraDB diagnostic the caller can act on
|
|
23
|
+
* instead of a generic host-side timeout carrying no information.
|
|
24
|
+
*/
|
|
25
|
+
export const DEFAULT_TIMEOUT_SECONDS = 30;
|
|
26
|
+
export const DEFAULT_MAX_RETRIES = 2;
|
|
27
|
+
/**
|
|
28
|
+
* An SDK logger that cannot corrupt the stdio transport.
|
|
29
|
+
*
|
|
30
|
+
* The SDK's own `ConsoleLogger` implements `debug`/`info` via `console.debug` /
|
|
31
|
+
* `console.info`, which are stdout aliases in Node. On stdio transport stdout IS
|
|
32
|
+
* the JSON-RPC channel, so a single SDK log line would break the session.
|
|
33
|
+
*
|
|
34
|
+
* Not currently live — the SDK's default logger is constructed `silent: true` —
|
|
35
|
+
* but the exposure is one `logging: { level: "debug" }` away, which is precisely
|
|
36
|
+
* what someone debugging a production incident reaches for. Passing an explicit
|
|
37
|
+
* logger pins the safe behaviour instead of inheriting it.
|
|
38
|
+
*/
|
|
39
|
+
const STDERR_LOGGER = {
|
|
40
|
+
debug: (message, ...args) => console.error("[hydradb-sdk]", message, ...args),
|
|
41
|
+
info: (message, ...args) => console.error("[hydradb-sdk]", message, ...args),
|
|
42
|
+
warn: (message, ...args) => console.error("[hydradb-sdk]", message, ...args),
|
|
43
|
+
error: (message, ...args) => console.error("[hydradb-sdk]", message, ...args),
|
|
44
|
+
};
|
|
45
|
+
/** Wrapper options → the SDK's per-request options, omitted when there is nothing to say. */
|
|
46
|
+
function req(opts) {
|
|
47
|
+
return opts?.signal ? { abortSignal: opts.signal } : undefined;
|
|
22
48
|
}
|
|
23
49
|
class Resource {
|
|
24
50
|
constructor(sdk, database, collection) {
|
|
@@ -42,28 +68,40 @@ class Resource {
|
|
|
42
68
|
}
|
|
43
69
|
}
|
|
44
70
|
export class ContextResource extends Resource {
|
|
71
|
+
// The base constructor is `protected`, so this one is what makes the class
|
|
72
|
+
// instantiable from outside the file. Removing it fails with TS2674.
|
|
73
|
+
// biome-ignore lint/complexity/noUselessConstructor: widens visibility
|
|
45
74
|
constructor(sdk, database, collection) {
|
|
46
75
|
super(sdk, database, collection);
|
|
47
76
|
}
|
|
48
77
|
/** The single retrieval entry point (SDK `client.query`). */
|
|
49
|
-
query(params) {
|
|
78
|
+
query(params, opts) {
|
|
50
79
|
return this.call("/query", () => this.sdk.query({
|
|
51
80
|
...this.scope(params.collection),
|
|
52
81
|
query: params.query,
|
|
53
|
-
type:
|
|
82
|
+
type: params.kind,
|
|
54
83
|
operator: params.operator,
|
|
55
84
|
maxResults: params.maxResults,
|
|
56
85
|
mode: params.mode,
|
|
57
86
|
graphContext: params.graphContext,
|
|
58
87
|
alpha: params.alpha,
|
|
59
88
|
recencyBias: params.recencyBias,
|
|
60
|
-
|
|
89
|
+
ids: params.ids,
|
|
90
|
+
metadataFilters: params.metadataFilters,
|
|
91
|
+
numRelatedChunks: params.numRelatedChunks,
|
|
92
|
+
}, req(opts)));
|
|
61
93
|
}
|
|
62
|
-
/**
|
|
63
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Ingest a memory or knowledge item (SDK `context.ingest`, multipart).
|
|
96
|
+
*
|
|
97
|
+
* `async` so the knowledge-path validation below surfaces as a rejection.
|
|
98
|
+
* Every other failure in this wrapper rejects, and a caller that only handles
|
|
99
|
+
* `.catch()` would otherwise see this one escape as a synchronous throw.
|
|
100
|
+
*/
|
|
101
|
+
async ingest(params, opts) {
|
|
64
102
|
const request = {
|
|
65
103
|
...this.scope(params.collection),
|
|
66
|
-
type:
|
|
104
|
+
type: params.kind,
|
|
67
105
|
};
|
|
68
106
|
if (params.upsert != null) {
|
|
69
107
|
request.upsert = String(params.upsert);
|
|
@@ -88,9 +126,41 @@ export class ContextResource extends Resource {
|
|
|
88
126
|
item.title = params.title;
|
|
89
127
|
if (params.userName != null)
|
|
90
128
|
item.user_name = params.userName;
|
|
129
|
+
if (params.metadata != null)
|
|
130
|
+
item.metadata = params.metadata;
|
|
131
|
+
if (params.additionalMetadata != null) {
|
|
132
|
+
item.additional_metadata = params.additionalMetadata;
|
|
133
|
+
}
|
|
134
|
+
if (params.observationDate != null) {
|
|
135
|
+
item.observation_date = params.observationDate;
|
|
136
|
+
}
|
|
91
137
|
request.memories = JSON.stringify([item]);
|
|
92
138
|
}
|
|
93
139
|
else {
|
|
140
|
+
// The knowledge path can only carry the document itself and its
|
|
141
|
+
// filename. Everything below belongs to the memory item shape and has
|
|
142
|
+
// nowhere to go here — so reject rather than accept and discard. A
|
|
143
|
+
// caller that sets `infer: true` on a knowledge write and is answered
|
|
144
|
+
// "success: 1, failed: 0" has been told its instruction was honoured
|
|
145
|
+
// when it was dropped on the floor.
|
|
146
|
+
const unsupported = [
|
|
147
|
+
["pairs", params.pairs],
|
|
148
|
+
["sourceId", params.sourceId],
|
|
149
|
+
["infer", params.infer],
|
|
150
|
+
["isMarkdown", params.isMarkdown],
|
|
151
|
+
["customInstructions", params.customInstructions],
|
|
152
|
+
["userName", params.userName],
|
|
153
|
+
["metadata", params.metadata],
|
|
154
|
+
["additionalMetadata", params.additionalMetadata],
|
|
155
|
+
["observationDate", params.observationDate],
|
|
156
|
+
]
|
|
157
|
+
.filter(([, value]) => value != null)
|
|
158
|
+
.map(([name]) => name);
|
|
159
|
+
if (unsupported.length > 0) {
|
|
160
|
+
throw new Error(`Knowledge ingestion does not support ${unsupported.join(", ")} — ` +
|
|
161
|
+
`those apply to memory ingestion only. Pass kind "memory" instead, ` +
|
|
162
|
+
`or drop them.`);
|
|
163
|
+
}
|
|
94
164
|
// Knowledge is multipart with the document as a file part — never the
|
|
95
165
|
// `app_knowledge` JSON field (guards the DX-G-002 class of bug).
|
|
96
166
|
if (params.text != null) {
|
|
@@ -103,54 +173,57 @@ export class ContextResource extends Resource {
|
|
|
103
173
|
};
|
|
104
174
|
}
|
|
105
175
|
}
|
|
106
|
-
return this.call("/context/ingest", () => this.sdk.context.ingest(request));
|
|
176
|
+
return this.call("/context/ingest", () => this.sdk.context.ingest(request, req(opts)));
|
|
107
177
|
}
|
|
108
178
|
/** List memories or knowledge sources (SDK `context.list`). */
|
|
109
|
-
list(params = {}) {
|
|
179
|
+
list(params = {}, opts) {
|
|
110
180
|
return this.call("/context/list", () => this.sdk.context.list({
|
|
111
181
|
...this.scope(params.collection),
|
|
112
|
-
type:
|
|
182
|
+
type: params.kind,
|
|
113
183
|
ids: params.ids,
|
|
114
184
|
page: params.page,
|
|
115
185
|
pageSize: params.pageSize,
|
|
116
|
-
}));
|
|
186
|
+
}, req(opts)));
|
|
117
187
|
}
|
|
118
188
|
/** Fetch a source's content (SDK `context.inspect`; was "fetch content"). */
|
|
119
|
-
inspect(params) {
|
|
189
|
+
inspect(params, opts) {
|
|
120
190
|
return this.call("/context/inspect", () => this.sdk.context.inspect({
|
|
121
191
|
...this.scope(params.collection),
|
|
122
192
|
id: params.id,
|
|
123
193
|
mode: params.mode,
|
|
124
194
|
expirySeconds: params.expirySeconds,
|
|
125
|
-
}));
|
|
195
|
+
}, req(opts)));
|
|
126
196
|
}
|
|
127
197
|
/** Per-source indexing progress (SDK `context.status`). */
|
|
128
|
-
ingestionStatus(params) {
|
|
198
|
+
ingestionStatus(params, opts) {
|
|
129
199
|
return this.call("/context/status", () => this.sdk.context.status({
|
|
130
200
|
...this.scope(params.collection),
|
|
131
201
|
ids: params.ids,
|
|
132
|
-
}));
|
|
202
|
+
}, req(opts)));
|
|
133
203
|
}
|
|
134
204
|
/** Knowledge-graph relations (SDK `context.relations`). */
|
|
135
205
|
relations(params = {}) {
|
|
136
206
|
return this.call("/context/relations", () => this.sdk.context.relations({
|
|
137
207
|
...this.scope(params.collection),
|
|
138
208
|
id: params.id,
|
|
139
|
-
type:
|
|
209
|
+
type: params.kind,
|
|
140
210
|
limit: params.limit,
|
|
141
211
|
cursor: params.cursor,
|
|
142
212
|
}));
|
|
143
213
|
}
|
|
144
214
|
/** Delete memories or knowledge sources (SDK `context.delete`). */
|
|
145
|
-
delete(params) {
|
|
215
|
+
delete(params, opts) {
|
|
146
216
|
return this.call("/context", () => this.sdk.context.delete({
|
|
147
217
|
...this.scope(params.collection),
|
|
148
218
|
ids: params.ids,
|
|
149
|
-
type:
|
|
150
|
-
}));
|
|
219
|
+
type: params.kind,
|
|
220
|
+
}, req(opts)));
|
|
151
221
|
}
|
|
152
222
|
}
|
|
153
223
|
export class DatabasesResource extends Resource {
|
|
224
|
+
// The base constructor is `protected`, so this one is what makes the class
|
|
225
|
+
// instantiable from outside the file. Removing it fails with TS2674.
|
|
226
|
+
// biome-ignore lint/complexity/noUselessConstructor: widens visibility
|
|
154
227
|
constructor(sdk, database, collection) {
|
|
155
228
|
super(sdk, database, collection);
|
|
156
229
|
}
|
|
@@ -189,6 +262,15 @@ export class HydraDB {
|
|
|
189
262
|
new HydraDBClient({
|
|
190
263
|
token: config.token,
|
|
191
264
|
...(config.baseUrl != null ? { baseUrl: config.baseUrl } : {}),
|
|
265
|
+
// Both are stated rather than inherited. The SDK's defaults (60s,
|
|
266
|
+
// 2 retries) were never chosen by this server, and their product is
|
|
267
|
+
// a ~3 minute worst case on the slowest tool it exposes.
|
|
268
|
+
timeoutInSeconds: config.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS,
|
|
269
|
+
maxRetries: config.maxRetries ?? DEFAULT_MAX_RETRIES,
|
|
270
|
+
// Never inherit the SDK's console logger: it writes to stdout, which
|
|
271
|
+
// on stdio transport is the JSON-RPC channel. Only the sink is
|
|
272
|
+
// overridden — level and silencing keep the SDK's own defaults.
|
|
273
|
+
logging: { logger: STDERR_LOGGER },
|
|
192
274
|
});
|
|
193
275
|
this.context = new ContextResource(client, config.database, config.collection);
|
|
194
276
|
this.databases = new DatabasesResource(client, config.database, config.collection);
|
package/dist/hydra/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/hydra/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAG7C,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAI7C,SAAS,
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/hydra/client.ts"],"names":[],"mappings":"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"}
|
package/dist/hydra/errors.js
CHANGED
|
@@ -27,17 +27,102 @@ export class HydraWrapperError extends Error {
|
|
|
27
27
|
Object.setPrototypeOf(this, HydraWrapperError.prototype);
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
+
/** Anything past this is not diagnosis, it is payload. */
|
|
31
|
+
const MAX_BODY_CHARS = 512;
|
|
32
|
+
/**
|
|
33
|
+
* Credential-shaped substrings, scrubbed before an error body is shown.
|
|
34
|
+
*
|
|
35
|
+
* Defence in depth for bodies we did not write. HydraDB's own error path is
|
|
36
|
+
* clean — every 401 message is a static string literal and backend failures
|
|
37
|
+
* collapse to "Internal server error" — but this string also carries whatever a
|
|
38
|
+
* CDN, load balancer, WAF or corporate proxy returns, and those are not ours to
|
|
39
|
+
* vouch for. The SDK already redacts sensitive headers and query params before
|
|
40
|
+
* logging; response bodies are the one place it does not.
|
|
41
|
+
*/
|
|
42
|
+
const SECRET_PATTERNS = [
|
|
43
|
+
[/\bBearer\s+\S+/gi, "Bearer [redacted]"],
|
|
44
|
+
[/\b(sk|hdb|key|tok)[-_][A-Za-z0-9_-]{16,}/g, "[redacted]"],
|
|
45
|
+
[
|
|
46
|
+
/("(?:api[-_]?key|token|authorization|password|secret)"\s*:\s*)"[^"]*"/gi,
|
|
47
|
+
'$1"[redacted]"',
|
|
48
|
+
],
|
|
49
|
+
];
|
|
50
|
+
function scrub(text) {
|
|
51
|
+
let out = text;
|
|
52
|
+
for (const [pattern, replacement] of SECRET_PATTERNS) {
|
|
53
|
+
out = out.replace(pattern, replacement);
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
/** Reduce an HTML error page to its readable text, so the cap spends its budget well. */
|
|
58
|
+
function stripMarkup(text) {
|
|
59
|
+
if (!/^\s*</.test(text))
|
|
60
|
+
return text;
|
|
61
|
+
const title = text.match(/<title[^>]*>([\s\S]*?)<\/title>/i)?.[1]?.trim();
|
|
62
|
+
const stripped = text
|
|
63
|
+
.replace(/<script[\s\S]*?<\/script>/gi, " ")
|
|
64
|
+
.replace(/<style[\s\S]*?<\/style>/gi, " ")
|
|
65
|
+
.replace(/<[^>]+>/g, " ")
|
|
66
|
+
.replace(/\s+/g, " ")
|
|
67
|
+
.trim();
|
|
68
|
+
return title ? `${title} — ${stripped}` : stripped;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The v2 envelope, when the body is one.
|
|
72
|
+
*
|
|
73
|
+
* Rendering `error.code` / `error.message` / `meta.request_id` instead of the
|
|
74
|
+
* whole JSON blob makes the common case both shorter AND more useful: the
|
|
75
|
+
* request id is exactly what a user needs to file a support ticket, and it was
|
|
76
|
+
* previously buried in a stringified object.
|
|
77
|
+
*/
|
|
78
|
+
function fromEnvelope(body) {
|
|
79
|
+
if (body == null || typeof body !== "object")
|
|
80
|
+
return undefined;
|
|
81
|
+
const record = body;
|
|
82
|
+
const error = record.error;
|
|
83
|
+
if (error == null || typeof error !== "object")
|
|
84
|
+
return undefined;
|
|
85
|
+
const { code, message } = error;
|
|
86
|
+
const parts = [];
|
|
87
|
+
if (typeof code === "string" && code !== "")
|
|
88
|
+
parts.push(code);
|
|
89
|
+
if (typeof message === "string" && message !== "")
|
|
90
|
+
parts.push(message);
|
|
91
|
+
if (parts.length === 0)
|
|
92
|
+
return undefined;
|
|
93
|
+
const meta = record.meta;
|
|
94
|
+
const requestId = meta != null && typeof meta === "object"
|
|
95
|
+
? meta.request_id
|
|
96
|
+
: undefined;
|
|
97
|
+
const trailer = typeof requestId === "string" && requestId !== ""
|
|
98
|
+
? ` (request_id: ${requestId})`
|
|
99
|
+
: "";
|
|
100
|
+
return `${parts.join(": ")}${trailer}`;
|
|
101
|
+
}
|
|
30
102
|
function bodyToString(body) {
|
|
31
103
|
if (body == null)
|
|
32
104
|
return "";
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
105
|
+
const structured = fromEnvelope(body);
|
|
106
|
+
if (structured != null)
|
|
107
|
+
return truncate(scrub(structured));
|
|
108
|
+
let raw;
|
|
109
|
+
if (typeof body === "string") {
|
|
110
|
+
raw = body;
|
|
37
111
|
}
|
|
38
|
-
|
|
39
|
-
|
|
112
|
+
else {
|
|
113
|
+
try {
|
|
114
|
+
raw = JSON.stringify(body);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
raw = String(body);
|
|
118
|
+
}
|
|
40
119
|
}
|
|
120
|
+
return truncate(scrub(stripMarkup(raw)));
|
|
121
|
+
}
|
|
122
|
+
function truncate(text) {
|
|
123
|
+
if (text.length <= MAX_BODY_CHARS)
|
|
124
|
+
return text;
|
|
125
|
+
return `${text.slice(0, MAX_BODY_CHARS)}… (truncated, ${text.length} chars)`;
|
|
41
126
|
}
|
|
42
127
|
/**
|
|
43
128
|
* Translate any error thrown by an SDK call into a `HydraWrapperError` carrying
|
package/dist/hydra/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/hydra/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IAU3C,YACC,OAAe,EACf,IAAY,EACZ,IAA2D;QAE3D,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;QAChC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,IAAI,EAAE,MAAM,CAAC;QAC3B,IAAI,CAAC,IAAI,GAAG,IAAI,EAAE,IAAI,CAAC;QACvB,IAAI,CAAC,KAAK,GAAG,IAAI,EAAE,KAAK,CAAC;QACzB,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC1D,CAAC;CACD;AAED,SAAS,YAAY,CAAC,IAAa;IAClC,IAAI,IAAI,IAAI,IAAI;QAAE,OAAO,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/hydra/errors.ts"],"names":[],"mappings":"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"}
|
package/dist/hydra/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* It is intentionally self-contained so the same pattern can be ported to the
|
|
6
6
|
* other client repos (per CONTRACT.md).
|
|
7
7
|
*/
|
|
8
|
-
export { HydraDB, ContextResource, DatabasesResource } from "./client.js";
|
|
9
|
-
export type { HydraConfig, ContextKind, ConversationTurn, QueryParams, IngestParams, ListParams, InspectParams, IngestionStatusParams, RelationsParams, DeleteParams, CreateDatabaseParams, } from "./client.js";
|
|
8
|
+
export { HydraDB, ContextResource, DatabasesResource, DEFAULT_TIMEOUT_SECONDS, DEFAULT_MAX_RETRIES, } from "./client.js";
|
|
9
|
+
export type { HydraConfig, ContextKind, RequestOptions, QueryKind, ConversationTurn, QueryParams, IngestParams, ListParams, InspectParams, IngestionStatusParams, RelationsParams, DeleteParams, CreateDatabaseParams, } from "./client.js";
|
|
10
10
|
export { HydraWrapperError, translateError } from "./errors.js";
|
|
11
11
|
export { unwrap } from "./envelope.js";
|
package/dist/hydra/index.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* It is intentionally self-contained so the same pattern can be ported to the
|
|
6
6
|
* other client repos (per CONTRACT.md).
|
|
7
7
|
*/
|
|
8
|
-
export { HydraDB, ContextResource, DatabasesResource } from "./client.js";
|
|
8
|
+
export { HydraDB, ContextResource, DatabasesResource, DEFAULT_TIMEOUT_SECONDS, DEFAULT_MAX_RETRIES, } from "./client.js";
|
|
9
9
|
export { HydraWrapperError, translateError } from "./errors.js";
|
|
10
10
|
export { unwrap } from "./envelope.js";
|
|
11
11
|
//# sourceMappingURL=index.js.map
|
package/dist/hydra/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hydra/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hydra/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACN,OAAO,EACP,eAAe,EACf,iBAAiB,EACjB,uBAAuB,EACvB,mBAAmB,GACnB,MAAM,aAAa,CAAC;AAgBrB,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
3
|
import { resolveConfig } from "./config.js";
|
|
4
|
-
import {
|
|
4
|
+
import { logger } from "./logger.js";
|
|
5
|
+
import { awaitInFlight, beginShutdown, createHydraDBServer, inFlightCount, } from "./server.js";
|
|
5
6
|
// Fail fast with a clean message if required config is missing. Honours the
|
|
6
7
|
// canonical HYDRADB_* names (and the deprecated HYDRA_DB_* aliases).
|
|
7
8
|
try {
|
|
@@ -11,16 +12,102 @@ catch (error) {
|
|
|
11
12
|
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
12
13
|
process.exit(1);
|
|
13
14
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
/**
|
|
16
|
+
* How long to let in-flight work finish after a stop signal.
|
|
17
|
+
*
|
|
18
|
+
* SIGTERM is how most MCP hosts and container runtimes stop a server, and an
|
|
19
|
+
* ingest that has been accepted but not yet answered is the case worth
|
|
20
|
+
* protecting: dropping it leaves the caller unable to tell whether the write
|
|
21
|
+
* committed. Short enough that nothing hangs a shutdown.
|
|
22
|
+
*/
|
|
23
|
+
const SHUTDOWN_GRACE_MS = 5000;
|
|
24
|
+
/** Stack included when there is one — this is the last thing logged before exit. */
|
|
25
|
+
function describe(error) {
|
|
26
|
+
if (error instanceof Error)
|
|
27
|
+
return error.stack ?? `${error.name}: ${error.message}`;
|
|
28
|
+
return String(error);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Wire process-level lifecycle handling.
|
|
32
|
+
*
|
|
33
|
+
* Before this the server had none: no SIGINT/SIGTERM handling, no
|
|
34
|
+
* unhandledRejection, no uncaughtException, and no transport close handling.
|
|
35
|
+
* SIGTERM terminated the process immediately, dropping any in-flight request
|
|
36
|
+
* with no log line; and after `connect()` resolved, a stray rejection killed the
|
|
37
|
+
* process with a raw stack on stderr and no MCP-level notice, so the host simply
|
|
38
|
+
* saw the server vanish mid-session.
|
|
39
|
+
*
|
|
40
|
+
* Everything here writes to stderr only. On stdio transport, stdout is the
|
|
41
|
+
* JSON-RPC channel and any stray byte on it corrupts the stream.
|
|
42
|
+
*/
|
|
43
|
+
function installLifecycle(server) {
|
|
44
|
+
let shuttingDown = false;
|
|
45
|
+
const shutdown = async (signal) => {
|
|
46
|
+
// A second signal means the operator is impatient; honour that rather
|
|
47
|
+
// than waiting out the grace period twice.
|
|
48
|
+
if (shuttingDown) {
|
|
49
|
+
logger.warn(`${signal} received again — exiting immediately`);
|
|
50
|
+
process.exit(130);
|
|
51
|
+
}
|
|
52
|
+
shuttingDown = true;
|
|
53
|
+
// Stop accepting NEW calls before draining. Without this, a call arriving
|
|
54
|
+
// after the drain resolves but before the transport closes is accepted and
|
|
55
|
+
// then aborted, which is the failure draining exists to prevent.
|
|
56
|
+
beginShutdown();
|
|
57
|
+
logger.info(`${signal} received — shutting down`);
|
|
58
|
+
const timer = setTimeout(() => {
|
|
59
|
+
logger.warn(`in-flight work did not finish within ${SHUTDOWN_GRACE_MS}ms — exiting anyway`);
|
|
60
|
+
process.exit(0);
|
|
61
|
+
}, SHUTDOWN_GRACE_MS);
|
|
62
|
+
// Do not let the grace timer itself hold the process open.
|
|
63
|
+
timer.unref();
|
|
64
|
+
try {
|
|
65
|
+
// Drain BEFORE closing. `server.close()` tears down the transport; it
|
|
66
|
+
// does not wait for handlers already running, so closing first would
|
|
67
|
+
// cut an accepted ingest off mid-write and leave the caller unable to
|
|
68
|
+
// tell whether it committed.
|
|
69
|
+
const pending = inFlightCount();
|
|
70
|
+
if (pending > 0) {
|
|
71
|
+
logger.info(`waiting for ${pending} in-flight tool call(s)`);
|
|
72
|
+
await awaitInFlight();
|
|
73
|
+
}
|
|
74
|
+
await server.close();
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
logger.error("error while closing the server", { error: describe(error) });
|
|
78
|
+
}
|
|
79
|
+
clearTimeout(timer);
|
|
80
|
+
process.exit(0);
|
|
81
|
+
};
|
|
82
|
+
process.on("SIGINT", () => void shutdown("SIGINT"));
|
|
83
|
+
process.on("SIGTERM", () => void shutdown("SIGTERM"));
|
|
84
|
+
// The host closing the pipe is a normal end of session, not a failure.
|
|
85
|
+
server.onclose = () => {
|
|
86
|
+
if (shuttingDown)
|
|
87
|
+
return;
|
|
88
|
+
logger.info("transport closed — exiting");
|
|
89
|
+
process.exit(0);
|
|
90
|
+
};
|
|
91
|
+
server.onerror = (error) => {
|
|
92
|
+
logger.error("transport error", { error: describe(error) });
|
|
93
|
+
};
|
|
94
|
+
// Node terminates on an unhandled rejection by default, with a raw stack and
|
|
95
|
+
// no explanation of which server it came from. Log it in our own format, then
|
|
96
|
+
// exit non-zero so a supervisor restarts rather than silently continuing.
|
|
97
|
+
process.on("unhandledRejection", (reason) => {
|
|
98
|
+
logger.error("unhandled promise rejection", { error: describe(reason) });
|
|
99
|
+
process.exit(1);
|
|
100
|
+
});
|
|
101
|
+
process.on("uncaughtException", (error) => {
|
|
102
|
+
logger.error("uncaught exception", { error: describe(error) });
|
|
22
103
|
process.exit(1);
|
|
23
|
-
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
async function main() {
|
|
107
|
+
const server = createHydraDBServer();
|
|
108
|
+
installLifecycle(server);
|
|
109
|
+
const transport = new StdioServerTransport();
|
|
110
|
+
await server.connect(transport);
|
|
24
111
|
}
|
|
25
112
|
main().catch((error) => {
|
|
26
113
|
console.error("Fatal error running server:", error);
|