@odatano/nightgate-mcp 0.5.1 → 0.7.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/client.js CHANGED
@@ -2,13 +2,37 @@
2
2
  export class NightgateApiError extends Error {
3
3
  status;
4
4
  code;
5
- constructor(status, code, message) {
5
+ detail;
6
+ constructor(status, code, message,
7
+ /** Extra fields of a gateway error body (unitsLeft, price, topup, products, validUntil, retryAfterSeconds). */
8
+ detail) {
6
9
  super(message);
7
10
  this.status = status;
8
11
  this.code = code;
12
+ this.detail = detail;
9
13
  this.name = 'NightgateApiError';
10
14
  }
11
15
  }
16
+ /**
17
+ * Two error shapes reach the client: CAP's `{ error: { code, message } }`
18
+ * from NIGHTGATE itself, and the ODATANO ACCESS gateway's `{ error: "<text>",
19
+ * ...detail }` (401 key problems, 402 units exhausted with a top-up hint,
20
+ * 403 closed action or product not on the key, 429 with Retry-After).
21
+ */
22
+ export function apiError(status, payload, headers, fallback) {
23
+ const body = (payload && typeof payload === 'object' ? payload : {});
24
+ const err = body.error;
25
+ if (typeof err === 'string') {
26
+ const { error: _e, ...rest } = body;
27
+ const detail = { ...rest };
28
+ const retry = headers.get('retry-after');
29
+ if (retry)
30
+ detail.retryAfterSeconds = Number(retry);
31
+ return new NightgateApiError(status, undefined, err, Object.keys(detail).length ? detail : undefined);
32
+ }
33
+ const e = (err ?? {});
34
+ return new NightgateApiError(status, e.code, e.message ?? fallback);
35
+ }
12
36
  export function int64(value) {
13
37
  const digits = String(value);
14
38
  if (!/^\d+$/.test(digits))
@@ -83,8 +107,7 @@ export class NightgateClient {
83
107
  payload = { raw: text };
84
108
  }
85
109
  if (!response.ok) {
86
- const err = payload.error;
87
- throw new NightgateApiError(response.status, err?.code, err?.message ?? `NIGHTGATE request failed with HTTP ${response.status}`);
110
+ throw apiError(response.status, payload, response.headers, `NIGHTGATE request failed with HTTP ${response.status}`);
88
111
  }
89
112
  return stripODataNoise(payload);
90
113
  }
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAEA,sEAAsE;AACtE,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IAExB;IACA;IAGA;IALlB,YACkB,MAAc,EACd,IAAwB,EACxC,OAAe;IACf,+GAA+G;IAC/F,MAAgC;QAEhD,KAAK,CAAC,OAAO,CAAC,CAAC;QANC,WAAM,GAAN,MAAM,CAAQ;QACd,SAAI,GAAJ,IAAI,CAAoB;QAGxB,WAAM,GAAN,MAAM,CAA0B;QAGhD,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,MAAc,EAAE,OAAgB,EAAE,OAAgB,EAAE,QAAgB;IAC3F,MAAM,IAAI,GAAG,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAA4B,CAAC;IAChG,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC;IACvB,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC;QACpC,MAAM,MAAM,GAA4B,EAAE,GAAG,IAAI,EAAE,CAAC;QACpD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QACzC,IAAI,KAAK;YAAE,MAAM,CAAC,iBAAiB,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QACpD,OAAO,IAAI,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACxG,CAAC;IACD,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,CAAwC,CAAC;IAC7D,OAAO,IAAI,iBAAiB,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,IAAI,QAAQ,CAAC,CAAC;AACtE,CAAC;AAUD,MAAM,UAAU,KAAK,CAAC,KAAsB;IAC1C,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC7B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,KAAK,EAAE,CAAC,CAAC;IACnF,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC5B,CAAC;AAED;;;;GAIG;AACH,MAAM,OAAO,eAAe;IACG;IAA7B,YAA6B,MAA0B;QAA1B,WAAM,GAAN,MAAM,CAAoB;IAAG,CAAC;IAE3D,6EAA6E;IAC7E,KAAK,CAAC,YAAY,CAAC,IAAY,EAAE,MAAkE;QACjG,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAClD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE;gBAAE,SAAS;YACpE,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,IAAI,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC9C,CAAC;QACD,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;QAC3F,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAClC,CAAC;IAED,uEAAuE;IACvE,KAAK,CAAC,UAAU,CAAC,IAAY,EAAE,MAA+B;QAC5D,MAAM,IAAI,GAA4B,EAAE,CAAC;QACzC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAClD,IAAI,KAAK,KAAK,SAAS;gBAAE,SAAS;YAClC,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACpB,CAAC;QACD,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,IAAI,EAAE,CAAC;QACvE,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IACzC,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,MAAsB,EAAE,GAAW,EAAE,IAAc;QACvE,MAAM,OAAO,GAA2B,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC;QACvE,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3C,wEAAwE;YACxE,uEAAuE;YACvE,oEAAoE;YACpE,OAAO,CAAC,eAAe,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;YAC7C,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;gBACzB,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;gBACtG,OAAO,CAAC,aAAa,GAAG,SAAS,KAAK,EAAE,CAAC;YAC3C,CAAC;QACH,CAAC;aAAM,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YAC7B,OAAO,CAAC,aAAa,GAAG,UAAU,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACxD,CAAC;aAAM,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YAChC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YACtG,OAAO,CAAC,aAAa,GAAG,SAAS,KAAK,EAAE,CAAC;QAC3C,CAAC;QACD,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;QAErE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAChC,MAAM;YACN,OAAO;YACP,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;YAC3D,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC;SACnD,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,IAAI,OAAgB,CAAC;QACrB,IAAI,CAAC;YACH,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACzC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;QAC1B,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,sCAAsC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QACtH,CAAC;QACD,OAAO,eAAe,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC;CACF;AAED,iFAAiF;AACjF,SAAS,YAAY,CAAC,KAAqC;IACzD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACpD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC,MAAM,CAAC;IACnD,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC;AAC1C,CAAC;AAED,4EAA4E;AAC5E,SAAS,eAAe,CAAC,OAAgB;IACvC,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAC;IAC9F,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAkC,CAAC,EAAE,CAAC;QAC9E,IAAI,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,SAAS;QACvC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACnB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
package/dist/config.d.ts CHANGED
@@ -1,18 +1,22 @@
1
1
  /**
2
- * Server configuration, environment-driven so the same binary works for
3
- * local dev (basic auth against a `cds watch` instance) and for a deployed
4
- * NIGHTGATE behind a reverse proxy (bearer token, phase B agent grants).
2
+ * Server configuration, environment-driven. The connection variables are the
3
+ * same for every ODATANO MCP server (ODATANO_ACCESS_*): one key from
4
+ * https://api.odatano.dev configures the Midnight and the Cardano server alike.
5
+ * A direct NIGHTGATE instance (local `cds watch`, an own deployment) is reached
6
+ * by pointing ODATANO_ACCESS_URL at it.
5
7
  */
6
8
  export interface NightgateMcpConfig {
7
- /** Base URL of the NIGHTGATE host app, e.g. http://localhost:4004 */
9
+ /** Base URL: the ODATANO ACCESS gateway (default https://api.odatano.dev) or a direct NIGHTGATE host app. */
8
10
  baseUrl: string;
9
11
  /**
10
- * Token credential. An `ngat_...` value is a NIGHTGATE agent-grant token
11
- * (sent as `x-agent-token`, optionally alongside basic transport auth);
12
- * anything else is sent as a plain `Authorization: Bearer` header.
12
+ * ODATANO_ACCESS_KEY. The usual value is an ODATANO ACCESS key (`oda_...`),
13
+ * sent as `Authorization: Bearer`; the gateway swaps in the agent grant.
14
+ * Against a direct NIGHTGATE instance an `ngat_...` agent-grant token goes
15
+ * as `x-agent-token` (optionally alongside basic transport auth); anything
16
+ * else is a plain bearer.
13
17
  */
14
18
  token?: string;
15
- /** Basic-auth credentials for dev/mocked CAP auth. */
19
+ /** ODATANO_ACCESS_USER / _PASSWORD: basic auth for a direct instance (CAP dev/mocked auth); never for agents. */
16
20
  username?: string;
17
21
  password?: string;
18
22
  /** OData service path of the main Nightgate service. */
package/dist/config.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export function loadConfig(env = process.env) {
2
- const baseUrl = (env.NIGHTGATE_BASE_URL ?? 'http://localhost:4004').replace(/\/+$/, '');
2
+ const baseUrl = (env.ODATANO_ACCESS_URL || 'https://api.odatano.dev').replace(/\/+$/, '');
3
3
  const timeoutMs = Number(env.NIGHTGATE_TIMEOUT_MS ?? 30000);
4
4
  if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
5
5
  throw new Error(`Invalid NIGHTGATE_TIMEOUT_MS: ${env.NIGHTGATE_TIMEOUT_MS}`);
@@ -14,9 +14,9 @@ export function loadConfig(env = process.env) {
14
14
  }
15
15
  return {
16
16
  baseUrl,
17
- token: env.NIGHTGATE_TOKEN || undefined,
18
- username: env.NIGHTGATE_USERNAME || undefined,
19
- password: env.NIGHTGATE_PASSWORD || undefined,
17
+ token: env.ODATANO_ACCESS_KEY || undefined,
18
+ username: env.ODATANO_ACCESS_USER || undefined,
19
+ password: env.ODATANO_ACCESS_PASSWORD || undefined,
20
20
  servicePath: env.NIGHTGATE_SERVICE_PATH ?? '/api/v1/nightgate',
21
21
  timeoutMs,
22
22
  seedHex,
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAsCA,MAAM,UAAU,UAAU,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC7D,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,kBAAkB,IAAI,uBAAuB,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACxF,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,oBAAoB,IAAI,KAAK,CAAC,CAAC;IAC5D,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CAAC,iCAAiC,GAAG,CAAC,oBAAoB,EAAE,CAAC,CAAC;IAC/E,CAAC;IACD,MAAM,OAAO,GAAG,GAAG,CAAC,iBAAiB,IAAI,SAAS,CAAC;IACnD,IAAI,CAAC,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACzD,MAAM,IAAI,KAAK,CAAC,8BAA8B,OAAO,gCAAgC,CAAC,CAAC;IACzF,CAAC;IACD,MAAM,OAAO,GAAG,GAAG,CAAC,kBAAkB,IAAI,SAAS,CAAC;IACpD,IAAI,OAAO,IAAI,CAAC,sCAAsC,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACrE,MAAM,IAAI,KAAK,CAAC,+DAA+D,CAAC,CAAC;IACnF,CAAC;IACD,OAAO;QACL,OAAO;QACP,KAAK,EAAE,GAAG,CAAC,eAAe,IAAI,SAAS;QACvC,QAAQ,EAAE,GAAG,CAAC,kBAAkB,IAAI,SAAS;QAC7C,QAAQ,EAAE,GAAG,CAAC,kBAAkB,IAAI,SAAS;QAC7C,WAAW,EAAE,GAAG,CAAC,sBAAsB,IAAI,mBAAmB;QAC9D,SAAS;QACT,OAAO;QACP,OAAO;QACP,cAAc,EAAE,GAAG,CAAC,0BAA0B,IAAI,SAAS;QAC3D,YAAY,EAAE,GAAG,CAAC,wBAAwB,IAAI,SAAS;QACvD,OAAO,EAAE,GAAG,CAAC,kBAAkB,IAAI,SAAS;QAC5C,eAAe,EAAE,GAAG,CAAC,4BAA4B,IAAI,SAAS;QAC9D,UAAU,EAAE,GAAG,CAAC,sBAAsB,IAAI,SAAS;QACnD,cAAc,EAAE,GAAG,CAAC,0BAA0B,IAAI,SAAS;KAC5D,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AA0CA,MAAM,UAAU,UAAU,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC7D,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,kBAAkB,IAAI,yBAAyB,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC1F,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,oBAAoB,IAAI,KAAK,CAAC,CAAC;IAC5D,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CAAC,iCAAiC,GAAG,CAAC,oBAAoB,EAAE,CAAC,CAAC;IAC/E,CAAC;IACD,MAAM,OAAO,GAAG,GAAG,CAAC,iBAAiB,IAAI,SAAS,CAAC;IACnD,IAAI,CAAC,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACzD,MAAM,IAAI,KAAK,CAAC,8BAA8B,OAAO,gCAAgC,CAAC,CAAC;IACzF,CAAC;IACD,MAAM,OAAO,GAAG,GAAG,CAAC,kBAAkB,IAAI,SAAS,CAAC;IACpD,IAAI,OAAO,IAAI,CAAC,sCAAsC,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACrE,MAAM,IAAI,KAAK,CAAC,+DAA+D,CAAC,CAAC;IACnF,CAAC;IACD,OAAO;QACL,OAAO;QACP,KAAK,EAAE,GAAG,CAAC,kBAAkB,IAAI,SAAS;QAC1C,QAAQ,EAAE,GAAG,CAAC,mBAAmB,IAAI,SAAS;QAC9C,QAAQ,EAAE,GAAG,CAAC,uBAAuB,IAAI,SAAS;QAClD,WAAW,EAAE,GAAG,CAAC,sBAAsB,IAAI,mBAAmB;QAC9D,SAAS;QACT,OAAO;QACP,OAAO;QACP,cAAc,EAAE,GAAG,CAAC,0BAA0B,IAAI,SAAS;QAC3D,YAAY,EAAE,GAAG,CAAC,wBAAwB,IAAI,SAAS;QACvD,OAAO,EAAE,GAAG,CAAC,kBAAkB,IAAI,SAAS;QAC5C,eAAe,EAAE,GAAG,CAAC,4BAA4B,IAAI,SAAS;QAC9D,UAAU,EAAE,GAAG,CAAC,sBAAsB,IAAI,SAAS;QACnD,cAAc,EAAE,GAAG,CAAC,0BAA0B,IAAI,SAAS;KAC5D,CAAC;AACJ,CAAC"}
package/dist/index.js CHANGED
@@ -6,6 +6,9 @@ async function main() {
6
6
  const config = loadConfig();
7
7
  const server = buildServer(config);
8
8
  // stdio transport: stdout is the protocol channel, diagnostics go to stderr
9
+ if (!config.token && !config.username && /api.odatano.dev$/.test(config.baseUrl)) {
10
+ console.error('nightgate-mcp: ODATANO_ACCESS_KEY is not set; api.odatano.dev answers 401 without a key (sign in, redeem a code or buy a pack at https://api.odatano.dev)');
11
+ }
9
12
  console.error(`nightgate-mcp: connecting tools to ${config.baseUrl}${config.servicePath}`);
10
13
  await server.connect(new StdioServerTransport());
11
14
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IACnC,4EAA4E;IAC5E,OAAO,CAAC,KAAK,CAAC,sCAAsC,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;IAC3F,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC;AACnD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,gCAAgC,EAAE,GAAG,CAAC,CAAC;IACrD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IACnC,4EAA4E;IAC5E,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QACjF,OAAO,CAAC,KAAK,CAAC,2JAA2J,CAAC,CAAC;IAC7K,CAAC;IACD,OAAO,CAAC,KAAK,CAAC,sCAAsC,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;IAC3F,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC;AACnD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,gCAAgC,EAAE,GAAG,CAAC,CAAC;IACrD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
package/dist/server.js CHANGED
@@ -3,7 +3,7 @@ import { NightgateClient } from './client.js';
3
3
  import { registerTools } from './tools.js';
4
4
  /** Build the MCP server with all tools registered; transport is the caller's choice. */
5
5
  export function buildServer(config) {
6
- const server = new McpServer({ name: 'nightgate', version: '0.5.0' });
6
+ const server = new McpServer({ name: 'nightgate', version: '0.7.0' });
7
7
  registerTools(server, new NightgateClient(config), config);
8
8
  return server;
9
9
  }
package/dist/tools.js CHANGED
@@ -122,6 +122,7 @@ const membershipClaim = z.object({
122
122
  const documentIntegrityClaim = z.object({
123
123
  predicate: z.literal('documentIntegrity'),
124
124
  payloadHashB: hex64('payloadHashB').describe('The second document (A is the batch payloadHash)'),
125
+ attesterIdB: hex64('attesterIdB').optional().describe('Document B\'s attester; default the batch attester'),
125
126
  allowedMask: z.number().int().min(0).max(MAX_MASK - 1)
126
127
  .describe('Packed slot mask, one bit per slot of the vault width (16 bits by default, 32 on attestation-vault-32), bit i = slot i MAY differ. Must leave at least one real schema slot constrained, otherwise the claim is vacuous and rejected'),
127
128
  schema: schemaSlots,
@@ -135,6 +136,7 @@ const documentIntegrityClaim = z.object({
135
136
  const documentDiffClaim = z.object({
136
137
  predicate: z.literal('documentDiff'),
137
138
  payloadHashB: hex64('payloadHashB').describe('The second document (A is the batch payloadHash)'),
139
+ attesterIdB: hex64('attesterIdB').optional().describe('Document B\'s attester; default the batch attester'),
138
140
  k: z.number().int().min(1).max(MAX_SLOT_WIDTH).describe('Minimum number of differing slots to prove, up to the vault width'),
139
141
  schema: schemaSlots,
140
142
  openingA: documentOpening,
@@ -148,12 +150,21 @@ const documentDiffClaim = z.object({
148
150
  export function registerTools(server, client, config) {
149
151
  const run = wrapHandler(client);
150
152
  server.registerTool('verify_attestation', {
151
- description: 'Verify against LIVE Midnight contract state that a payload hash is attested in an ' +
152
- 'AttestationVault (crawler-free, no txHash needed). Optionally also checks that the ' +
153
- 'anchored content root matches. Returns verified:false (not an error) when absent.',
153
+ description: 'Verify against LIVE Midnight contract state that an attester\'s record of a payload ' +
154
+ 'hash stands in an AttestationVault (crawler-free, no txHash needed). A record is ONE ' +
155
+ 'attester\'s attestation of ONE payload (ledger key recordKey(attesterId, payloadHash)): ' +
156
+ 'name it by attesterId + payloadHash, or by a bound documentId, which resolves to exactly ' +
157
+ 'one record and reveals its attester. Optionally also checks that the anchored content ' +
158
+ 'root / schema id match. Returns verified:false (not an error) when absent; the result ' +
159
+ 'carries attesterId, payloadHash, recordKey and documentId.',
154
160
  inputSchema: {
155
161
  contractAddress: z.string().min(1).describe('AttestationVault contract address'),
156
- payloadHash: hex64('payloadHash').describe('The attested payload hash (sha256, 64 hex)'),
162
+ attesterId: hex64('attesterId').optional()
163
+ .describe('The attester whose record to check (64 hex; get_attester_identity for this server\'s own). Required unless documentId is given'),
164
+ payloadHash: hex64('payloadHash').optional()
165
+ .describe('The attested payload hash (sha256, 64 hex). Required with attesterId; next to documentId it must match the bound record'),
166
+ documentId: hex64('documentId').optional()
167
+ .describe('A bound document id (64 hex): resolves the record through the vault\'s document bindings'),
157
168
  contentRoot: hex64('contentRoot').optional().describe('Optional anchored content root to check (64 hex)'),
158
169
  schemaId: hex64('schemaId').optional()
159
170
  .describe('Optional anchored schema id to check; the result reports schemaOk, so an examiner can pin the canonical field list'),
@@ -161,23 +172,32 @@ export function registerTools(server, client, config) {
161
172
  .describe('Read from another network public indexer instead of the configured one'),
162
173
  compiledArtifactRef: z.string().optional().describe("Contract artifact ref, defaults to 'attestation-vault'"),
163
174
  },
164
- }, run(async (args) => client.callFunction('verifyAttestationState', {
165
- contractAddress: args.contractAddress,
166
- payloadHash: args.payloadHash,
167
- contentRoot: args.contentRoot,
168
- schemaId: args.schemaId,
169
- compiledArtifactRef: args.compiledArtifactRef,
170
- network: args.network,
171
- })));
175
+ }, run(async (args) => {
176
+ if (!args.documentId && !(args.attesterId && args.payloadHash)) {
177
+ throw new Error('name the record: attesterId + payloadHash, or documentId');
178
+ }
179
+ return client.callFunction('verifyAttestationState', {
180
+ contractAddress: args.contractAddress,
181
+ attesterId: args.attesterId,
182
+ payloadHash: args.payloadHash,
183
+ documentId: args.documentId,
184
+ contentRoot: args.contentRoot,
185
+ schemaId: args.schemaId,
186
+ compiledArtifactRef: args.compiledArtifactRef,
187
+ network: args.network,
188
+ });
189
+ }));
172
190
  server.registerTool('verify_predicate', {
173
191
  description: 'Verify against LIVE Midnight contract state that a ZK claim was recorded true on-chain. ' +
174
192
  'Id-free: works for proofs NIGHTGATE never saw. Numeric predicates (lessOrEqual / ' +
175
193
  'greaterOrEqual) need threshold (the SAME scaled integer the circuit hashed; a scaling ' +
176
194
  'mismatch yields verified:false) and optionally fieldKey for field-bound proofs. ' +
177
195
  'bytesEquality needs fieldKey + expectedDigest; setMembership needs fieldKey + setRoot ' +
178
- '(recompute it from the published list via prepare_membership_set).',
196
+ '(recompute it from the published list via prepare_membership_set). Every claim is bound ' +
197
+ 'to ONE attester\'s record of the document, so attesterId is part of the coordinates.',
179
198
  inputSchema: {
180
199
  contractAddress: z.string().min(1).describe('AttestationVault contract address'),
200
+ attesterId: hex64('attesterId').describe('The attester whose record of payloadHash carries the claim (64 hex)'),
181
201
  payloadHash: hex64('payloadHash').describe('The attestation payload hash (64 hex)'),
182
202
  predicate: z.enum(['lessOrEqual', 'greaterOrEqual', 'bytesEquality', 'setMembership',
183
203
  'documentIntegrity', 'documentDiff']).describe('Claim kind'),
@@ -191,6 +211,8 @@ export function registerTools(server, client, config) {
191
211
  .describe("setMembership only: the canonical allow-list set root"),
192
212
  payloadHashB: hex64('payloadHashB').optional()
193
213
  .describe('Cross-root kinds: the second document. (A, B) order is part of the claim, query it as proven'),
214
+ attesterIdB: hex64('attesterIdB').optional()
215
+ .describe('Cross-root kinds: document B\'s attester; defaults to attesterId'),
194
216
  allowedMask: z.number().int().min(0).max(MAX_MASK).optional()
195
217
  .describe('documentIntegrity only: the packed slot mask that was proven (16 bits by default, 32 on attestation-vault-32)'),
196
218
  k: z.number().int().min(1).max(MAX_SLOT_WIDTH).optional()
@@ -217,7 +239,9 @@ export function registerTools(server, client, config) {
217
239
  }
218
240
  return client.callFunction('verifyPredicateState', {
219
241
  contractAddress: args.contractAddress,
242
+ attesterId: args.attesterId,
220
243
  payloadHash: args.payloadHash,
244
+ attesterIdB: args.attesterIdB,
221
245
  fieldKey: args.fieldKey,
222
246
  predicate: args.predicate,
223
247
  threshold: args.threshold === undefined ? undefined : int64(args.threshold),
@@ -328,12 +352,11 @@ export function registerTools(server, client, config) {
328
352
  })));
329
353
  server.registerTool('anchor_document', {
330
354
  description: 'Anchor a document content hash on the Midnight chain via the AttestationVault attest ' +
331
- 'circuit. Commits only the sha256 + public metadata; you are responsible for storing the ' +
332
- 'actual bytes at storageRef. Plain attestation is first-come-first-served per hash, so a ' +
333
- 'mempool observer can front-run a visible hash: for a hash that is secret until anchoring, ' +
334
- 'use prepare_anchor_commitment + commit_document_anchor first and pass the nonce here, ' +
335
- 'which turns this call into the guarded REVEAL and reclaims a front-run hash. ' + POLL_HINT +
336
- ' Also returns documentId for verify_document.',
355
+ 'circuit (one transaction). Commits only the sha256 + public metadata; you are responsible ' +
356
+ 'for storing the actual bytes at storageRef. The on-chain record is keyed by the session\'s ' +
357
+ 'attester id AND the hash, so nobody can pre-empt or take over it; the same hash anchored ' +
358
+ 'by another identity is a separate record. ' + POLL_HINT +
359
+ ' Also returns documentId for verify_document and attesterId, which verifiers need next to the hash.',
337
360
  inputSchema: {
338
361
  sha256: hex64('sha256').describe('sha256 of the document content (64 hex), becomes the on-chain payload hash'),
339
362
  storageRef: z.string().min(1).describe('Where the bytes live, e.g. file://, s3://, ipfs://'),
@@ -342,8 +365,6 @@ export function registerTools(server, client, config) {
342
365
  contentType: z.string().optional().describe('MIME type, informational'),
343
366
  size: z.number().int().nonnegative().optional().describe('Content size in bytes, informational'),
344
367
  metadata: z.record(z.unknown()).optional().describe('Public metadata object; its hash is anchored alongside'),
345
- nonce: hex64('nonce').optional()
346
- .describe('Guarded REVEAL: the secret nonce from prepare_anchor_commitment, after commit_document_anchor finalized. Same sha256 and metadata as the commitment'),
347
368
  compiledArtifactRef: z.string().optional().describe("Contract artifact ref, defaults to 'attestation-vault'"),
348
369
  idempotencyKey: z.string().optional().describe('Dedupes retries of the same anchor request'),
349
370
  sponsorSessionId: z.string().uuid().optional().describe('Optional second session that pays the dust fee'),
@@ -354,7 +375,6 @@ export function registerTools(server, client, config) {
354
375
  size: args.size,
355
376
  storageRef: args.storageRef,
356
377
  metadata: args.metadata === undefined ? undefined : JSON.stringify(args.metadata),
357
- nonce: args.nonce,
358
378
  sessionId: args.sessionId,
359
379
  contractAddress: args.contractAddress,
360
380
  compiledArtifactRef: args.compiledArtifactRef,
@@ -370,6 +390,8 @@ export function registerTools(server, client, config) {
370
390
  'at local proving time, nothing is submitted. ' + POLL_HINT,
371
391
  inputSchema: {
372
392
  payloadHash: hex64('payloadHash').describe('Attestation payload hash (64 hex)'),
393
+ attesterId: hex64('attesterId').optional()
394
+ .describe('The attester whose record of payloadHash the claim is bound to (64 hex); default the session\'s own. A contentRoot can only be anchored under the session\'s own record'),
373
395
  fieldKey: hex64('fieldKey').describe('Canonical field id (64 hex, public)'),
374
396
  value: z.string().regex(/^\d+$/, 'value must be a non-negative integer (decimal string)')
375
397
  .describe('Scaled integer field value (witness only, never persisted)'),
@@ -389,6 +411,7 @@ export function registerTools(server, client, config) {
389
411
  },
390
412
  }, run(async (args) => client.callAction('issueFieldPredicateAttestation', {
391
413
  payloadHash: args.payloadHash,
414
+ attesterId: args.attesterId,
392
415
  fieldKey: args.fieldKey,
393
416
  value: args.value,
394
417
  fieldSalt: args.fieldSalt,
@@ -441,6 +464,8 @@ export function registerTools(server, client, config) {
441
464
  'supplied it is anchored first. ' + POLL_HINT,
442
465
  inputSchema: {
443
466
  payloadHash: hex64('payloadHash').describe('Attestation payload hash (64 hex)'),
467
+ attesterId: hex64('attesterId').optional()
468
+ .describe('The attester whose record of payloadHash the claim is bound to (64 hex); default the session\'s own. A contentRoot can only be anchored under the session\'s own record'),
444
469
  fieldKey: hex64('fieldKey').describe('Canonical field id (64 hex, public)'),
445
470
  expectedValue: z.string().min(1).optional()
446
471
  .describe('Raw expected string (the server digests the EXACT string; pass this OR expectedDigest)'),
@@ -463,6 +488,7 @@ export function registerTools(server, client, config) {
463
488
  }
464
489
  return client.callAction('issueFieldEqualityAttestation', {
465
490
  payloadHash: args.payloadHash,
491
+ attesterId: args.attesterId,
466
492
  fieldKey: args.fieldKey,
467
493
  expectedValue: args.expectedValue,
468
494
  expectedDigest: args.expectedDigest,
@@ -488,6 +514,8 @@ export function registerTools(server, client, config) {
488
514
  'contentRoot is supplied it is anchored first. ' + POLL_HINT,
489
515
  inputSchema: {
490
516
  payloadHash: hex64('payloadHash').describe('Attestation payload hash (64 hex)'),
517
+ attesterId: hex64('attesterId').optional()
518
+ .describe('The attester whose record of payloadHash the claim is bound to (64 hex); default the session\'s own. A contentRoot can only be anchored under the session\'s own record'),
491
519
  fieldKey: hex64('fieldKey').describe('Canonical field id (64 hex, public)'),
492
520
  value: z.string().min(1).optional()
493
521
  .describe('Raw member string (witness only; pass this OR valueDigest)'),
@@ -522,6 +550,7 @@ export function registerTools(server, client, config) {
522
550
  }
523
551
  return client.callAction('issueFieldMembershipAttestation', {
524
552
  payloadHash: args.payloadHash,
553
+ attesterId: args.attesterId,
525
554
  fieldKey: args.fieldKey,
526
555
  value: args.value,
527
556
  valueDigest: args.valueDigest,
@@ -552,6 +581,8 @@ export function registerTools(server, client, config) {
552
581
  'assuming all-or-nothing. ' + POLL_HINT,
553
582
  inputSchema: {
554
583
  payloadHash: hex64('payloadHash').describe('Shared attestation payload hash; also document A of any cross-root claim (64 hex)'),
584
+ attesterId: hex64('attesterId').optional()
585
+ .describe('The attester whose record of payloadHash the claim is bound to (64 hex); default the session\'s own. A contentRoot can only be anchored under the session\'s own record'),
555
586
  claims: z.array(z.union([numericClaim, equalityClaim, membershipClaim, documentIntegrityClaim, documentDiffClaim]))
556
587
  .min(1).max(8)
557
588
  .describe('1-8 claims on the same payload hash; any mix of numeric, bytesEquality, setMembership, documentIntegrity and documentDiff'),
@@ -569,6 +600,7 @@ export function registerTools(server, client, config) {
569
600
  }
570
601
  return client.callAction('issueFieldPredicateAttestationBatch', {
571
602
  payloadHash: args.payloadHash,
603
+ attesterId: args.attesterId,
572
604
  contentRoot: args.contentRoot,
573
605
  schemaId: args.schemaId,
574
606
  claimsJson: JSON.stringify(args.claims.map((c) => 'threshold' in c ? { ...c, threshold: String(c.threshold) } : c)),
@@ -590,6 +622,10 @@ export function registerTools(server, client, config) {
590
622
  inputSchema: {
591
623
  payloadHashA: hex64('payloadHashA').describe('Anchored document A (64 hex)'),
592
624
  payloadHashB: hex64('payloadHashB').describe('Anchored document B; must differ from A'),
625
+ attesterIdA: hex64('attesterIdA').optional()
626
+ .describe('Document A\'s attester (64 hex); default the session\'s own'),
627
+ attesterIdB: hex64('attesterIdB').optional()
628
+ .describe('Document B\'s attester (64 hex); default attesterIdA'),
593
629
  allowedMask: z.number().int().min(0).max(MAX_MASK - 1)
594
630
  .describe('Packed slot mask, one bit per slot of the vault width (16 bits by default, 32 on attestation-vault-32), bit i = slot i MAY differ. At least one real schema slot must stay constrained'),
595
631
  schema: schemaSlots,
@@ -610,6 +646,8 @@ export function registerTools(server, client, config) {
610
646
  return client.callAction('issueDocumentIntegrityAttestation', {
611
647
  payloadHashA: args.payloadHashA,
612
648
  payloadHashB: args.payloadHashB,
649
+ attesterIdA: args.attesterIdA,
650
+ attesterIdB: args.attesterIdB,
613
651
  allowedMask: args.allowedMask,
614
652
  schemaJson: JSON.stringify(args.schema),
615
653
  openingAJson: JSON.stringify(args.openingA),
@@ -635,6 +673,10 @@ export function registerTools(server, client, config) {
635
673
  inputSchema: {
636
674
  payloadHashA: hex64('payloadHashA').describe('Anchored document A (64 hex)'),
637
675
  payloadHashB: hex64('payloadHashB').describe('Anchored document B; must differ from A'),
676
+ attesterIdA: hex64('attesterIdA').optional()
677
+ .describe('Document A\'s attester (64 hex); default the session\'s own'),
678
+ attesterIdB: hex64('attesterIdB').optional()
679
+ .describe('Document B\'s attester (64 hex); default attesterIdA'),
638
680
  k: z.number().int().min(1).max(MAX_SLOT_WIDTH).describe('Minimum number of differing slots to prove, up to the vault width'),
639
681
  schema: schemaSlots,
640
682
  openingA: documentOpening,
@@ -651,6 +693,8 @@ export function registerTools(server, client, config) {
651
693
  }, run(async (args) => client.callAction('issueDocumentDiffAttestation', {
652
694
  payloadHashA: args.payloadHashA,
653
695
  payloadHashB: args.payloadHashB,
696
+ attesterIdA: args.attesterIdA,
697
+ attesterIdB: args.attesterIdB,
654
698
  k: args.k,
655
699
  schemaJson: JSON.stringify(args.schema),
656
700
  openingAJson: JSON.stringify(args.openingA),
@@ -664,46 +708,6 @@ export function registerTools(server, client, config) {
664
708
  idempotencyKey: args.idempotencyKey,
665
709
  sponsorSessionId: args.sponsorSessionId,
666
710
  })));
667
- server.registerTool('prepare_anchor_commitment', {
668
- description: 'Phase 0 of guarded anchoring: compute the opaque commitment for commit_document_anchor ' +
669
- 'plus the nonce the later reveal needs. Compute-only and synchronous. STORE the nonce and ' +
670
- 'keep it SECRET until the reveal: it is exactly what a mempool front-runner cannot forge. ' +
671
- 'Pass the same metadata here and to anchor_document. Use this when the payload hash is ' +
672
- 'secret until anchoring; for publicly known identifiers, registrar pre-assignment is the ' +
673
- 'better protection.',
674
- inputSchema: {
675
- sha256: hex64('sha256').describe('sha256 of the document content (64 hex)'),
676
- metadata: z.record(z.unknown()).optional()
677
- .describe('Public metadata object; MUST equal the metadata passed to anchor_document later'),
678
- nonce: hex64('nonce').optional().describe('Reuse a specific nonce; omit for a fresh random one'),
679
- },
680
- }, run(async (args) => client.callAction('prepareAnchorCommitment', {
681
- sha256: args.sha256,
682
- metadata: args.metadata === undefined ? undefined : JSON.stringify(args.metadata),
683
- nonce: args.nonce,
684
- })));
685
- server.registerTool('commit_document_anchor', {
686
- description: 'Phase 1 of guarded anchoring: record the opaque commitment on-chain. Observers learn ' +
687
- 'nothing about the payload. Once this job finalizes, call anchor_document with the SAME ' +
688
- 'sha256 and metadata plus the nonce to reveal; a plain attest that front-ran the reveal is ' +
689
- 'taken over in-circuit, and everything the front-runner recorded meanwhile (content root, ' +
690
- 'disclosure grants, claims) stops counting. ' + POLL_HINT,
691
- inputSchema: {
692
- commitment: hex64('commitment').describe('The commitment from prepare_anchor_commitment (64 hex)'),
693
- sessionId: z.string().uuid().describe('Wallet session id that signs and submits; the SAME session must reveal'),
694
- contractAddress: z.string().min(1).describe('AttestationVault deployment'),
695
- compiledArtifactRef: z.string().optional().describe("Contract artifact ref, defaults to 'attestation-vault'"),
696
- idempotencyKey: z.string().optional().describe('Dedupes retries'),
697
- sponsorSessionId: z.string().uuid().optional().describe('Optional second session that pays the dust fee'),
698
- },
699
- }, run(async (args) => client.callAction('commitDocumentAnchor', {
700
- commitment: args.commitment,
701
- sessionId: args.sessionId,
702
- contractAddress: args.contractAddress,
703
- compiledArtifactRef: args.compiledArtifactRef,
704
- idempotencyKey: args.idempotencyKey,
705
- sponsorSessionId: args.sponsorSessionId,
706
- })));
707
711
  server.registerTool('grant_disclosure', {
708
712
  description: 'Grant a disclosure level for an attestation to a grantee identity, on-chain via the ' +
709
713
  'AttestationVault. Attester-only: the transaction is rejected in-circuit unless the ' +
@@ -764,16 +768,19 @@ export function registerTools(server, client, config) {
764
768
  'server. params by call: attest {payloadHash, metadataHash}; anchorContentRoot ' +
765
769
  '{payloadHash, contentRoot, schemaId}; grantDisclosure {payloadHash, grantee, level 0|1|2}; ' +
766
770
  'revokeDisclosure {payloadHash, grantee}; registerPassport {passportId, ownerId}; ' +
767
- 'bindPassport {passportId, payloadHash}; attestCommit {commitment}; attestReveal ' +
768
- '{payloadHash, metadataHash, nonce}. ZK CLAIMS, proven here with no wallet on the ' +
769
- 'server and no witness ever sent to it: proveFieldPredicate {payloadHash, fieldKey, ' +
770
- 'threshold, op 0=lessOrEqual|1=greaterOrEqual} plus merkleProof {fieldValue, fieldSalt, ' +
771
- 'siblings, dirs}; proveFieldEquality {payloadHash, fieldKey, expectedDigest} plus ' +
772
- 'merkleProof {fieldSalt, siblings, dirs}; proveFieldMembership {payloadHash, fieldKey, ' +
773
- 'setRoot} plus merkleProof {fieldDigest, fieldSalt, siblings, dirs, setProof}; ' +
774
- 'proveFieldsUnchangedExcept {payloadHashA, payloadHashB, allowedMask} and ' +
775
- 'proveFieldsDiffer {payloadHashA, payloadHashB, k}, both plus docPair {schema, ' +
776
- 'openingA, openingB}. Every witness field comes straight out of ' +
771
+ 'bindPassport {passportId, payloadHash}. The owner-gated calls address THIS builder\'s own ' +
772
+ 'record of the payload (the circuit derives recordKey(attesterId, payloadHash) from the ' +
773
+ 'caller). ZK CLAIMS, proven here with no wallet on the server and no witness ever sent ' +
774
+ 'to it, address a record explicitly: pass payloadHash (this builder\'s record) or ' +
775
+ 'payloadHash + attesterId (another attester\'s record) or recordKey directly: ' +
776
+ 'proveFieldPredicate {payloadHash, fieldKey, threshold, op 0=lessOrEqual|1=greaterOrEqual} ' +
777
+ 'plus merkleProof {fieldValue, fieldSalt, siblings, dirs}; proveFieldEquality ' +
778
+ '{payloadHash, fieldKey, expectedDigest} plus merkleProof {fieldSalt, siblings, dirs}; ' +
779
+ 'proveFieldMembership {payloadHash, fieldKey, setRoot} plus merkleProof {fieldDigest, ' +
780
+ 'fieldSalt, siblings, dirs, setProof}; proveFieldsUnchangedExcept {payloadHashA, ' +
781
+ 'payloadHashB, allowedMask} and proveFieldsDiffer {payloadHashA, payloadHashB, k} ' +
782
+ '(optionally attesterIdA / attesterIdB, or recordKeyA / recordKeyB), both plus docPair ' +
783
+ '{schema, openingA, openingB}. Every witness field comes straight out of ' +
777
784
  'prepare_document_proof. All hashes 64 hex. The built bytes are valid for ' +
778
785
  'the transaction TTL (~30 min) and against the contract state at build time: if the ' +
779
786
  'sponsor job ends CHAIN_EXECUTION_FAILED, build again.',
@@ -911,7 +918,7 @@ function wrapHandler(_client) {
911
918
  }
912
919
  catch (err) {
913
920
  if (err instanceof NightgateApiError) {
914
- const detail = { httpStatus: err.status, code: err.code ?? null, message: err.message };
921
+ const detail = { httpStatus: err.status, code: err.code ?? null, message: err.message, ...(err.detail ?? {}) };
915
922
  return { content: [{ type: 'text', text: JSON.stringify(detail, null, 2) }], isError: true };
916
923
  }
917
924
  const message = err instanceof Error ? err.message : String(err);