@pairsystems/goodmem-mcp 0.1.17 → 0.1.19

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.
Files changed (3) hide show
  1. package/README.md +51 -3
  2. package/dist/index.js +1243 -238
  3. package/package.json +3 -1
package/dist/index.js CHANGED
@@ -3105,6 +3105,9 @@ var require_utils = __commonJS({
3105
3105
  "use strict";
3106
3106
  var isUUID = RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu);
3107
3107
  var isIPv4 = RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u);
3108
+ var isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu);
3109
+ var isUnreserved = RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu);
3110
+ var isPathCharacter = RegExp.prototype.test.bind(/^[\da-z\-._~!$&'()*+,;=:@/]$/iu);
3108
3111
  function stringArrayToHexStripped(input) {
3109
3112
  let acc = "";
3110
3113
  let code = 0;
@@ -3297,27 +3300,77 @@ var require_utils = __commonJS({
3297
3300
  }
3298
3301
  return output.join("");
3299
3302
  }
3300
- function normalizeComponentEncoding(component, esc2) {
3301
- const func = esc2 !== true ? escape : unescape;
3302
- if (component.scheme !== void 0) {
3303
- component.scheme = func(component.scheme);
3304
- }
3305
- if (component.userinfo !== void 0) {
3306
- component.userinfo = func(component.userinfo);
3307
- }
3308
- if (component.host !== void 0) {
3309
- component.host = func(component.host);
3303
+ var HOST_DELIMS = { "@": "%40", "/": "%2F", "?": "%3F", "#": "%23", ":": "%3A" };
3304
+ var HOST_DELIM_RE = /[@/?#:]/g;
3305
+ var HOST_DELIM_NO_COLON_RE = /[@/?#]/g;
3306
+ function reescapeHostDelimiters(host, isIP) {
3307
+ const re = isIP ? HOST_DELIM_NO_COLON_RE : HOST_DELIM_RE;
3308
+ re.lastIndex = 0;
3309
+ return host.replace(re, (ch) => HOST_DELIMS[ch]);
3310
+ }
3311
+ function normalizePercentEncoding(input, decodeUnreserved = false) {
3312
+ if (input.indexOf("%") === -1) {
3313
+ return input;
3310
3314
  }
3311
- if (component.path !== void 0) {
3312
- component.path = func(component.path);
3315
+ let output = "";
3316
+ for (let i = 0; i < input.length; i++) {
3317
+ if (input[i] === "%" && i + 2 < input.length) {
3318
+ const hex = input.slice(i + 1, i + 3);
3319
+ if (isHexPair(hex)) {
3320
+ const normalizedHex = hex.toUpperCase();
3321
+ const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
3322
+ if (decodeUnreserved && isUnreserved(decoded)) {
3323
+ output += decoded;
3324
+ } else {
3325
+ output += "%" + normalizedHex;
3326
+ }
3327
+ i += 2;
3328
+ continue;
3329
+ }
3330
+ }
3331
+ output += input[i];
3313
3332
  }
3314
- if (component.query !== void 0) {
3315
- component.query = func(component.query);
3333
+ return output;
3334
+ }
3335
+ function normalizePathEncoding(input) {
3336
+ let output = "";
3337
+ for (let i = 0; i < input.length; i++) {
3338
+ if (input[i] === "%" && i + 2 < input.length) {
3339
+ const hex = input.slice(i + 1, i + 3);
3340
+ if (isHexPair(hex)) {
3341
+ const normalizedHex = hex.toUpperCase();
3342
+ const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
3343
+ if (decoded !== "." && isUnreserved(decoded)) {
3344
+ output += decoded;
3345
+ } else {
3346
+ output += "%" + normalizedHex;
3347
+ }
3348
+ i += 2;
3349
+ continue;
3350
+ }
3351
+ }
3352
+ if (isPathCharacter(input[i])) {
3353
+ output += input[i];
3354
+ } else {
3355
+ output += escape(input[i]);
3356
+ }
3316
3357
  }
3317
- if (component.fragment !== void 0) {
3318
- component.fragment = func(component.fragment);
3358
+ return output;
3359
+ }
3360
+ function escapePreservingEscapes(input) {
3361
+ let output = "";
3362
+ for (let i = 0; i < input.length; i++) {
3363
+ if (input[i] === "%" && i + 2 < input.length) {
3364
+ const hex = input.slice(i + 1, i + 3);
3365
+ if (isHexPair(hex)) {
3366
+ output += "%" + hex.toUpperCase();
3367
+ i += 2;
3368
+ continue;
3369
+ }
3370
+ }
3371
+ output += escape(input[i]);
3319
3372
  }
3320
- return component;
3373
+ return output;
3321
3374
  }
3322
3375
  function recomposeAuthority(component) {
3323
3376
  const uriTokens = [];
@@ -3332,7 +3385,7 @@ var require_utils = __commonJS({
3332
3385
  if (ipV6res.isIPV6 === true) {
3333
3386
  host = `[${ipV6res.escapedHost}]`;
3334
3387
  } else {
3335
- host = component.host;
3388
+ host = reescapeHostDelimiters(host, false);
3336
3389
  }
3337
3390
  }
3338
3391
  uriTokens.push(host);
@@ -3346,7 +3399,10 @@ var require_utils = __commonJS({
3346
3399
  module.exports = {
3347
3400
  nonSimpleDomain,
3348
3401
  recomposeAuthority,
3349
- normalizeComponentEncoding,
3402
+ reescapeHostDelimiters,
3403
+ normalizePercentEncoding,
3404
+ normalizePathEncoding,
3405
+ escapePreservingEscapes,
3350
3406
  removeDotSegments,
3351
3407
  isIPv4,
3352
3408
  isUUID,
@@ -3570,12 +3626,12 @@ var require_schemes = __commonJS({
3570
3626
  var require_fast_uri = __commonJS({
3571
3627
  "node_modules/fast-uri/index.js"(exports, module) {
3572
3628
  "use strict";
3573
- var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require_utils();
3629
+ var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
3574
3630
  var { SCHEMES, getSchemeHandler } = require_schemes();
3575
3631
  function normalize(uri, options) {
3576
3632
  if (typeof uri === "string") {
3577
3633
  uri = /** @type {T} */
3578
- serialize(parse3(uri, options), options);
3634
+ normalizeString(uri, options);
3579
3635
  } else if (typeof uri === "object") {
3580
3636
  uri = /** @type {T} */
3581
3637
  parse3(serialize(uri, options), options);
@@ -3584,7 +3640,12 @@ var require_fast_uri = __commonJS({
3584
3640
  }
3585
3641
  function resolve(baseURI, relativeURI, options) {
3586
3642
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3587
- const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
3643
+ const { parsed: baseParsed, malformedAuthorityOrPort: baseMalformed } = parseWithStatus(baseURI, schemelessOptions);
3644
+ const { parsed: relativeParsed, malformedAuthorityOrPort: relativeMalformed } = parseWithStatus(relativeURI, schemelessOptions);
3645
+ if (baseMalformed || relativeMalformed) {
3646
+ throw new Error(baseParsed.error || relativeParsed.error || "URI is malformed.");
3647
+ }
3648
+ const resolved = resolveComponent(baseParsed, relativeParsed, schemelessOptions, true);
3588
3649
  schemelessOptions.skipEscape = true;
3589
3650
  return serialize(resolved, schemelessOptions);
3590
3651
  }
@@ -3642,19 +3703,9 @@ var require_fast_uri = __commonJS({
3642
3703
  return target;
3643
3704
  }
3644
3705
  function equal(uriA, uriB, options) {
3645
- if (typeof uriA === "string") {
3646
- uriA = unescape(uriA);
3647
- uriA = serialize(normalizeComponentEncoding(parse3(uriA, options), true), { ...options, skipEscape: true });
3648
- } else if (typeof uriA === "object") {
3649
- uriA = serialize(normalizeComponentEncoding(uriA, true), { ...options, skipEscape: true });
3650
- }
3651
- if (typeof uriB === "string") {
3652
- uriB = unescape(uriB);
3653
- uriB = serialize(normalizeComponentEncoding(parse3(uriB, options), true), { ...options, skipEscape: true });
3654
- } else if (typeof uriB === "object") {
3655
- uriB = serialize(normalizeComponentEncoding(uriB, true), { ...options, skipEscape: true });
3656
- }
3657
- return uriA.toLowerCase() === uriB.toLowerCase();
3706
+ const normalizedA = normalizeComparableURI(uriA, options);
3707
+ const normalizedB = normalizeComparableURI(uriB, options);
3708
+ return normalizedA !== void 0 && normalizedB !== void 0 && normalizedA.toLowerCase() === normalizedB.toLowerCase();
3658
3709
  }
3659
3710
  function serialize(cmpts, opts) {
3660
3711
  const component = {
@@ -3679,12 +3730,12 @@ var require_fast_uri = __commonJS({
3679
3730
  if (schemeHandler && schemeHandler.serialize) schemeHandler.serialize(component, options);
3680
3731
  if (component.path !== void 0) {
3681
3732
  if (!options.skipEscape) {
3682
- component.path = escape(component.path);
3733
+ component.path = escapePreservingEscapes(component.path);
3683
3734
  if (component.scheme !== void 0) {
3684
3735
  component.path = component.path.split("%3A").join(":");
3685
3736
  }
3686
3737
  } else {
3687
- component.path = unescape(component.path);
3738
+ component.path = normalizePercentEncoding(component.path);
3688
3739
  }
3689
3740
  }
3690
3741
  if (options.reference !== "suffix" && component.scheme) {
@@ -3719,7 +3770,18 @@ var require_fast_uri = __commonJS({
3719
3770
  return uriTokens.join("");
3720
3771
  }
3721
3772
  var URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u;
3722
- function parse3(uri, opts) {
3773
+ var AUTHORITY_PREFIX = /^(?:[^#/:?]+:)?\/\/([^/?#]*)/;
3774
+ var AUTHORITY_INTRODUCER_REGION = /^(?:[^#/:?]+:)?([/\\\t\n\r]*)/;
3775
+ function getParseError(parsed, matches) {
3776
+ if (matches[2] !== void 0 && parsed.path && parsed.path[0] !== "/") {
3777
+ return 'URI path must start with "/" when authority is present.';
3778
+ }
3779
+ if (typeof parsed.port === "number" && (parsed.port < 0 || parsed.port > 65535)) {
3780
+ return "URI port is malformed.";
3781
+ }
3782
+ return void 0;
3783
+ }
3784
+ function parseWithStatus(uri, opts) {
3723
3785
  const options = Object.assign({}, opts);
3724
3786
  const parsed = {
3725
3787
  scheme: void 0,
@@ -3730,6 +3792,7 @@ var require_fast_uri = __commonJS({
3730
3792
  query: void 0,
3731
3793
  fragment: void 0
3732
3794
  };
3795
+ let malformedAuthorityOrPort = false;
3733
3796
  let isIP = false;
3734
3797
  if (options.reference === "suffix") {
3735
3798
  if (options.scheme) {
@@ -3738,6 +3801,25 @@ var require_fast_uri = __commonJS({
3738
3801
  uri = "//" + uri;
3739
3802
  }
3740
3803
  }
3804
+ const authorityMatch = uri.match(AUTHORITY_PREFIX);
3805
+ if (authorityMatch !== null && authorityMatch[1].indexOf("\\") !== -1) {
3806
+ parsed.error = "URI authority must not contain a literal backslash.";
3807
+ malformedAuthorityOrPort = true;
3808
+ }
3809
+ const introducerMatch = uri.match(AUTHORITY_INTRODUCER_REGION);
3810
+ if (introducerMatch !== null) {
3811
+ const region = introducerMatch[1];
3812
+ const normalizedRegion = region.replace(/[\t\n\r]/g, "");
3813
+ if (normalizedRegion.length >= 2) {
3814
+ if (normalizedRegion.slice(0, 2) !== "//") {
3815
+ parsed.error = parsed.error || "URI authority must not contain a literal backslash.";
3816
+ malformedAuthorityOrPort = true;
3817
+ } else if (region.length !== normalizedRegion.length) {
3818
+ parsed.error = parsed.error || "URI authority introducer must not contain whitespace.";
3819
+ malformedAuthorityOrPort = true;
3820
+ }
3821
+ }
3822
+ }
3741
3823
  const matches = uri.match(URI_PARSE);
3742
3824
  if (matches) {
3743
3825
  parsed.scheme = matches[1];
@@ -3750,6 +3832,11 @@ var require_fast_uri = __commonJS({
3750
3832
  if (isNaN(parsed.port)) {
3751
3833
  parsed.port = matches[5];
3752
3834
  }
3835
+ const parseError = getParseError(parsed, matches);
3836
+ if (parseError !== void 0) {
3837
+ parsed.error = parsed.error || parseError;
3838
+ malformedAuthorityOrPort = true;
3839
+ }
3753
3840
  if (parsed.host) {
3754
3841
  const ipv4result = isIPv4(parsed.host);
3755
3842
  if (ipv4result === false) {
@@ -3776,7 +3863,7 @@ var require_fast_uri = __commonJS({
3776
3863
  if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport)) {
3777
3864
  if (parsed.host && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
3778
3865
  try {
3779
- parsed.host = URL.domainToASCII(parsed.host.toLowerCase());
3866
+ parsed.host = new URL("http://" + parsed.host).hostname;
3780
3867
  } catch (e) {
3781
3868
  parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
3782
3869
  }
@@ -3788,14 +3875,18 @@ var require_fast_uri = __commonJS({
3788
3875
  parsed.scheme = unescape(parsed.scheme);
3789
3876
  }
3790
3877
  if (parsed.host !== void 0) {
3791
- parsed.host = unescape(parsed.host);
3878
+ parsed.host = reescapeHostDelimiters(unescape(parsed.host), isIP);
3792
3879
  }
3793
3880
  }
3794
3881
  if (parsed.path) {
3795
- parsed.path = escape(unescape(parsed.path));
3882
+ parsed.path = normalizePathEncoding(parsed.path);
3796
3883
  }
3797
3884
  if (parsed.fragment) {
3798
- parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment));
3885
+ try {
3886
+ parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment));
3887
+ } catch {
3888
+ parsed.error = parsed.error || "URI malformed";
3889
+ }
3799
3890
  }
3800
3891
  }
3801
3892
  if (schemeHandler && schemeHandler.parse) {
@@ -3804,7 +3895,29 @@ var require_fast_uri = __commonJS({
3804
3895
  } else {
3805
3896
  parsed.error = parsed.error || "URI can not be parsed.";
3806
3897
  }
3807
- return parsed;
3898
+ return { parsed, malformedAuthorityOrPort };
3899
+ }
3900
+ function parse3(uri, opts) {
3901
+ return parseWithStatus(uri, opts).parsed;
3902
+ }
3903
+ function normalizeString(uri, opts) {
3904
+ return normalizeStringWithStatus(uri, opts).normalized;
3905
+ }
3906
+ function normalizeStringWithStatus(uri, opts) {
3907
+ const { parsed, malformedAuthorityOrPort } = parseWithStatus(uri, opts);
3908
+ return {
3909
+ normalized: malformedAuthorityOrPort ? uri : serialize(parsed, opts),
3910
+ malformedAuthorityOrPort
3911
+ };
3912
+ }
3913
+ function normalizeComparableURI(uri, opts) {
3914
+ if (typeof uri === "string") {
3915
+ const { normalized, malformedAuthorityOrPort } = normalizeStringWithStatus(uri, opts);
3916
+ return malformedAuthorityOrPort ? void 0 : normalized;
3917
+ }
3918
+ if (typeof uri === "object") {
3919
+ return serialize(uri, opts);
3920
+ }
3808
3921
  }
3809
3922
  var fastUri = {
3810
3923
  SCHEMES,
@@ -20988,7 +21101,21 @@ var StdioServerTransport = class {
20988
21101
  // dist/index.js
20989
21102
  var goodmemBaseUrl = process.env.GOODMEM_BASE_URL || "";
20990
21103
  var goodmemApiKey = process.env.GOODMEM_API_KEY || "";
20991
- var BASED_ON_GOODMEM_COMMIT = "d15028640e4a884f3a648db9b25aee4cdfab719d";
21104
+ var DEFAULT_STREAM_MAX_LINE_BYTES = 8 * 1024 * 1024;
21105
+ function _parseStreamMaxLineBytes(raw) {
21106
+ if (!raw)
21107
+ return 0;
21108
+ const n = Number(raw);
21109
+ return Number.isFinite(n) && n > 0 ? Math.floor(n) : 0;
21110
+ }
21111
+ var goodmemStreamMaxLineBytes = _parseStreamMaxLineBytes(process.env.GOODMEM_STREAM_MAX_LINE_BYTES);
21112
+ function _concatBytes(a, b) {
21113
+ const out = new Uint8Array(a.length + b.length);
21114
+ out.set(a, 0);
21115
+ out.set(b, a.length);
21116
+ return out;
21117
+ }
21118
+ var BASED_ON_GOODMEM_COMMIT = "232e437dbe75cefc1f075649027ce7275d357380";
20992
21119
  function isConfigured() {
20993
21120
  return goodmemBaseUrl !== "";
20994
21121
  }
@@ -21104,18 +21231,61 @@ async function callApiNdjson(method, path, body, query, timeoutSec) {
21104
21231
  ...body ? { body: JSON.stringify(body) } : {}
21105
21232
  });
21106
21233
  if (!resp.ok) {
21107
- const text2 = await resp.text();
21108
- throw new Error(`GoodMem API error ${resp.status}: ${_trimBody(text2)}`);
21234
+ const text = await resp.text();
21235
+ throw new Error(`GoodMem API error ${resp.status}: ${_trimBody(text)}`);
21109
21236
  }
21110
- const text = await resp.text();
21111
- const lines = text.split("\n").map((line) => line.trim()).filter(Boolean);
21112
- return lines.map((line, i) => {
21237
+ const maxLineBytes = goodmemStreamMaxLineBytes > 0 ? goodmemStreamMaxLineBytes : DEFAULT_STREAM_MAX_LINE_BYTES;
21238
+ const results = [];
21239
+ if (resp.body === null) {
21240
+ return results;
21241
+ }
21242
+ const _overCap = () => new Error(`stream event exceeded the ${maxLineBytes}-byte per-event line cap; raise it via the goodmem_configure stream_max_line_bytes option (or the GOODMEM_STREAM_MAX_LINE_BYTES env var), or avoid inlining large bodies by leaving fetch_memory_content unset and fetching content via the memory content tool`);
21243
+ const reader = resp.body.getReader();
21244
+ const decoder = new TextDecoder("utf-8");
21245
+ let pending = new Uint8Array(0);
21246
+ let lineNo = 0;
21247
+ const _emit = (bytes) => {
21248
+ const line = decoder.decode(bytes).trim();
21249
+ if (!line)
21250
+ return;
21251
+ lineNo++;
21113
21252
  try {
21114
- return JSON.parse(line);
21253
+ results.push(JSON.parse(line));
21115
21254
  } catch (e) {
21116
- throw new Error(`NDJSON parse error on line ${i + 1}: ${e instanceof Error ? e.message : String(e)}`);
21255
+ throw new Error(`NDJSON parse error on line ${lineNo}: ${e instanceof Error ? e.message : String(e)}`);
21117
21256
  }
21118
- });
21257
+ };
21258
+ try {
21259
+ for (; ; ) {
21260
+ const { done, value } = await reader.read();
21261
+ if (done)
21262
+ break;
21263
+ if (value === void 0 || value.length === 0)
21264
+ continue;
21265
+ pending = pending.length === 0 ? value : _concatBytes(pending, value);
21266
+ let nl;
21267
+ while ((nl = pending.indexOf(10)) !== -1) {
21268
+ const lineBytes = pending.subarray(0, nl);
21269
+ if (lineBytes.length > maxLineBytes)
21270
+ throw _overCap();
21271
+ _emit(lineBytes);
21272
+ pending = pending.subarray(nl + 1);
21273
+ }
21274
+ if (pending.length > maxLineBytes)
21275
+ throw _overCap();
21276
+ }
21277
+ if (pending.length > 0) {
21278
+ if (pending.length > maxLineBytes)
21279
+ throw _overCap();
21280
+ _emit(pending);
21281
+ }
21282
+ } finally {
21283
+ try {
21284
+ reader.releaseLock();
21285
+ } catch {
21286
+ }
21287
+ }
21288
+ return results;
21119
21289
  }
21120
21290
  var MODEL_REGISTRY = {
21121
21291
  "embedders": {
@@ -21220,15 +21390,74 @@ var MODEL_REGISTRY = {
21220
21390
  ],
21221
21391
  "maxSequenceLength": 512
21222
21392
  },
21223
- "jina-embeddings-v4": {
21393
+ "jina-embeddings-v5-text-small": {
21224
21394
  "providerType": "JINA",
21225
21395
  "dimensions": {
21226
21396
  "default": 1024,
21227
- "allowed": [
21228
- 128,
21229
- 256,
21230
- 512,
21231
- 1024,
21397
+ "range": [
21398
+ 1,
21399
+ 1024
21400
+ ]
21401
+ },
21402
+ "supportedModalities": [
21403
+ "TEXT"
21404
+ ],
21405
+ "maxSequenceLength": 32768
21406
+ },
21407
+ "jina-embeddings-v5-text-nano": {
21408
+ "providerType": "JINA",
21409
+ "dimensions": {
21410
+ "default": 768,
21411
+ "range": [
21412
+ 1,
21413
+ 768
21414
+ ]
21415
+ },
21416
+ "supportedModalities": [
21417
+ "TEXT"
21418
+ ],
21419
+ "maxSequenceLength": 8192
21420
+ },
21421
+ "jina-embeddings-v5-omni-small": {
21422
+ "providerType": "JINA",
21423
+ "dimensions": {
21424
+ "default": 1024,
21425
+ "range": [
21426
+ 1,
21427
+ 1024
21428
+ ]
21429
+ },
21430
+ "supportedModalities": [
21431
+ "TEXT",
21432
+ "IMAGE",
21433
+ "AUDIO",
21434
+ "VIDEO"
21435
+ ],
21436
+ "maxSequenceLength": 32768
21437
+ },
21438
+ "jina-embeddings-v5-omni-nano": {
21439
+ "providerType": "JINA",
21440
+ "dimensions": {
21441
+ "default": 768,
21442
+ "range": [
21443
+ 1,
21444
+ 768
21445
+ ]
21446
+ },
21447
+ "supportedModalities": [
21448
+ "TEXT",
21449
+ "IMAGE",
21450
+ "AUDIO",
21451
+ "VIDEO"
21452
+ ],
21453
+ "maxSequenceLength": 8192
21454
+ },
21455
+ "jina-embeddings-v4": {
21456
+ "providerType": "JINA",
21457
+ "dimensions": {
21458
+ "default": 1536,
21459
+ "range": [
21460
+ 1,
21232
21461
  2048
21233
21462
  ]
21234
21463
  },
@@ -21238,17 +21467,40 @@ var MODEL_REGISTRY = {
21238
21467
  ],
21239
21468
  "maxSequenceLength": 32768
21240
21469
  },
21470
+ "jina-code-embeddings-1.5b": {
21471
+ "providerType": "JINA",
21472
+ "dimensions": {
21473
+ "default": 1536,
21474
+ "range": [
21475
+ 1,
21476
+ 1536
21477
+ ]
21478
+ },
21479
+ "supportedModalities": [
21480
+ "TEXT"
21481
+ ],
21482
+ "maxSequenceLength": 32768
21483
+ },
21484
+ "jina-code-embeddings-0.5b": {
21485
+ "providerType": "JINA",
21486
+ "dimensions": {
21487
+ "default": 896,
21488
+ "range": [
21489
+ 1,
21490
+ 896
21491
+ ]
21492
+ },
21493
+ "supportedModalities": [
21494
+ "TEXT"
21495
+ ],
21496
+ "maxSequenceLength": 32768
21497
+ },
21241
21498
  "jina-embeddings-v3": {
21242
21499
  "providerType": "JINA",
21243
21500
  "dimensions": {
21244
21501
  "default": 1024,
21245
- "allowed": [
21246
- 32,
21247
- 64,
21248
- 128,
21249
- 256,
21250
- 512,
21251
- 768,
21502
+ "range": [
21503
+ 1,
21252
21504
  1024
21253
21505
  ]
21254
21506
  },
@@ -21340,12 +21592,8 @@ var MODEL_REGISTRY = {
21340
21592
  "providerType": "JINA",
21341
21593
  "dimensions": {
21342
21594
  "default": 1024,
21343
- "allowed": [
21344
- 64,
21345
- 128,
21346
- 256,
21347
- 512,
21348
- 768,
21595
+ "range": [
21596
+ 1,
21349
21597
  1024
21350
21598
  ]
21351
21599
  },
@@ -21403,6 +21651,22 @@ var MODEL_REGISTRY = {
21403
21651
  ],
21404
21652
  "maxSequenceLength": 32e3
21405
21653
  },
21654
+ "voyage-code-4": {
21655
+ "providerType": "VOYAGE",
21656
+ "dimensions": {
21657
+ "default": 1024,
21658
+ "allowed": [
21659
+ 256,
21660
+ 512,
21661
+ 1024,
21662
+ 2048
21663
+ ]
21664
+ },
21665
+ "supportedModalities": [
21666
+ "TEXT"
21667
+ ],
21668
+ "maxSequenceLength": 32e3
21669
+ },
21406
21670
  "voyage-code-3": {
21407
21671
  "providerType": "VOYAGE",
21408
21672
  "dimensions": {
@@ -21544,40 +21808,142 @@ var MODEL_REGISTRY = {
21544
21808
  "TEXT"
21545
21809
  ],
21546
21810
  "maxSequenceLength": 32e3
21547
- }
21548
- },
21549
- "llms": {
21550
- "gpt-5.2": {
21551
- "providerType": "OPENAI",
21811
+ },
21812
+ "text-embedding-v4": {
21813
+ "providerType": "DASHSCOPE",
21814
+ "dimensions": {
21815
+ "default": 1024,
21816
+ "allowed": [
21817
+ 64,
21818
+ 128,
21819
+ 256,
21820
+ 512,
21821
+ 768,
21822
+ 1024,
21823
+ 1536,
21824
+ 2048
21825
+ ]
21826
+ },
21552
21827
  "supportedModalities": [
21553
- "TEXT",
21554
- "IMAGE"
21828
+ "TEXT"
21555
21829
  ],
21556
- "maxContextLength": 4e5
21830
+ "maxSequenceLength": 8192
21557
21831
  },
21558
- "gpt-5.2-pro": {
21559
- "providerType": "OPENAI",
21832
+ "text-embedding-v3": {
21833
+ "providerType": "DASHSCOPE",
21834
+ "dimensions": {
21835
+ "default": 1024,
21836
+ "allowed": [
21837
+ 512,
21838
+ 768,
21839
+ 1024
21840
+ ]
21841
+ },
21560
21842
  "supportedModalities": [
21561
- "TEXT",
21562
- "IMAGE"
21843
+ "TEXT"
21563
21844
  ],
21564
- "maxContextLength": 4e5
21845
+ "maxSequenceLength": 8192
21565
21846
  },
21566
- "gpt-5.1": {
21567
- "providerType": "OPENAI",
21847
+ "tongyi-embedding-vision-plus": {
21848
+ "providerType": "DASHSCOPE",
21849
+ "dimensions": {
21850
+ "default": 1152,
21851
+ "allowed": [
21852
+ 64,
21853
+ 128,
21854
+ 256,
21855
+ 512,
21856
+ 1024,
21857
+ 1152
21858
+ ]
21859
+ },
21568
21860
  "supportedModalities": [
21569
- "TEXT",
21570
- "IMAGE"
21861
+ "TEXT"
21571
21862
  ],
21572
- "maxContextLength": 4e5
21863
+ "maxSequenceLength": 1024
21573
21864
  },
21574
- "gpt-5": {
21575
- "providerType": "OPENAI",
21865
+ "tongyi-embedding-vision-flash": {
21866
+ "providerType": "DASHSCOPE",
21867
+ "dimensions": {
21868
+ "default": 768,
21869
+ "allowed": [
21870
+ 64,
21871
+ 128,
21872
+ 256,
21873
+ 512,
21874
+ 768
21875
+ ]
21876
+ },
21576
21877
  "supportedModalities": [
21577
- "TEXT",
21578
- "IMAGE"
21878
+ "TEXT"
21579
21879
  ],
21580
- "maxContextLength": 4e5
21880
+ "maxSequenceLength": 1024
21881
+ },
21882
+ "qwen3-vl-embedding": {
21883
+ "providerType": "DASHSCOPE",
21884
+ "dimensions": {
21885
+ "default": 1536,
21886
+ "allowed": [
21887
+ 256,
21888
+ 512,
21889
+ 768,
21890
+ 1024,
21891
+ 1536,
21892
+ 2048,
21893
+ 2560
21894
+ ]
21895
+ },
21896
+ "supportedModalities": [
21897
+ "TEXT"
21898
+ ],
21899
+ "maxSequenceLength": 32e3
21900
+ },
21901
+ "multimodal-embedding-v1": {
21902
+ "providerType": "DASHSCOPE",
21903
+ "dimensions": {
21904
+ "default": 1024,
21905
+ "allowed": [
21906
+ 1024
21907
+ ]
21908
+ },
21909
+ "supportedModalities": [
21910
+ "TEXT"
21911
+ ],
21912
+ "maxSequenceLength": 512
21913
+ }
21914
+ },
21915
+ "llms": {
21916
+ "gpt-5.2": {
21917
+ "providerType": "OPENAI",
21918
+ "supportedModalities": [
21919
+ "TEXT",
21920
+ "IMAGE"
21921
+ ],
21922
+ "maxContextLength": 4e5
21923
+ },
21924
+ "gpt-5.2-pro": {
21925
+ "providerType": "OPENAI",
21926
+ "supportedModalities": [
21927
+ "TEXT",
21928
+ "IMAGE"
21929
+ ],
21930
+ "maxContextLength": 4e5
21931
+ },
21932
+ "gpt-5.1": {
21933
+ "providerType": "OPENAI",
21934
+ "supportedModalities": [
21935
+ "TEXT",
21936
+ "IMAGE"
21937
+ ],
21938
+ "maxContextLength": 4e5
21939
+ },
21940
+ "gpt-5": {
21941
+ "providerType": "OPENAI",
21942
+ "supportedModalities": [
21943
+ "TEXT",
21944
+ "IMAGE"
21945
+ ],
21946
+ "maxContextLength": 4e5
21581
21947
  },
21582
21948
  "gpt-5-mini": {
21583
21949
  "providerType": "OPENAI",
@@ -21696,6 +22062,97 @@ var MODEL_REGISTRY = {
21696
22062
  "TEXT"
21697
22063
  ],
21698
22064
  "maxContextLength": 128e3
22065
+ },
22066
+ "qwen-max": {
22067
+ "providerType": "DASHSCOPE",
22068
+ "supportedModalities": [
22069
+ "TEXT"
22070
+ ],
22071
+ "maxContextLength": 32768
22072
+ },
22073
+ "qwen-plus": {
22074
+ "providerType": "DASHSCOPE",
22075
+ "supportedModalities": [
22076
+ "TEXT"
22077
+ ],
22078
+ "maxContextLength": 1e6
22079
+ },
22080
+ "qwen-turbo": {
22081
+ "providerType": "DASHSCOPE",
22082
+ "supportedModalities": [
22083
+ "TEXT"
22084
+ ],
22085
+ "maxContextLength": 131072
22086
+ },
22087
+ "qwen-long": {
22088
+ "providerType": "DASHSCOPE",
22089
+ "supportedModalities": [
22090
+ "TEXT"
22091
+ ],
22092
+ "maxContextLength": 1e7
22093
+ },
22094
+ "qwen3-max": {
22095
+ "providerType": "DASHSCOPE",
22096
+ "supportedModalities": [
22097
+ "TEXT"
22098
+ ],
22099
+ "maxContextLength": 262144
22100
+ },
22101
+ "qwen3-coder-plus": {
22102
+ "providerType": "DASHSCOPE",
22103
+ "supportedModalities": [
22104
+ "TEXT"
22105
+ ],
22106
+ "maxContextLength": 1e6
22107
+ },
22108
+ "qwen3.7-max": {
22109
+ "providerType": "DASHSCOPE",
22110
+ "supportedModalities": [
22111
+ "TEXT"
22112
+ ],
22113
+ "maxContextLength": 1e6
22114
+ },
22115
+ "qwen3.7-plus": {
22116
+ "providerType": "DASHSCOPE",
22117
+ "supportedModalities": [
22118
+ "TEXT"
22119
+ ],
22120
+ "maxContextLength": 1e6
22121
+ },
22122
+ "qwen3.8-max": {
22123
+ "providerType": "DASHSCOPE",
22124
+ "supportedModalities": [
22125
+ "TEXT"
22126
+ ],
22127
+ "maxContextLength": 1e6
22128
+ },
22129
+ "qwen3.6-flash": {
22130
+ "providerType": "DASHSCOPE",
22131
+ "supportedModalities": [
22132
+ "TEXT"
22133
+ ],
22134
+ "maxContextLength": 1e6
22135
+ },
22136
+ "qwen-vl-max": {
22137
+ "providerType": "DASHSCOPE",
22138
+ "supportedModalities": [
22139
+ "TEXT"
22140
+ ],
22141
+ "maxContextLength": 131072
22142
+ },
22143
+ "qwen-vl-plus": {
22144
+ "providerType": "DASHSCOPE",
22145
+ "supportedModalities": [
22146
+ "TEXT"
22147
+ ],
22148
+ "maxContextLength": 131072
22149
+ },
22150
+ "deepseek-v4-pro": {
22151
+ "providerType": "DASHSCOPE",
22152
+ "supportedModalities": [
22153
+ "TEXT"
22154
+ ],
22155
+ "maxContextLength": 1e6
21699
22156
  }
21700
22157
  },
21701
22158
  "rerankers": {
@@ -21794,6 +22251,24 @@ var MODEL_REGISTRY = {
21794
22251
  "supportedModalities": [
21795
22252
  "TEXT"
21796
22253
  ]
22254
+ },
22255
+ "qwen3-vl-rerank": {
22256
+ "providerType": "DASHSCOPE",
22257
+ "supportedModalities": [
22258
+ "TEXT"
22259
+ ]
22260
+ },
22261
+ "gte-rerank-v2": {
22262
+ "providerType": "DASHSCOPE",
22263
+ "supportedModalities": [
22264
+ "TEXT"
22265
+ ]
22266
+ },
22267
+ "qwen3-rerank": {
22268
+ "providerType": "DASHSCOPE",
22269
+ "supportedModalities": [
22270
+ "TEXT"
22271
+ ]
21797
22272
  }
21798
22273
  }
21799
22274
  };
@@ -21801,7 +22276,8 @@ var PROVIDER_ENDPOINTS = {
21801
22276
  "OPENAI": "https://api.openai.com/v1",
21802
22277
  "COHERE": "https://api.cohere.com",
21803
22278
  "VOYAGE": "https://api.voyageai.com/v1",
21804
- "JINA": "https://api.jina.ai"
22279
+ "JINA": "https://api.jina.ai",
22280
+ "DASHSCOPE": "https://dashscope.aliyuncs.com"
21805
22281
  };
21806
22282
  var PROVIDER_CREDENTIAL_REQUIREMENTS = {
21807
22283
  "OPENAI": [
@@ -21818,6 +22294,12 @@ var PROVIDER_CREDENTIAL_REQUIREMENTS = {
21818
22294
  ],
21819
22295
  "JINA": [
21820
22296
  "api.jina.ai"
22297
+ ],
22298
+ "DASHSCOPE": [
22299
+ "dashscope.aliyuncs.com",
22300
+ "dashscope-intl.aliyuncs.com",
22301
+ "dashscope-us.aliyuncs.com",
22302
+ "cn-hongkong.dashscope.aliyuncs.com"
21821
22303
  ]
21822
22304
  };
21823
22305
  function checkCredentialRequired(args) {
@@ -21876,12 +22358,13 @@ function autoInferFromRegistry(registryType, args) {
21876
22358
  }
21877
22359
  var server = new McpServer({
21878
22360
  name: "goodmem",
21879
- version: "0.1.17"
22361
+ version: "0.1.19"
21880
22362
  });
21881
22363
  server.tool("goodmem_configure", "Configure GoodMem server connection. Call this before using other GoodMem tools if GOODMEM_BASE_URL and GOODMEM_API_KEY environment variables are not set. Session credentials override environment variables. For bootstrap workflows that need to call /v1/system/info or /v1/system/init before keys exist, api_key may be omitted; most other tools will return AuthenticationException until a real key is provided.", {
21882
22364
  base_url: external_exports.string().describe("GoodMem server URL (e.g., https://your-server.example.com)"),
21883
- api_key: external_exports.string().describe("GoodMem API key (starts with gm_). Omit for bootstrap-only sessions that call /v1/system/info or /v1/system/init.").optional()
21884
- }, async (args) => {
22365
+ api_key: external_exports.string().describe("GoodMem API key (starts with gm_). Omit for bootstrap-only sessions that call /v1/system/info or /v1/system/init.").optional(),
22366
+ stream_max_line_bytes: external_exports.number().int().positive().describe("Maximum size in bytes of a single NDJSON stream event (memories.retrieve, ping.stream). Events larger than this are rejected instead of buffering without bound. Defaults to 8388608 (8 MiB).").optional()
22367
+ }, { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true }, async (args) => {
21885
22368
  if (!/^https?:\/\//i.test(args.base_url)) {
21886
22369
  return { isError: true, content: [{ type: "text", text: `base_url must include a scheme (e.g., http://${args.base_url} or https://${args.base_url}).` }] };
21887
22370
  }
@@ -21904,6 +22387,9 @@ server.tool("goodmem_configure", "Configure GoodMem server connection. Call this
21904
22387
  const info = await resp.json().catch(() => ({}));
21905
22388
  goodmemBaseUrl = args.base_url;
21906
22389
  goodmemApiKey = args.api_key ?? "";
22390
+ if (args.stream_max_line_bytes !== void 0) {
22391
+ goodmemStreamMaxLineBytes = args.stream_max_line_bytes;
22392
+ }
21907
22393
  const keyNote = goodmemApiKey === "" ? " (bootstrap-only session \u2014 no api_key set)" : "";
21908
22394
  return { content: [{ type: "text", text: `Connected to GoodMem at ${goodmemBaseUrl}. Server version: ${info.version ?? "unknown"}.${keyNote}` }] };
21909
22395
  } catch (err) {
@@ -21918,7 +22404,7 @@ server.tool("goodmem_configure", "Configure GoodMem server connection. Call this
21918
22404
  server.tool("goodmem_lookup_model", "Look up a model in the registry to see its provider, endpoint, dimensions, and other auto-inferred fields. Use this to check what models are available before creating an embedder, LLM, or reranker.", {
21919
22405
  model_identifier: external_exports.string().describe("Model name to look up (e.g., text-embedding-3-large, gpt-4o, rerank-2)"),
21920
22406
  type: external_exports.enum(["embedder", "llm", "reranker"]).describe("Model type to search").optional()
21921
- }, async (args) => {
22407
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
21922
22408
  const searches = args.type ? [[args.type + "s", args.type]] : [["embedders", "embedder"], ["llms", "llm"], ["rerankers", "reranker"]];
21923
22409
  for (const [registryKey, label] of searches) {
21924
22410
  const registry2 = MODEL_REGISTRY[registryKey] ?? {};
@@ -21956,19 +22442,166 @@ server.tool("goodmem_lookup_model", "Look up a model in the registry to see its
21956
22442
  return { content: [{ type: "text", text: `Model "${args.model_identifier}" not found in ${scope}. Available models:
21957
22443
  ${allModels.join("\n")}` }] };
21958
22444
  });
21959
- server.tool("goodmem_client_info", "Return the MCP client package version and the goodmem server commit it was built against. No server connection required.", {}, async () => {
22445
+ server.tool("goodmem_client_info", "Return the MCP client package version and the goodmem server commit it was built against. No server connection required.", {}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async () => {
21960
22446
  return { content: [{ type: "text", text: JSON.stringify({
21961
- mcp_version: "0.1.17",
22447
+ mcp_version: "0.1.19",
21962
22448
  based_on_goodmem_commit: BASED_ON_GOODMEM_COMMIT
21963
22449
  }, null, 2) }] };
21964
22450
  });
21965
22451
  function registerTools() {
22452
+ server.tool("goodmem_access_policy_check", "Check effective authorization\n\nEvaluates 1 to 50 concrete operation-and-target checks under the authenticated caller's live authority and any API-key ceiling. Results are positional and advisory: missing targets and denied operations both return allowed=false, and every later resource request performs fresh authorization. Top-level creates target INSTANCE; CREATE_MEMORY and LIST_MEMORY target their parent SPACE; reads, mutations, proxy operations, and access-policy administration target concrete resources. LIST_API_KEY and LIST_RETRIEVE_MEMORY_LOG_POLICY are rejected because their current candidate-based list rules have no instance-wide preflight.", {
22453
+ checks: external_exports.array(external_exports.object({
22454
+ operation: external_exports.enum(["CREATE_USER", "READ_USER", "UPDATE_USER", "DELETE_USER", "LIST_USER", "MANAGE_USER_ENROLLMENT", "CREATE_SERVICE_IDENTITY", "READ_SERVICE_IDENTITY", "UPDATE_SERVICE_IDENTITY", "DELETE_SERVICE_IDENTITY", "LIST_SERVICE_IDENTITY", "CREATE_SPACE", "READ_SPACE", "UPDATE_SPACE", "DELETE_SPACE", "LIST_SPACE", "CREATE_API_KEY", "READ_API_KEY", "UPDATE_API_KEY", "DELETE_API_KEY", "LIST_API_KEY", "CREATE_EMBEDDER", "READ_EMBEDDER", "UPDATE_EMBEDDER", "DELETE_EMBEDDER", "LIST_EMBEDDER", "PING_EMBEDDER", "EXECUTE_EMBEDDER", "READ_EMBEDDER_CREDENTIALS", "CREATE_RERANKER", "READ_RERANKER", "UPDATE_RERANKER", "DELETE_RERANKER", "LIST_RERANKER", "PING_RERANKER", "EXECUTE_RERANKER", "READ_RERANKER_CREDENTIALS", "CREATE_LLM", "READ_LLM", "UPDATE_LLM", "DELETE_LLM", "LIST_LLM", "PING_LLM", "EXECUTE_LLM", "READ_LLM_CREDENTIALS", "PROXY_INFERENCE_TARGET", "OCR_DOCUMENT", "CREATE_MEMORY", "READ_MEMORY", "DELETE_MEMORY", "LIST_MEMORY", "CREATE_EXTENSION", "READ_EXTENSION", "UPDATE_EXTENSION", "DELETE_EXTENSION", "LIST_EXTENSION", "DOWNLOAD_EXTENSION", "READ_INSTANCE", "TRANSFER_INSTANCE_OWNERSHIP", "TRANSFER_RESOURCE_OWNERSHIP", "RELOAD_LICENSE", "DRAIN_SERVER", "PURGE_BACKGROUND_JOBS", "CREATE_RETRIEVE_MEMORY_LOG_POLICY", "READ_RETRIEVE_MEMORY_LOG_POLICY", "LIST_RETRIEVE_MEMORY_LOG_POLICY", "DELETE_RETRIEVE_MEMORY_LOG_POLICY", "MANAGE_ACCESS"]).describe("Operation the caller proposes to perform."),
22455
+ target: external_exports.object({
22456
+ kind: external_exports.enum(["INSTANCE", "USER", "SERVICE_IDENTITY", "SPACE", "API_KEY", "EMBEDDER", "RERANKER", "LLM", "MEMORY", "EXTENSION", "RETRIEVE_MEMORY_LOG_POLICY"]).describe("Concrete target kind."),
22457
+ resourceId: external_exports.string().describe("Concrete resource UUID; omitted for the singleton INSTANCE target.").optional()
22458
+ }).passthrough().describe("Target required by the operation: INSTANCE for top-level creates, parent SPACE for CREATE_MEMORY or LIST_MEMORY, otherwise the concrete resource.")
22459
+ }).passthrough()).describe("Concrete checks evaluated in request order.")
22460
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
22461
+ const body = {};
22462
+ if (args.checks !== void 0)
22463
+ body["checks"] = args.checks;
22464
+ const result = await callApi("POST", `/v1/access-policy:check`, body);
22465
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22466
+ });
22467
+ server.tool("goodmem_access_policy_grants_create", "Create an authorization grant\n\nCreates one direct grant after resolving its typed policy target and requiring MANAGE_ACCESS. Direct grants cannot confer credential-read or ownership-transfer authority. ALL_AUTHENTICATED grants require an assigned-resource selector. MANAGE_ACCESS and MANAGE_USER_ENROLLMENT require a concrete principal and ANY or EXACT; MANAGE_USER_ENROLLMENT with EXACT must target USER.", {
22468
+ grant_id: external_exports.string().describe("Optional caller-provided grant UUID.").optional(),
22469
+ audience: external_exports.object({
22470
+ principalId: external_exports.string().describe("Active HUMAN or SERVICE principal UUID.").optional(),
22471
+ allAuthenticated: external_exports.literal(true).describe("Set to true to address every authenticated principal.").optional()
22472
+ }).passthrough().describe("Audience receiving the grant."),
22473
+ rule: external_exports.object({
22474
+ operation: external_exports.enum(["CREATE_USER", "READ_USER", "UPDATE_USER", "DELETE_USER", "LIST_USER", "MANAGE_USER_ENROLLMENT", "CREATE_SERVICE_IDENTITY", "READ_SERVICE_IDENTITY", "UPDATE_SERVICE_IDENTITY", "DELETE_SERVICE_IDENTITY", "LIST_SERVICE_IDENTITY", "CREATE_SPACE", "READ_SPACE", "UPDATE_SPACE", "DELETE_SPACE", "LIST_SPACE", "CREATE_API_KEY", "READ_API_KEY", "UPDATE_API_KEY", "DELETE_API_KEY", "LIST_API_KEY", "CREATE_EMBEDDER", "READ_EMBEDDER", "UPDATE_EMBEDDER", "DELETE_EMBEDDER", "LIST_EMBEDDER", "PING_EMBEDDER", "EXECUTE_EMBEDDER", "READ_EMBEDDER_CREDENTIALS", "CREATE_RERANKER", "READ_RERANKER", "UPDATE_RERANKER", "DELETE_RERANKER", "LIST_RERANKER", "PING_RERANKER", "EXECUTE_RERANKER", "READ_RERANKER_CREDENTIALS", "CREATE_LLM", "READ_LLM", "UPDATE_LLM", "DELETE_LLM", "LIST_LLM", "PING_LLM", "EXECUTE_LLM", "READ_LLM_CREDENTIALS", "PROXY_INFERENCE_TARGET", "OCR_DOCUMENT", "CREATE_MEMORY", "READ_MEMORY", "DELETE_MEMORY", "LIST_MEMORY", "CREATE_EXTENSION", "READ_EXTENSION", "UPDATE_EXTENSION", "DELETE_EXTENSION", "LIST_EXTENSION", "DOWNLOAD_EXTENSION", "READ_INSTANCE", "TRANSFER_INSTANCE_OWNERSHIP", "TRANSFER_RESOURCE_OWNERSHIP", "RELOAD_LICENSE", "DRAIN_SERVER", "PURGE_BACKGROUND_JOBS", "CREATE_RETRIEVE_MEMORY_LOG_POLICY", "READ_RETRIEVE_MEMORY_LOG_POLICY", "LIST_RETRIEVE_MEMORY_LOG_POLICY", "DELETE_RETRIEVE_MEMORY_LOG_POLICY", "MANAGE_ACCESS"]).describe("Protected operation."),
22475
+ selector: external_exports.enum(["ANY", "OWN", "EXACT", "DIRECT_MEMBERS_OF"]).describe("Resource-selection semantics."),
22476
+ assignedResource: external_exports.object({
22477
+ kind: external_exports.enum(["INSTANCE", "USER", "SERVICE_IDENTITY", "SPACE", "API_KEY", "EMBEDDER", "RERANKER", "LLM", "MEMORY", "EXTENSION", "RETRIEVE_MEMORY_LOG_POLICY"]).describe("Concrete target kind."),
22478
+ resourceId: external_exports.string().describe("Concrete resource UUID; omitted for the singleton INSTANCE target.").optional()
22479
+ }).passthrough().describe("Required exactly for EXACT and DIRECT_MEMBERS_OF selectors.").optional()
22480
+ }).passthrough().describe("Authorization descriptor to grant.")
22481
+ }, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
22482
+ const body = {};
22483
+ if (args.grant_id !== void 0)
22484
+ body["grantId"] = args.grant_id;
22485
+ if (args.audience !== void 0)
22486
+ body["audience"] = args.audience;
22487
+ if (args.rule !== void 0)
22488
+ body["rule"] = args.rule;
22489
+ const result = await callApi("POST", `/v1/access-policy/grants`, body);
22490
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22491
+ });
22492
+ server.tool("goodmem_access_policy_grants_delete", "Revoke an authorization grant\n\nSoft-revokes one grant and returns its durable historical row. Repeating the request is idempotent while the caller retains MANAGE_ACCESS on the target.", {
22493
+ id: external_exports.string().describe("Grant UUID")
22494
+ }, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
22495
+ if (typeof args.id !== "string" || args.id === "") {
22496
+ throw new Error("'id' must be a non-empty string");
22497
+ }
22498
+ await callApi("DELETE", `/v1/access-policy/grants/${encodeURIComponent(String(args.id))}`, void 0);
22499
+ return { content: [{ type: "text", text: "Deleted successfully." }] };
22500
+ });
22501
+ server.tool("goodmem_access_policy_grants_get", "Get an authorization grant\n\nReads one live grant, or one revoked historical grant when include_revoked is true, after requiring MANAGE_ACCESS on its policy target.", {
22502
+ id: external_exports.string().describe("Grant UUID"),
22503
+ include_revoked: external_exports.boolean().describe("Include a revoked historical row").optional()
22504
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
22505
+ if (typeof args.id !== "string" || args.id === "") {
22506
+ throw new Error("'id' must be a non-empty string");
22507
+ }
22508
+ const query = {};
22509
+ if (args.include_revoked !== void 0)
22510
+ query["includeRevoked"] = String(args.include_revoked);
22511
+ const result = await callApi("GET", `/v1/access-policy/grants/${encodeURIComponent(String(args.id))}`, void 0, query);
22512
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22513
+ });
22514
+ server.tool("goodmem_access_policy_grants_list", "List authorization grants\n\nLists grants attached to one resource. MANAGE_ACCESS is required on that resource; continuation tokens are bound to the caller and filters.", {
22515
+ resource_kind: external_exports.enum(["INSTANCE", "USER", "SERVICE_IDENTITY", "SPACE", "API_KEY", "EMBEDDER", "RERANKER", "LLM", "MEMORY", "EXTENSION", "RETRIEVE_MEMORY_LOG_POLICY"]).describe("Required target resource kind"),
22516
+ resource_id: external_exports.string().describe("Required target UUID except when resource_kind is INSTANCE").optional(),
22517
+ include_revoked: external_exports.boolean().describe("Include revoked history").optional(),
22518
+ max_results: external_exports.number().int().describe("Page size; 0 or omission uses the default of 50, maximum 1,000").optional(),
22519
+ next_token: external_exports.string().describe("Opaque continuation token").optional()
22520
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
22521
+ const query = {};
22522
+ if (args.resource_kind !== void 0)
22523
+ query["resourceKind"] = String(args.resource_kind);
22524
+ if (args.resource_id !== void 0)
22525
+ query["resourceId"] = String(args.resource_id);
22526
+ if (args.include_revoked !== void 0)
22527
+ query["includeRevoked"] = String(args.include_revoked);
22528
+ if (args.max_results !== void 0)
22529
+ query["maxResults"] = String(args.max_results);
22530
+ if (args.next_token !== void 0)
22531
+ query["nextToken"] = String(args.next_token);
22532
+ const result = await callApi("GET", `/v1/access-policy/grants`, void 0, query);
22533
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22534
+ });
22535
+ server.tool("goodmem_access_policy_role_assignments_create", "Assign a scoped role\n\nAssigns one code-defined role to an active principal at INSTANCE or SPACE scope after requiring MANAGE_ACCESS. ROOT is maintained only by ownership workflows.", {
22536
+ role_assignment_id: external_exports.string().describe("Optional caller-provided role-assignment UUID.").optional(),
22537
+ principal_id: external_exports.string().describe("Active principal receiving the role."),
22538
+ role: external_exports.enum(["ADMIN", "USER", "SPACE_VIEWER", "SPACE_CONTRIBUTOR", "SPACE_CONTENT_MANAGER", "SPACE_ADMIN"]).describe("Code-defined non-ROOT role to assign."),
22539
+ assigned_resource: external_exports.object({
22540
+ kind: external_exports.enum(["INSTANCE", "SPACE"]).describe("Role-assignment target kind."),
22541
+ resourceId: external_exports.string().describe("Memory-space UUID; omitted for the singleton INSTANCE target.").optional()
22542
+ }).passthrough().describe("INSTANCE or SPACE boundary receiving the assignment.")
22543
+ }, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
22544
+ const body = {};
22545
+ if (args.role_assignment_id !== void 0)
22546
+ body["roleAssignmentId"] = args.role_assignment_id;
22547
+ if (args.principal_id !== void 0)
22548
+ body["principalId"] = args.principal_id;
22549
+ if (args.role !== void 0)
22550
+ body["role"] = args.role;
22551
+ if (args.assigned_resource !== void 0)
22552
+ body["assignedResource"] = args.assigned_resource;
22553
+ const result = await callApi("POST", `/v1/access-policy/role-assignments`, body);
22554
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22555
+ });
22556
+ server.tool("goodmem_access_policy_role_assignments_delete", "Revoke a scoped role assignment\n\nSoft-revokes one non-ROOT assignment and returns its durable historical row. Repeating the request is idempotent while the caller retains MANAGE_ACCESS.", {
22557
+ id: external_exports.string().describe("Role-assignment UUID")
22558
+ }, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
22559
+ if (typeof args.id !== "string" || args.id === "") {
22560
+ throw new Error("'id' must be a non-empty string");
22561
+ }
22562
+ await callApi("DELETE", `/v1/access-policy/role-assignments/${encodeURIComponent(String(args.id))}`, void 0);
22563
+ return { content: [{ type: "text", text: "Deleted successfully." }] };
22564
+ });
22565
+ server.tool("goodmem_access_policy_role_assignments_get", "Get a scoped role assignment\n\nReads one live assignment, or one revoked historical assignment when include_revoked is true, after requiring MANAGE_ACCESS on its policy target.", {
22566
+ id: external_exports.string().describe("Role-assignment UUID"),
22567
+ include_revoked: external_exports.boolean().describe("Include a revoked historical row").optional()
22568
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
22569
+ if (typeof args.id !== "string" || args.id === "") {
22570
+ throw new Error("'id' must be a non-empty string");
22571
+ }
22572
+ const query = {};
22573
+ if (args.include_revoked !== void 0)
22574
+ query["includeRevoked"] = String(args.include_revoked);
22575
+ const result = await callApi("GET", `/v1/access-policy/role-assignments/${encodeURIComponent(String(args.id))}`, void 0, query);
22576
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22577
+ });
22578
+ server.tool("goodmem_access_policy_role_assignments_list", "List scoped role assignments\n\nLists assignments attached to one required INSTANCE or SPACE boundary after requiring MANAGE_ACCESS. Continuation tokens are bound to the caller and filters.", {
22579
+ resource_kind: external_exports.enum(["INSTANCE", "SPACE"]).describe("Required INSTANCE or SPACE kind"),
22580
+ resource_id: external_exports.string().describe("Required space UUID; omitted for INSTANCE").optional(),
22581
+ include_revoked: external_exports.boolean().describe("Include revoked history").optional(),
22582
+ max_results: external_exports.number().int().describe("Page size; 0 or omission uses the default of 50, maximum 1,000").optional(),
22583
+ next_token: external_exports.string().describe("Opaque continuation token").optional()
22584
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
22585
+ const query = {};
22586
+ if (args.resource_kind !== void 0)
22587
+ query["resourceKind"] = String(args.resource_kind);
22588
+ if (args.resource_id !== void 0)
22589
+ query["resourceId"] = String(args.resource_id);
22590
+ if (args.include_revoked !== void 0)
22591
+ query["includeRevoked"] = String(args.include_revoked);
22592
+ if (args.max_results !== void 0)
22593
+ query["maxResults"] = String(args.max_results);
22594
+ if (args.next_token !== void 0)
22595
+ query["nextToken"] = String(args.next_token);
22596
+ const result = await callApi("GET", `/v1/access-policy/role-assignments`, void 0, query);
22597
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22598
+ });
21966
22599
  server.tool("goodmem_admin_background_jobs_purge", "Purge completed background jobs\n\nDeletes terminal background jobs older than a retention cutoff.", {
21967
22600
  older_than: external_exports.string().describe("ISO-8601 timestamp cutoff; only terminal jobs older than this instant are eligible."),
21968
22601
  statuses: external_exports.array(external_exports.enum(["BACKGROUND_JOB_SUCCEEDED", "BACKGROUND_JOB_FAILED", "BACKGROUND_JOB_CANCELED"])).describe("Optional terminal background job statuses to target for purging. If omitted, all terminal statuses are eligible. Canonical values are BACKGROUND_JOB_SUCCEEDED, BACKGROUND_JOB_FAILED, and BACKGROUND_JOB_CANCELED. Short aliases SUCCEEDED, FAILED, and CANCELED are also accepted for compatibility.").optional(),
21969
22602
  dry_run: external_exports.boolean().describe("If true, report purge counts without deleting any rows.").optional(),
21970
22603
  limit: external_exports.number().int().describe("Maximum number of jobs to purge in this request. Must be >= 0; 0 means no limit.").optional()
21971
- }, async (args) => {
22604
+ }, { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false }, async (args) => {
21972
22605
  const body = {};
21973
22606
  if (args.older_than !== void 0)
21974
22607
  body["olderThan"] = args.older_than;
@@ -21985,7 +22618,7 @@ function registerTools() {
21985
22618
  timeout_sec: external_exports.number().int().describe("Maximum seconds to wait for the server to quiesce before returning.").optional(),
21986
22619
  reason: external_exports.string().describe("Human-readable reason for initiating drain mode.").optional(),
21987
22620
  wait_for_quiesce: external_exports.boolean().describe("If true, wait for in-flight requests to complete and the server to reach QUIESCED before responding.").optional()
21988
- }, async (args) => {
22621
+ }, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
21989
22622
  const body = {};
21990
22623
  if (args.timeout_sec !== void 0)
21991
22624
  body["timeoutSec"] = args.timeout_sec;
@@ -21996,7 +22629,7 @@ function registerTools() {
21996
22629
  const result = await callApi("POST", `/v1/admin:drain`, body, void 0, ((args.timeout_sec ?? 900) + 30) * 1e3);
21997
22630
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
21998
22631
  });
21999
- server.tool("goodmem_admin_license_reload", "Reload the active license from disk\n\nTriggers the server to reload its license file from the configured directory and returns metadata about the currently active license.", {}, async (args) => {
22632
+ server.tool("goodmem_admin_license_reload", "Reload the active license from disk\n\nTriggers the server to reload its license file from the configured directory and returns metadata about the currently active license.", {}, { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }, async (args) => {
22000
22633
  const result = await callApi("POST", `/v1/admin/license:reload`, void 0);
22001
22634
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22002
22635
  });
@@ -22017,7 +22650,7 @@ function registerTools() {
22017
22650
  active_from: external_exports.number().int().describe("Inclusive activation time in milliseconds since epoch.").optional(),
22018
22651
  active_until: external_exports.number().int().describe("Exclusive deactivation time in milliseconds since epoch.").optional(),
22019
22652
  labels: external_exports.record(external_exports.string(), external_exports.string()).describe("Operator labels for listing and administration.").optional()
22020
- }, async (args) => {
22653
+ }, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
22021
22654
  const body = {};
22022
22655
  if (args.policy_id !== void 0)
22023
22656
  body["policyId"] = args.policy_id;
@@ -22039,7 +22672,7 @@ function registerTools() {
22039
22672
  server.tool("goodmem_admin_retrieve_memory_log_policies_delete", "Delete a RetrieveMemory log policy\n\nIdempotently tombstones an immutable RetrieveMemory log policy.", {
22040
22673
  id: external_exports.string().describe("The UUID of the policy to delete"),
22041
22674
  reason: external_exports.string().describe("Optional tombstone reason.").optional()
22042
- }, async (args) => {
22675
+ }, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
22043
22676
  if (typeof args.id !== "string" || args.id === "") {
22044
22677
  throw new Error("'id' must be a non-empty string");
22045
22678
  }
@@ -22052,7 +22685,7 @@ function registerTools() {
22052
22685
  server.tool("goodmem_admin_retrieve_memory_log_policies_get", "Get a RetrieveMemory log policy\n\nRetrieves a live RetrieveMemory log policy by UUID, or a tombstoned policy when include_deleted is true.", {
22053
22686
  id: external_exports.string().describe("The UUID of the policy to retrieve"),
22054
22687
  include_deleted: external_exports.boolean().describe("Whether to include tombstoned policies. Also accepts include_deleted.").optional()
22055
- }, async (args) => {
22688
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
22056
22689
  if (typeof args.id !== "string" || args.id === "") {
22057
22690
  throw new Error("'id' must be a non-empty string");
22058
22691
  }
@@ -22071,7 +22704,7 @@ function registerTools() {
22071
22704
  sort_by: external_exports.enum(["created_at", "updated_at", "display_name"]).describe("Sort field: created_at, updated_at, or display_name. Also accepts sort_by.").optional(),
22072
22705
  sort_order: external_exports.enum(["ASCENDING", "DESCENDING", "SORT_ORDER_UNSPECIFIED"]).describe("Sort order. Also accepts sort_order.").optional(),
22073
22706
  label: external_exports.record(external_exports.string(), external_exports.string()).describe("Filter by label. Each key-value pair is sent as label[key]=value query parameter.").optional()
22074
- }, async (args) => {
22707
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
22075
22708
  const query = {};
22076
22709
  if (args.include_deleted !== void 0)
22077
22710
  query["includeDeleted"] = String(args.include_deleted);
@@ -22094,11 +22727,31 @@ function registerTools() {
22094
22727
  const result = await callApi("GET", `/v1/admin/retrieve-memory-log-policies`, void 0, query);
22095
22728
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22096
22729
  });
22097
- server.tool("goodmem_apikeys_create", "Create a new API key\n\nCreates a new API key for authenticating with the API. New keys start in ACTIVE status and can be used immediately for authentication. The response includes the one-time raw API key value which will not be retrievable later - clients must save this value as it cannot be recovered. Requires CREATE_APIKEY_OWN permission (or CREATE_APIKEY_ANY for admin users). Side effects include generating cryptographically secure key material, recording creation timestamp, and tracking creator ID. Returns INVALID_ARGUMENT if expires_at is set to a time in the past.", {
22098
- labels: external_exports.record(external_exports.string(), external_exports.string()).describe("Key-value pairs of metadata associated with the API key. Used for organization and filtering.").optional(),
22099
- expires_at: external_exports.number().int().describe("Expiration timestamp in milliseconds since epoch. If not provided, the key does not expire.").optional(),
22100
- api_key_id: external_exports.string().describe("Optional client-provided UUID for idempotent creation. If not provided, server generates a new UUID. Returns ALREADY_EXISTS if ID is already in use.").optional()
22101
- }, async (args) => {
22730
+ server.tool("goodmem_admin_transfer_instance_ownership", "Transfer GoodMem instance ownership\n\nTransfers the singleton GoodMem instance to another active human principal. Only the current instance owner may invoke this operation; ADMIN, MANAGE_ACCESS, and ordinary grants are insufficient. Ownership and the synthetic ROOT assignment move atomically. All ordinary roles, including ADMIN, remain unchanged. No credential is created or returned. After an unknown outcome, read the current owner before retrying.", {
22731
+ new_owner_id: external_exports.string().describe("Existing principal UUID that will become the new owner.")
22732
+ }, { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false }, async (args) => {
22733
+ const body = {};
22734
+ if (args.new_owner_id !== void 0)
22735
+ body["newOwnerId"] = args.new_owner_id;
22736
+ const result = await callApi("POST", `/v1/admin:transferInstanceOwnership`, body);
22737
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22738
+ });
22739
+ server.tool("goodmem_apikeys_create", "Create a new API key\n\nIssues a new API key and returns its raw value exactly once. Omitted subject and authority_mode create a self-issued human key that inherits the subject's live authority. A SCOPED key requires a nonempty immutable ceiling, and every ceiling rule must be conservatively covered by both the subject's live authority and the issuing credential's effective authority. A scoped issuer can create only scoped children. SERVICE subjects require SCOPED mode and MANAGE_ACCESS on the service identity. AUTHORIZATION: Requires CREATE_API_KEY on the proposed credential; subject and ceiling checks are repeated by the final insertion statement.", {
22740
+ labels: external_exports.record(external_exports.string(), external_exports.string()).describe("Key-value pairs of metadata associated with the API key. Used for organization and filtering. At most 20 entries; keys and values contain at most 255 characters; keys use [a-z0-9._-].").optional(),
22741
+ expires_at: external_exports.number().int().describe("Exclusive expiration timestamp in milliseconds since epoch. It must be later than valid_from, which defaults to issuance time; if omitted, the key does not expire.").optional(),
22742
+ api_key_id: external_exports.string().describe("Optional client-provided UUID for idempotent creation. If not provided, server generates a new UUID. Returns ALREADY_EXISTS if ID is already in use.").optional(),
22743
+ subject_principal_id: external_exports.string().describe("Principal authenticated by this key. Omit to use the authenticated principal.").optional(),
22744
+ authority_mode: external_exports.enum(["INHERIT_SUBJECT", "SCOPED"]).describe("Authority mode. Omit to create a self-issued human key that inherits live authority. A scoped issuing credential may create only SCOPED children.").optional(),
22745
+ ceiling: external_exports.array(external_exports.object({
22746
+ operation: external_exports.enum(["CREATE_USER", "READ_USER", "UPDATE_USER", "DELETE_USER", "LIST_USER", "MANAGE_USER_ENROLLMENT", "CREATE_SERVICE_IDENTITY", "READ_SERVICE_IDENTITY", "UPDATE_SERVICE_IDENTITY", "DELETE_SERVICE_IDENTITY", "LIST_SERVICE_IDENTITY", "CREATE_SPACE", "READ_SPACE", "UPDATE_SPACE", "DELETE_SPACE", "LIST_SPACE", "CREATE_API_KEY", "READ_API_KEY", "UPDATE_API_KEY", "DELETE_API_KEY", "LIST_API_KEY", "CREATE_EMBEDDER", "READ_EMBEDDER", "UPDATE_EMBEDDER", "DELETE_EMBEDDER", "LIST_EMBEDDER", "PING_EMBEDDER", "EXECUTE_EMBEDDER", "READ_EMBEDDER_CREDENTIALS", "CREATE_RERANKER", "READ_RERANKER", "UPDATE_RERANKER", "DELETE_RERANKER", "LIST_RERANKER", "PING_RERANKER", "EXECUTE_RERANKER", "READ_RERANKER_CREDENTIALS", "CREATE_LLM", "READ_LLM", "UPDATE_LLM", "DELETE_LLM", "LIST_LLM", "PING_LLM", "EXECUTE_LLM", "READ_LLM_CREDENTIALS", "PROXY_INFERENCE_TARGET", "OCR_DOCUMENT", "CREATE_MEMORY", "READ_MEMORY", "DELETE_MEMORY", "LIST_MEMORY", "CREATE_EXTENSION", "READ_EXTENSION", "UPDATE_EXTENSION", "DELETE_EXTENSION", "LIST_EXTENSION", "DOWNLOAD_EXTENSION", "READ_INSTANCE", "TRANSFER_INSTANCE_OWNERSHIP", "TRANSFER_RESOURCE_OWNERSHIP", "RELOAD_LICENSE", "DRAIN_SERVER", "PURGE_BACKGROUND_JOBS", "CREATE_RETRIEVE_MEMORY_LOG_POLICY", "READ_RETRIEVE_MEMORY_LOG_POLICY", "LIST_RETRIEVE_MEMORY_LOG_POLICY", "DELETE_RETRIEVE_MEMORY_LOG_POLICY", "MANAGE_ACCESS"]).describe("Protected operation."),
22747
+ selector: external_exports.enum(["ANY", "OWN", "EXACT", "DIRECT_MEMBERS_OF"]).describe("Resource-selection semantics."),
22748
+ assignedResource: external_exports.object({
22749
+ kind: external_exports.enum(["INSTANCE", "USER", "SERVICE_IDENTITY", "SPACE", "API_KEY", "EMBEDDER", "RERANKER", "LLM", "MEMORY", "EXTENSION", "RETRIEVE_MEMORY_LOG_POLICY"]).describe("Concrete target kind."),
22750
+ resourceId: external_exports.string().describe("Concrete resource UUID; omitted for the singleton INSTANCE target.").optional()
22751
+ }).passthrough().describe("Required exactly for EXACT and DIRECT_MEMBERS_OF selectors.").optional()
22752
+ }).passthrough()).describe("Immutable authorization ceiling. Required and nonempty for SCOPED; omitted for INHERIT_SUBJECT, with at most 1,000 rules. Every rule must be covered by both the subject's live authority and the issuing credential's effective authority.").optional(),
22753
+ valid_from: external_exports.number().int().describe("Inclusive activation time in epoch milliseconds. Omit to activate at issuance time.").optional()
22754
+ }, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
22102
22755
  const body = {};
22103
22756
  if (args.labels !== void 0)
22104
22757
  body["labels"] = args.labels;
@@ -22106,28 +22759,65 @@ function registerTools() {
22106
22759
  body["expiresAt"] = args.expires_at;
22107
22760
  if (args.api_key_id !== void 0)
22108
22761
  body["apiKeyId"] = args.api_key_id;
22762
+ if (args.subject_principal_id !== void 0)
22763
+ body["subjectPrincipalId"] = args.subject_principal_id;
22764
+ if (args.authority_mode !== void 0)
22765
+ body["authorityMode"] = args.authority_mode;
22766
+ if (args.ceiling !== void 0)
22767
+ body["ceiling"] = args.ceiling;
22768
+ if (args.valid_from !== void 0)
22769
+ body["validFrom"] = args.valid_from;
22109
22770
  const result = await callApi("POST", `/v1/apikeys`, body);
22110
22771
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22111
22772
  });
22112
- server.tool("goodmem_apikeys_delete", 'Delete an API key\n\nPermanently deletes an API key. This operation cannot be undone and immediately invalidates the key for all future authentication attempts. TIP: For reversible deactivation, use the `goodmem_apikeys_update` tool with `status: "INACTIVE"` instead. Requires DELETE_APIKEY_OWN permission for keys you own (or DELETE_APIKEY_ANY for admin users to delete any user\'s keys). Side effects include permanently removing the key record from the database and immediate authentication invalidation.', {
22773
+ server.tool("goodmem_apikeys_delete", 'Delete an API key\n\nPermanently revokes an API key and immediately rejects it for future authentication. The durable credential and audit history remain stored. This operation requires DELETE_API_KEY and records the revocation time and actor; it cannot be undone. The `goodmem_apikeys_update` tool with `status: "INACTIVE"` performs the same permanent revocation; revoked keys cannot be reactivated.', {
22113
22774
  id: external_exports.string().describe("The UUID of the API key to delete")
22114
- }, async (args) => {
22775
+ }, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
22115
22776
  if (typeof args.id !== "string" || args.id === "") {
22116
22777
  throw new Error("'id' must be a non-empty string");
22117
22778
  }
22118
22779
  await callApi("DELETE", `/v1/apikeys/${encodeURIComponent(String(args.id))}`, void 0);
22119
22780
  return { content: [{ type: "text", text: "Deleted successfully." }] };
22120
22781
  });
22121
- server.tool("goodmem_apikeys_list", "List API keys\n\nRetrieves a list of API keys belonging to the authenticated user. The list includes metadata about each key but not the actual key values or key hashes for security reasons. Requires LIST_APIKEY_OWN permission (or LIST_APIKEY_ANY for admin users to view keys from any user). This is a read-only operation with no side effects.", {}, async (args) => {
22122
- const result = await callApi("GET", `/v1/apikeys`, void 0);
22782
+ server.tool("goodmem_apikeys_get", "Get an API key\n\nReturns complete non-secret metadata for one existing credential after requiring effective READ_API_KEY authority. The immutable ceiling is always complete and ceilingOmitted is false. Raw key material and hashes are never returned.", {
22783
+ id: external_exports.string().describe("API-key UUID")
22784
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
22785
+ if (typeof args.id !== "string" || args.id === "") {
22786
+ throw new Error("'id' must be a non-empty string");
22787
+ }
22788
+ const result = await callApi("GET", `/v1/apikeys/${encodeURIComponent(String(args.id))}`, void 0);
22789
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22790
+ });
22791
+ server.tool("goodmem_apikeys_list", "List API keys\n\nRetrieves one UUID-ordered page of API keys discoverable under the authenticated principal's effective API-key policy. Subject, owner, and lifecycle filters are applied after authorization. FULL includes complete immutable ceilings; BASIC omits them, sets ceilingOmitted=true, and permits larger pages. Raw key values and key hashes are never returned.", {
22792
+ subject_principal_id: external_exports.string().describe("Filter by exact subject-principal UUID").optional(),
22793
+ owner_principal_id: external_exports.string().describe("Filter by exact administrative-owner UUID").optional(),
22794
+ lifecycle_state: external_exports.enum(["NOT_YET_VALID", "USABLE", "EXPIRED", "REVOKED"]).describe("Filter by precise lifecycle state").optional(),
22795
+ view: external_exports.enum(["FULL", "BASIC"]).describe("Metadata projection; omission defaults to FULL").optional(),
22796
+ max_results: external_exports.number().int().describe("Page size; FULL defaults to 10 and permits at most 20, while BASIC defaults to 50 and permits at most 1,000").optional(),
22797
+ next_token: external_exports.string().describe("Opaque continuation token returned by the preceding page").optional()
22798
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
22799
+ const query = {};
22800
+ if (args.subject_principal_id !== void 0)
22801
+ query["subjectPrincipalId"] = String(args.subject_principal_id);
22802
+ if (args.owner_principal_id !== void 0)
22803
+ query["ownerPrincipalId"] = String(args.owner_principal_id);
22804
+ if (args.lifecycle_state !== void 0)
22805
+ query["lifecycleState"] = String(args.lifecycle_state);
22806
+ if (args.view !== void 0)
22807
+ query["view"] = String(args.view);
22808
+ if (args.max_results !== void 0)
22809
+ query["maxResults"] = String(args.max_results);
22810
+ if (args.next_token !== void 0)
22811
+ query["nextToken"] = String(args.next_token);
22812
+ const result = await callApi("GET", `/v1/apikeys`, void 0, query);
22123
22813
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22124
22814
  });
22125
- server.tool("goodmem_apikeys_update", "Update an API key\n\nUpdates an existing API key with new values for status or labels. IMPORTANT: Key ID, user ownership, key material, and audit fields cannot be modified - only status (ACTIVE/INACTIVE) and labels are mutable. Requires UPDATE_APIKEY_OWN permission for keys you own (or UPDATE_APIKEY_ANY for admin users to modify any user's keys). Side effects include updating the updated_at timestamp and recording the updater's user ID. This operation is idempotent - repeated identical requests have no additional effect.", {
22815
+ server.tool("goodmem_apikeys_update", "Update an API key\n\nUpdates an existing API key's labels or lifecycle status. Key ID, subject, ownership, key material, validity window, and creation audit fields remain immutable. Label changes require UPDATE_API_KEY; setting status=INACTIVE permanently revokes the key and requires DELETE_API_KEY; a request doing both requires both operations. Revoked keys cannot be reactivated. Side effects include updating administrative audit fields and, for revocation, recording the revocation time and actor.", {
22126
22816
  id: external_exports.string().describe("The UUID of the API key to update"),
22127
- status: external_exports.enum(["ACTIVE", "INACTIVE"]).describe("New status for the API key. Allowed values: ACTIVE, INACTIVE.").optional(),
22128
- replace_labels: external_exports.record(external_exports.string(), external_exports.string()).describe("Replace all existing labels with this set. Mutually exclusive with merge_labels.").optional(),
22129
- merge_labels: external_exports.record(external_exports.string(), external_exports.string()).describe("Merge these labels with existing ones. Mutually exclusive with replace_labels.").optional()
22130
- }, async (args) => {
22817
+ status: external_exports.enum(["ACTIVE", "INACTIVE"]).describe("New status for the API key. INACTIVE is permanent; revoked keys cannot be reactivated.").optional(),
22818
+ replace_labels: external_exports.record(external_exports.string(), external_exports.string()).describe("Replace all existing labels with this set. Mutually exclusive with merge_labels. The stored map may contain at most 20 entries; keys and values contain at most 255 characters; keys use [a-z0-9._-].").optional(),
22819
+ merge_labels: external_exports.record(external_exports.string(), external_exports.string()).describe("Merge these labels with existing ones. Mutually exclusive with replace_labels. The final stored map may contain at most 20 entries; keys and values contain at most 255 characters; keys use [a-z0-9._-].").optional()
22820
+ }, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
22131
22821
  if (typeof args.id !== "string" || args.id === "") {
22132
22822
  throw new Error("'id' must be a non-empty string");
22133
22823
  }
@@ -22145,12 +22835,12 @@ function registerTools() {
22145
22835
  const result = await callApi("PUT", `/v1/apikeys/${encodeURIComponent(String(args.id))}`, body);
22146
22836
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22147
22837
  });
22148
- server.tool("goodmem_embedders_create", "Create a new embedder\n\nCreates a new embedder configuration for vectorizing content. Embedders represent connections to different embedding API services (like OpenAI, vLLM, etc.) and include all the necessary configuration to use them with memory spaces. DUPLICATE DETECTION: Returns HTTP 409 Conflict (ALREADY_EXISTS) if another embedder exists with identical {owner_id, provider_type, endpoint_url, api_path, model_identifier, dimensionality, distribution_type, credentialsFingerprint} after URL canonicalization. Uniqueness is enforced per-owner, allowing different users to have identical configurations. Credentials are hashed (SHA-256) for uniqueness while remaining encrypted. The api_path field defaults to '/v2/embed' for Cohere, '/embed' for TEI, and '/embeddings' for other providers when omitted. Requires CREATE_EMBEDDER_OWN permission (or CREATE_EMBEDDER_ANY for admin users).", {
22838
+ server.tool("goodmem_embedders_create", "Create a new embedder\n\nCreates an embedder configuration for use with memory spaces. If owner_id is omitted, the authenticated principal becomes the owner; CREATE_EMBEDDER is evaluated against that proposed embedder and owner. Returns 409 when an equivalent embedder configuration already exists for the owner. See the [embedder provider guide](https://github.com/PAIR-Systems-Inc/goodmem/blob/main/docs/guides/embedder-providers.md) for provider-specific configuration.", {
22149
22839
  display_name: external_exports.string().describe("User-facing name of the embedder"),
22150
22840
  description: external_exports.string().describe("Description of the embedder").optional(),
22151
- provider_type: external_exports.enum(["OPENAI", "VLLM", "TEI", "LLAMA_CPP", "VOYAGE", "COHERE", "JINA"]).describe("Type of embedding provider (auto-inferred from model_identifier if omitted)").optional(),
22152
- endpoint_url: external_exports.string().describe("API endpoint URL (auto-inferred from model_identifier if omitted)").optional(),
22153
- api_path: external_exports.string().describe("API path for embeddings request (defaults: Cohere /v2/embed, TEI /embed, others /embeddings)").optional(),
22841
+ provider_type: external_exports.enum(["OPENAI", "VLLM", "TEI", "LLAMA_CPP", "VOYAGE", "COHERE", "JINA", "DASHSCOPE", "GEMINI"]).describe("Type of embedding provider (auto-inferred from model_identifier if omitted)").optional(),
22842
+ endpoint_url: external_exports.string().describe("Base HTTP(S) endpoint for provider requests. Gemini endpoint URLs must not contain query parameters. (auto-inferred from model_identifier if omitted)").optional(),
22843
+ api_path: external_exports.string().describe("Provider-relative request path. Omit or send blank to use the provider default. For Gemini, this is an API version: /v1beta for Developer and /v1 for Google Cloud.").optional(),
22154
22844
  model_identifier: external_exports.string().describe("Model identifier"),
22155
22845
  dimensionality: external_exports.number().int().describe("Output vector dimensions (auto-inferred from model_identifier if omitted)").optional(),
22156
22846
  distribution_type: external_exports.enum(["DENSE", "SPARSE"]).describe("Type of embedding distribution (DENSE or SPARSE) (auto-inferred from model_identifier if omitted)").optional(),
@@ -22176,9 +22866,15 @@ function registerTools() {
22176
22866
  labels: external_exports.record(external_exports.string(), external_exports.string()).describe("User-defined labels for categorization").optional(),
22177
22867
  version: external_exports.string().describe("Version information").optional(),
22178
22868
  monitoring_endpoint: external_exports.string().describe("Monitoring endpoint URL").optional(),
22179
- owner_id: external_exports.string().describe("Optional owner ID. If not provided, derived from the authentication context. Requires CREATE_EMBEDDER_ANY permission if specified.").optional(),
22180
- embedder_id: external_exports.string().describe("Optional client-provided UUID for idempotent creation. If not provided, server generates a new UUID. Returns ALREADY_EXISTS if ID is already in use.").optional()
22181
- }, async (args) => {
22869
+ owner_id: external_exports.string().describe("Optional owner principal UUID. If omitted, defaults to the authenticated principal. CREATE_EMBEDDER is evaluated against the proposed embedder and owner.").optional(),
22870
+ embedder_id: external_exports.string().describe("Optional client-provided UUID for idempotent creation. If not provided, server generates a new UUID. Returns ALREADY_EXISTS if ID is already in use.").optional(),
22871
+ dashscope_api_dialect: external_exports.enum(["EMBEDDING_NATIVE_TEXT", "EMBEDDING_NATIVE_CONTENTS", "OPENAI_COMPATIBLE"]).describe("DashScope request and response API dialect. Valid only for the DASHSCOPE provider. Omit to infer the dialect from api_path, the model catalog, or the native text default.").optional(),
22872
+ gemini_endpoint_config: external_exports.object({
22873
+ backend: external_exports.enum(["UNSPECIFIED", "DEVELOPER", "GOOGLE_CLOUD"]).describe("Google API surface. UNSPECIFIED is invalid when this configuration is supplied on a write."),
22874
+ projectId: external_exports.string().describe("Google Cloud resource project. Required for GOOGLE_CLOUD and unused for DEVELOPER; this is distinct from the ADC quota project.").optional(),
22875
+ location: external_exports.string().describe("Google Cloud location. Valid only for GOOGLE_CLOUD; omission defaults to global.").optional()
22876
+ }).passthrough().describe("Gemini backend routing. Valid only for the GEMINI provider. Omit to use the Developer API; when present, backend is required and the gRPC service validates the backend-specific projectId and location contract.").optional()
22877
+ }, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
22182
22878
  const inferred = autoInferFromRegistry("embedders", args);
22183
22879
  checkCredentialRequired(inferred);
22184
22880
  const body = {};
@@ -22214,32 +22910,40 @@ function registerTools() {
22214
22910
  body["ownerId"] = inferred.owner_id;
22215
22911
  if (inferred.embedder_id !== void 0)
22216
22912
  body["embedderId"] = inferred.embedder_id;
22913
+ if (inferred.dashscope_api_dialect !== void 0)
22914
+ body["dashscopeApiDialect"] = inferred.dashscope_api_dialect;
22915
+ if (inferred.gemini_endpoint_config !== void 0)
22916
+ body["geminiEndpointConfig"] = inferred.gemini_endpoint_config;
22217
22917
  const result = await callApi("POST", `/v1/embedders`, body);
22218
22918
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22219
22919
  });
22220
- server.tool("goodmem_embedders_delete", "Delete an embedder\n\nPermanently deletes an embedder configuration. This operation cannot be undone and removes the embedder record and securely deletes stored credentials. IMPORTANT: This does NOT invalidate or delete embeddings previously created with this embedder - existing embeddings remain accessible. CONFLICT: Returns HTTP 409 Conflict if the embedder is still referenced by a space. Requires DELETE_EMBEDDER_OWN permission for embedders you own (or DELETE_EMBEDDER_ANY for admin users).", {
22920
+ server.tool("goodmem_embedders_delete", "Delete an embedder\n\nPermanently deletes an embedder configuration. This operation cannot be undone and removes the embedder record and securely deletes stored credentials. IMPORTANT: This does NOT invalidate or delete embeddings previously created with this embedder - existing embeddings remain accessible. CONFLICT: Returns HTTP 409 Conflict if the embedder is still referenced by a space. Requires DELETE_EMBEDDER on the requested embedder.", {
22221
22921
  id: external_exports.string().describe("The unique identifier of the embedder to delete")
22222
- }, async (args) => {
22922
+ }, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
22223
22923
  if (typeof args.id !== "string" || args.id === "") {
22224
22924
  throw new Error("'id' must be a non-empty string");
22225
22925
  }
22226
22926
  await callApi("DELETE", `/v1/embedders/${encodeURIComponent(String(args.id))}`, void 0);
22227
22927
  return { content: [{ type: "text", text: "Deleted successfully." }] };
22228
22928
  });
22229
- server.tool("goodmem_embedders_get", "Get an embedder by ID\n\nRetrieves the details of a specific embedder configuration by its unique identifier. Requires READ_EMBEDDER_OWN permission for embedders you own (or READ_EMBEDDER_ANY for admin users to view any user's embedders). This is a read-only operation with no side effects.", {
22230
- id: external_exports.string().describe("The unique identifier of the embedder to retrieve")
22231
- }, async (args) => {
22929
+ server.tool("goodmem_embedders_get", "Get an embedder by ID\n\nRetrieves the details of a specific embedder configuration by its unique identifier. Requires READ_EMBEDDER on the requested embedder. The service distinguishes a missing embedder from an existing embedder the caller cannot read. This is a read-only operation with no side effects.", {
22930
+ id: external_exports.string().describe("The unique identifier of the embedder to retrieve"),
22931
+ include_credentials: external_exports.boolean().describe("Whether to return stored credentials. Also accepts include_credentials. Requires READ_EMBEDDER_CREDENTIALS in addition to READ_EMBEDDER.").optional()
22932
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
22232
22933
  if (typeof args.id !== "string" || args.id === "") {
22233
22934
  throw new Error("'id' must be a non-empty string");
22234
22935
  }
22235
- const result = await callApi("GET", `/v1/embedders/${encodeURIComponent(String(args.id))}`, void 0);
22936
+ const query = {};
22937
+ if (args.include_credentials !== void 0)
22938
+ query["includeCredentials"] = String(args.include_credentials);
22939
+ const result = await callApi("GET", `/v1/embedders/${encodeURIComponent(String(args.id))}`, void 0, query);
22236
22940
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22237
22941
  });
22238
- server.tool("goodmem_embedders_list", "List embedders\n\nRetrieves a list of embedder configurations accessible to the caller, with optional filtering. LABEL FILTERS: Label filters accept either label.<key>=<value> or label[key]=value (for example, label.environment=production or label[environment]=production). PERMISSION-BASED FILTERING: With LIST_EMBEDDER_OWN permission, you can only see your own embedders (owner_id filter is ignored if set to another user). With LIST_EMBEDDER_ANY permission, you can see all embedders or filter by any owner_id. This is a read-only operation with no side effects.", {
22239
- owner_id: external_exports.string().describe("Filter embedders by owner ID. With LIST_EMBEDDER_ANY permission, omitting this shows all accessible embedders; providing it filters by that owner. With LIST_EMBEDDER_OWN permission, only your own embedders are shown regardless of this parameter.").optional(),
22240
- provider_type: external_exports.enum(["OPENAI", "VLLM", "TEI", "LLAMA_CPP", "VOYAGE", "COHERE", "JINA"]).describe("Filter embedders by provider type. Allowed values match the ProviderType schema.").optional(),
22942
+ server.tool("goodmem_embedders_list", "List embedders\n\nRetrieves a list of embedder configurations accessible to the caller, with optional filtering. LABEL FILTERS: Label filters accept either label.<key>=<value> or label[key]=value (for example, label.environment=production or label[environment]=production). AUTHORIZATION: Requires LIST_EMBEDDER on the GoodMem instance. Each returned embedder must also be visible through READ_EMBEDDER; unauthorized embedders are filtered in PostgreSQL. The owner_id parameter filters that already-authorized result set and does not grant additional visibility. This is a read-only operation with no side effects.", {
22943
+ owner_id: external_exports.string().describe("Filter the already-authorized result set by owner principal UUID. Omitting this parameter does not bypass per-embedder READ_EMBEDDER filtering.").optional(),
22944
+ provider_type: external_exports.enum(["OPENAI", "VLLM", "TEI", "LLAMA_CPP", "VOYAGE", "COHERE", "JINA", "DASHSCOPE", "GEMINI"]).describe("Filter embedders by provider type. Allowed values match the ProviderType schema.").optional(),
22241
22945
  label: external_exports.record(external_exports.string(), external_exports.string()).describe("Filter by label. Each key-value pair is sent as label[key]=value query parameter.").optional()
22242
- }, async (args) => {
22946
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
22243
22947
  const query = {};
22244
22948
  if (args.owner_id !== void 0)
22245
22949
  query["ownerId"] = String(args.owner_id);
@@ -22252,12 +22956,12 @@ function registerTools() {
22252
22956
  const result = await callApi("GET", `/v1/embedders`, void 0, query);
22253
22957
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22254
22958
  });
22255
- server.tool("goodmem_embedders_update", "Update an embedder\n\nUpdates an existing embedder configuration including display information, endpoint configuration, model parameters, credentials, and labels. All fields are optional - only specified fields will be updated. IMPORTANT: providerType is IMMUTABLE after creation and cannot be changed. CRITICAL: Returns HTTP 412 Precondition Failed (FAILED_PRECONDITION) if attempting to update core fields (dimensionality, distribution_type, model_identifier) while the embedder is actively referenced by spaces or ingestion jobs. DUPLICATE DETECTION: Returns HTTP 409 Conflict (ALREADY_EXISTS) if the update would create a duplicate embedder with the same provider, endpoint, model, dimensionality, distribution, and credentials for this owner. Requires UPDATE_EMBEDDER_OWN permission for embedders you own (or UPDATE_EMBEDDER_ANY for admin users).", {
22959
+ server.tool("goodmem_embedders_update", "Update an embedder\n\nUpdates explicitly supplied embedder fields; at least one mutable field is required. Field omission and reset semantics are defined by the request schema, and providerType cannot be changed. Returns 409 if the resulting configuration duplicates another embedder for the owner, and 412 when model-defining fields are changed while the embedder is in use. Requires UPDATE_EMBEDDER on the requested embedder. See the [embedder provider guide](https://github.com/PAIR-Systems-Inc/goodmem/blob/main/docs/guides/embedder-providers.md) for provider-specific configuration.", {
22256
22960
  id: external_exports.string().describe("The unique identifier of the embedder to update"),
22257
22961
  display_name: external_exports.string().describe("User-facing name of the embedder").optional(),
22258
22962
  description: external_exports.string().describe("Description of the embedder").optional(),
22259
- endpoint_url: external_exports.string().describe("API endpoint URL").optional(),
22260
- api_path: external_exports.string().describe("API path for embeddings request").optional(),
22963
+ endpoint_url: external_exports.string().describe("Replacement base HTTP(S) endpoint. Omit to preserve the stored value. Gemini endpoint URLs must not contain query parameters.").optional(),
22964
+ api_path: external_exports.string().describe("Replacement provider-relative request path. Omit to preserve the stored value, except that changing the Gemini backend without api_path selects that backend's default; send blank to restore the provider default. For Gemini, this is an API version: /v1beta for Developer and /v1 for Google Cloud.").optional(),
22261
22965
  model_identifier: external_exports.string().describe("Model identifier").optional(),
22262
22966
  dimensionality: external_exports.number().int().describe("Output vector dimensions").optional(),
22263
22967
  distribution_type: external_exports.enum(["DENSE", "SPARSE"]).describe("Type of embedding distribution (DENSE or SPARSE)").optional(),
@@ -22278,12 +22982,18 @@ function registerTools() {
22278
22982
  quotaProjectId: external_exports.string().describe("Optional quota project used for billing").optional()
22279
22983
  }).passthrough().describe("Configuration when kind is CREDENTIAL_KIND_GCP_ADC").optional(),
22280
22984
  labels: external_exports.record(external_exports.string(), external_exports.string()).describe('Optional annotations to aid operators (e.g., "owner=vertex")').optional()
22281
- }).passthrough().describe("Structured credential payload describing how to authenticate with the provider").optional(),
22985
+ }).passthrough().describe("Replace stored credentials. Omit this field to preserve the current credentials; a present empty payload is invalid and never clears them.").optional(),
22282
22986
  replace_labels: external_exports.record(external_exports.string(), external_exports.string()).describe("Replace all existing labels with these (mutually exclusive with merge_labels)").optional(),
22283
22987
  merge_labels: external_exports.record(external_exports.string(), external_exports.string()).describe("Merge these labels with existing ones (mutually exclusive with replace_labels)").optional(),
22284
22988
  version: external_exports.string().describe("Version information").optional(),
22285
- monitoring_endpoint: external_exports.string().describe("Monitoring endpoint URL").optional()
22286
- }, async (args) => {
22989
+ monitoring_endpoint: external_exports.string().describe("Monitoring endpoint URL").optional(),
22990
+ dashscope_api_dialect: external_exports.enum(["EMBEDDING_NATIVE_TEXT", "EMBEDDING_NATIVE_CONTENTS", "OPENAI_COMPATIBLE"]).describe("Update the DashScope request and response API dialect. Valid only for the DASHSCOPE provider. Omit to preserve the stored dialect. Changing api_path to a recognized canonical DashScope dialect infers its matching dialect; a custom path preserves an existing dialect, while a legacy null dialect is inferred.").optional(),
22991
+ gemini_endpoint_config: external_exports.object({
22992
+ backend: external_exports.enum(["UNSPECIFIED", "DEVELOPER", "GOOGLE_CLOUD"]).describe("Google API surface. UNSPECIFIED is invalid when this configuration is supplied on a write."),
22993
+ projectId: external_exports.string().describe("Google Cloud resource project. Required for GOOGLE_CLOUD and unused for DEVELOPER; this is distinct from the ADC quota project.").optional(),
22994
+ location: external_exports.string().describe("Google Cloud location. Valid only for GOOGLE_CLOUD; omission defaults to global.").optional()
22995
+ }).passthrough().describe("When present, atomically replaces the complete Gemini backend routing configuration. Valid only for a GEMINI embedder. Omit to preserve the stored configuration; the gRPC service validates the backend-specific projectId and location contract.").optional()
22996
+ }, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
22287
22997
  if (typeof args.id !== "string" || args.id === "") {
22288
22998
  throw new Error("'id' must be a non-empty string");
22289
22999
  }
@@ -22318,13 +23028,21 @@ function registerTools() {
22318
23028
  body["version"] = args.version;
22319
23029
  if (args.monitoring_endpoint !== void 0)
22320
23030
  body["monitoringEndpoint"] = args.monitoring_endpoint;
23031
+ if (args.dashscope_api_dialect !== void 0)
23032
+ body["dashscopeApiDialect"] = args.dashscope_api_dialect;
23033
+ if (args.gemini_endpoint_config !== void 0)
23034
+ body["geminiEndpointConfig"] = args.gemini_endpoint_config;
22321
23035
  const result = await callApi("PUT", `/v1/embedders/${encodeURIComponent(String(args.id))}`, body);
22322
23036
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22323
23037
  });
22324
- server.tool("goodmem_llms_create", "Create a new LLM\n\nCreates a new LLM configuration for text generation services. LLMs represent connections to different language model API services (like OpenAI, vLLM, etc.) and include all the necessary configuration to use them for text generation. DUPLICATE DETECTION: Returns HTTP 409 Conflict (ALREADY_EXISTS) if another LLM exists with identical {owner_id, provider_type, endpoint_url, api_path, model_identifier, credentialsFingerprint} after URL canonicalization. Uniqueness is enforced per-owner. Credentials are hashed (SHA-256) for uniqueness while remaining encrypted. The api_path field defaults to '/chat/completions' if omitted. Requires CREATE_LLM_OWN permission (or CREATE_LLM_ANY for admin users).", {
23038
+ server.tool("goodmem_instance_get", "Get the GoodMem instance\n\nReturns singleton instance identity, ownership, and audit metadata after requiring effective READ_INSTANCE authority. The built-in ADMIN role supplies this authority; instance ownership alone does not. A scoped API key must also retain READ_INSTANCE in its immutable ceiling. This operation does not expose credentials or mutate instance state.", {}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
23039
+ const result = await callApi("GET", `/v1/instance`, void 0);
23040
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
23041
+ });
23042
+ server.tool("goodmem_llms_create", "Create a new LLM\n\nCreates a new LLM configuration for text generation services. LLMs represent connections to different language model API services (like OpenAI, vLLM, etc.) and include all the necessary configuration to use them for text generation. DUPLICATE DETECTION: Returns HTTP 409 Conflict (ALREADY_EXISTS) if another LLM exists with the same effective provider connection and model configuration for this owner after endpoint canonicalization and provider-default resolution. Equivalent credentials participate in the comparison. The api_path field defaults to '/chat/completions' if omitted. OWNER DEFAULTS: Owner defaults to the authenticated principal unless owner_id is provided; CREATE_LLM is evaluated against the proposed LLM and owner.", {
22325
23043
  display_name: external_exports.string().describe("User-facing name of the LLM"),
22326
23044
  description: external_exports.string().describe("Description of the LLM").optional(),
22327
- provider_type: external_exports.enum(["OPENAI", "LITELLM_PROXY", "OPEN_ROUTER", "VLLM", "OLLAMA", "LLAMA_CPP", "CUSTOM_OPENAI_COMPATIBLE"]).describe("Type of LLM provider (auto-inferred from model_identifier if omitted)").optional(),
23045
+ provider_type: external_exports.enum(["OPENAI", "LITELLM_PROXY", "OPEN_ROUTER", "DASHSCOPE", "VLLM", "OLLAMA", "LLAMA_CPP", "CUSTOM_OPENAI_COMPATIBLE"]).describe("Type of LLM provider (auto-inferred from model_identifier if omitted)").optional(),
22328
23046
  endpoint_url: external_exports.string().describe("API endpoint base URL (OpenAI-compatible base, typically ends with /v1) (auto-inferred from model_identifier if omitted)").optional(),
22329
23047
  api_path: external_exports.string().describe("API path for chat/completions request (defaults to /chat/completions if not provided)").optional(),
22330
23048
  model_identifier: external_exports.string().describe("Model identifier"),
@@ -22368,9 +23086,10 @@ function registerTools() {
22368
23086
  }).passthrough().describe("Default sampling parameters for generation requests").optional(),
22369
23087
  max_context_length: external_exports.number().int().describe("Maximum context window size in tokens").optional(),
22370
23088
  client_config: external_exports.record(external_exports.string(), external_exports.any()).describe("Provider-specific client configuration as flexible JSON structure").optional(),
22371
- owner_id: external_exports.string().describe("Optional owner ID. If not provided, derived from the authentication context. Requires CREATE_LLM_ANY permission if specified.").optional(),
22372
- llm_id: external_exports.string().describe("Optional client-provided UUID for idempotent creation. If not provided, server generates a new UUID. Returns ALREADY_EXISTS if ID is already in use.").optional()
22373
- }, async (args) => {
23089
+ owner_id: external_exports.string().describe("Optional owner principal UUID. If omitted, defaults to the authenticated principal. CREATE_LLM is evaluated against the proposed LLM and owner.").optional(),
23090
+ llm_id: external_exports.string().describe("Optional client-provided UUID for idempotent creation. If not provided, server generates a new UUID. Returns ALREADY_EXISTS if ID is already in use.").optional(),
23091
+ dashscope_api_dialect: external_exports.enum(["LLM_NATIVE_TEXT", "LLM_NATIVE_MULTIMODAL", "OPENAI_COMPATIBLE"]).describe("DashScope request and response API dialect. Valid only for the DASHSCOPE provider. When omitted, a recognized api_path determines the dialect; otherwise OPENAI_COMPATIBLE is used.").optional()
23092
+ }, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
22374
23093
  const inferred = autoInferFromRegistry("llms", args);
22375
23094
  checkCredentialRequired(inferred);
22376
23095
  const body = {};
@@ -22408,32 +23127,38 @@ function registerTools() {
22408
23127
  body["ownerId"] = inferred.owner_id;
22409
23128
  if (inferred.llm_id !== void 0)
22410
23129
  body["llmId"] = inferred.llm_id;
23130
+ if (inferred.dashscope_api_dialect !== void 0)
23131
+ body["dashscopeApiDialect"] = inferred.dashscope_api_dialect;
22411
23132
  const result = await callApi("POST", `/v1/llms`, body);
22412
23133
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22413
23134
  });
22414
- server.tool("goodmem_llms_delete", "Delete an LLM\n\nPermanently deletes an LLM configuration. This operation cannot be undone and removes the LLM record and securely deletes stored credentials. IMPORTANT: This does NOT invalidate or delete any previously generated content using this LLM - existing generations remain accessible. Requires DELETE_LLM_OWN permission for LLMs you own (or DELETE_LLM_ANY for admin users).", {
23135
+ server.tool("goodmem_llms_delete", "Delete an LLM\n\nPermanently deletes an LLM configuration. This operation cannot be undone and removes the LLM record and securely deletes stored credentials. IMPORTANT: This does NOT invalidate or delete any previously generated content using this LLM - existing generations remain accessible. Requires DELETE_LLM on the requested LLM.", {
22415
23136
  id: external_exports.string().describe("The unique identifier of the LLM to delete")
22416
- }, async (args) => {
23137
+ }, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
22417
23138
  if (typeof args.id !== "string" || args.id === "") {
22418
23139
  throw new Error("'id' must be a non-empty string");
22419
23140
  }
22420
23141
  await callApi("DELETE", `/v1/llms/${encodeURIComponent(String(args.id))}`, void 0);
22421
23142
  return { content: [{ type: "text", text: "Deleted successfully." }] };
22422
23143
  });
22423
- server.tool("goodmem_llms_get", "Get an LLM by ID\n\nRetrieves the details of a specific LLM configuration by its unique identifier. Requires READ_LLM_OWN permission for LLMs you own (or READ_LLM_ANY for admin users to view any user's LLMs). This is a read-only operation with no side effects.", {
22424
- id: external_exports.string().describe("The unique identifier of the LLM to retrieve")
22425
- }, async (args) => {
23144
+ server.tool("goodmem_llms_get", "Get an LLM by ID\n\nRetrieves the details of a specific LLM configuration by its unique identifier. Requires READ_LLM on the requested LLM. The service distinguishes a missing LLM from an existing LLM the caller cannot read. This is a read-only operation with no side effects.", {
23145
+ id: external_exports.string().describe("The unique identifier of the LLM to retrieve"),
23146
+ include_credentials: external_exports.boolean().describe("Whether to return stored credentials. Also accepts include_credentials. Requires READ_LLM_CREDENTIALS in addition to READ_LLM.").optional()
23147
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
22426
23148
  if (typeof args.id !== "string" || args.id === "") {
22427
23149
  throw new Error("'id' must be a non-empty string");
22428
23150
  }
22429
- const result = await callApi("GET", `/v1/llms/${encodeURIComponent(String(args.id))}`, void 0);
23151
+ const query = {};
23152
+ if (args.include_credentials !== void 0)
23153
+ query["includeCredentials"] = String(args.include_credentials);
23154
+ const result = await callApi("GET", `/v1/llms/${encodeURIComponent(String(args.id))}`, void 0, query);
22430
23155
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22431
23156
  });
22432
- server.tool("goodmem_llms_list", "List LLMs\n\nRetrieves a list of LLM configurations accessible to the caller, with optional filtering. LABEL FILTERS: Label filters accept either label.<key>=<value> or label[key]=value (for example, label.environment=production or label[environment]=production). PERMISSION-BASED FILTERING: With LIST_LLM_OWN permission, you can only see your own LLMs (owner_id filter is ignored if set to another user). With LIST_LLM_ANY permission, you can see all LLMs or filter by any owner_id. This is a read-only operation with no side effects.", {
22433
- owner_id: external_exports.string().describe("Filter LLMs by owner ID. With LIST_LLM_ANY permission, omitting this shows all accessible LLMs; providing it filters by that owner. With LIST_LLM_OWN permission, only your own LLMs are shown regardless of this parameter.").optional(),
22434
- provider_type: external_exports.enum(["OPENAI", "LITELLM_PROXY", "OPEN_ROUTER", "VLLM", "OLLAMA", "LLAMA_CPP", "CUSTOM_OPENAI_COMPATIBLE"]).describe("Filter LLMs by provider type. Allowed values match the LLMProviderType schema.").optional(),
23157
+ server.tool("goodmem_llms_list", "List LLMs\n\nRetrieves a list of LLM configurations accessible to the caller, with optional filtering. LABEL FILTERS: Label filters accept either label.<key>=<value> or label[key]=value (for example, label.environment=production or label[environment]=production). AUTHORIZATION: Requires LIST_LLM on the GoodMem instance. Each returned LLM must also be visible through READ_LLM; unauthorized LLMs are filtered in PostgreSQL. The owner_id parameter filters that already-authorized result set and does not grant additional visibility. This is a read-only operation with no side effects.", {
23158
+ owner_id: external_exports.string().describe("Filter the already-authorized result set by owner principal UUID. Omitting this parameter does not bypass per-LLM READ_LLM filtering.").optional(),
23159
+ provider_type: external_exports.enum(["OPENAI", "LITELLM_PROXY", "OPEN_ROUTER", "DASHSCOPE", "VLLM", "OLLAMA", "LLAMA_CPP", "CUSTOM_OPENAI_COMPATIBLE"]).describe("Filter LLMs by provider type. Allowed values match the LLMProviderType schema.").optional(),
22435
23160
  label: external_exports.record(external_exports.string(), external_exports.string()).describe("Filter by label. Each key-value pair is sent as label[key]=value query parameter.").optional()
22436
- }, async (args) => {
23161
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
22437
23162
  const query = {};
22438
23163
  if (args.owner_id !== void 0)
22439
23164
  query["ownerId"] = String(args.owner_id);
@@ -22446,7 +23171,7 @@ function registerTools() {
22446
23171
  const result = await callApi("GET", `/v1/llms`, void 0, query);
22447
23172
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22448
23173
  });
22449
- server.tool("goodmem_llms_update", "Update an LLM\n\nUpdates an existing LLM configuration including display information, endpoint configuration, model parameters, credentials, and labels. All fields are optional - only specified fields will be updated. SUPPORTED_MODALITIES UPDATE: If the array contains >=1 elements, it replaces the stored set; if empty or omitted, no change occurs and it does not count as an update by itself. IMPORTANT: providerType is IMMUTABLE after creation and cannot be changed. Requires UPDATE_LLM_OWN permission for LLMs you own (or UPDATE_LLM_ANY for admin users).", {
23174
+ server.tool("goodmem_llms_update", "Update an LLM\n\nUpdates an existing LLM configuration including display information, endpoint configuration, model parameters, credentials, and labels. All fields are optional - only specified fields will be updated. SUPPORTED_MODALITIES UPDATE: If the array contains >=1 elements, it replaces the stored set; if empty or omitted, no change occurs and it does not count as an update by itself. IMPORTANT: providerType is IMMUTABLE after creation and cannot be changed. Requires UPDATE_LLM on the requested LLM.", {
22450
23175
  id: external_exports.string().describe("The unique identifier of the LLM to update"),
22451
23176
  display_name: external_exports.string().describe("Update display name").optional(),
22452
23177
  description: external_exports.string().describe("Update description").optional(),
@@ -22470,7 +23195,7 @@ function registerTools() {
22470
23195
  quotaProjectId: external_exports.string().describe("Optional quota project used for billing").optional()
22471
23196
  }).passthrough().describe("Configuration when kind is CREDENTIAL_KIND_GCP_ADC").optional(),
22472
23197
  labels: external_exports.record(external_exports.string(), external_exports.string()).describe('Optional annotations to aid operators (e.g., "owner=vertex")').optional()
22473
- }).passthrough().describe("Update credentials").optional(),
23198
+ }).passthrough().describe("Replace stored credentials. Omit this field to preserve the current credentials; a present empty payload is invalid and never clears them.").optional(),
22474
23199
  version: external_exports.string().describe("Update version information").optional(),
22475
23200
  monitoring_endpoint: external_exports.string().describe("Update monitoring endpoint URL").optional(),
22476
23201
  capabilities: external_exports.object({
@@ -22493,8 +23218,9 @@ function registerTools() {
22493
23218
  max_context_length: external_exports.number().int().describe("Update maximum context window size in tokens").optional(),
22494
23219
  client_config: external_exports.record(external_exports.string(), external_exports.any()).describe("Update provider-specific client configuration (replaces entire config; no merging)").optional(),
22495
23220
  replace_labels: external_exports.record(external_exports.string(), external_exports.string()).describe("Replace all existing labels with this set. Empty map clears all labels. Cannot be used with merge_labels.").optional(),
22496
- merge_labels: external_exports.record(external_exports.string(), external_exports.string()).describe("Merge with existing labels: upserts with overwrite. Labels not mentioned are preserved. Cannot be used with replace_labels.").optional()
22497
- }, async (args) => {
23221
+ merge_labels: external_exports.record(external_exports.string(), external_exports.string()).describe("Merge with existing labels: upserts with overwrite. Labels not mentioned are preserved. Cannot be used with replace_labels.").optional(),
23222
+ dashscope_api_dialect: external_exports.enum(["LLM_NATIVE_TEXT", "LLM_NATIVE_MULTIMODAL", "OPENAI_COMPATIBLE"]).describe("Update the DashScope request and response API dialect. Valid only for the DASHSCOPE provider. Omit to preserve the stored dialect. Changing api_path to a recognized canonical DashScope dialect infers its matching dialect; a custom path preserves an existing dialect, while a legacy null dialect is inferred.").optional()
23223
+ }, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
22498
23224
  if (typeof args.id !== "string" || args.id === "") {
22499
23225
  throw new Error("'id' must be a non-empty string");
22500
23226
  }
@@ -22533,6 +23259,8 @@ function registerTools() {
22533
23259
  body["replaceLabels"] = args.replace_labels;
22534
23260
  if (args.merge_labels !== void 0)
22535
23261
  body["mergeLabels"] = args.merge_labels;
23262
+ if (args.dashscope_api_dialect !== void 0)
23263
+ body["dashscopeApiDialect"] = args.dashscope_api_dialect;
22536
23264
  const result = await callApi("PUT", `/v1/llms/${encodeURIComponent(String(args.id))}`, body);
22537
23265
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22538
23266
  });
@@ -22544,7 +23272,7 @@ function registerTools() {
22544
23272
  originalContentB64: external_exports.string().describe("Original content as base64-encoded binary data (use either this or originalContent)").optional(),
22545
23273
  originalContentRef: external_exports.string().describe("Reference to external content location").optional(),
22546
23274
  contentType: external_exports.string().describe("MIME type of the content").optional(),
22547
- metadata: external_exports.record(external_exports.string(), external_exports.any()).describe("Additional metadata for the memory").optional(),
23275
+ metadata: external_exports.record(external_exports.string(), external_exports.any()).describe("Additional metadata for the memory. A top-level textual title is an optional document-title hint for embedding providers that support one; blank or non-string titles are ignored. Later metadata edits do not automatically re-embed existing chunks.").optional(),
22548
23276
  chunkingConfig: external_exports.object({
22549
23277
  none: external_exports.any().describe("No chunking strategy - preserve original content as single unit").optional(),
22550
23278
  recursive: external_exports.object({
@@ -22564,7 +23292,7 @@ function registerTools() {
22564
23292
  }).passthrough().describe("Chunking strategy for this memory (if not provided, uses space default)").optional(),
22565
23293
  extractPageImages: external_exports.boolean().describe("Optional hint to extract page images for eligible document types (for example, PDFs)").optional()
22566
23294
  }).passthrough()).describe("Array of memory creation requests.")
22567
- }, async (args) => {
23295
+ }, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
22568
23296
  for (const [_i, _item] of (Array.isArray(args.requests) ? args.requests : []).entries()) {
22569
23297
  const _itemSet = [_item?.["originalContent"] !== void 0 && _item?.["originalContent"] !== null, _item?.["originalContentB64"] !== void 0 && _item?.["originalContentB64"] !== null].filter(Boolean).length;
22570
23298
  if (_itemSet !== 1) {
@@ -22588,7 +23316,7 @@ function registerTools() {
22588
23316
  const result = await callApi("POST", `/v1/memories:batchCreate`, body);
22589
23317
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22590
23318
  });
22591
- server.tool("goodmem_memories_batch_delete", "Delete memories in batch\n\nDeletes memories using selector entries. Each selector can target either a specific memory ID or a filtered subset scoped to a specific space.", {
23319
+ server.tool("goodmem_memories_batch_delete", "Delete memories in batch\n\nDeletes memories using selector entries. Each selector can target either a specific memory ID or a filtered subset scoped to a specific space. Each selected memory requires DELETE_MEMORY; authority may be granted through DIRECT_MEMBERS_OF its containing space.", {
22592
23320
  requests: external_exports.array(external_exports.object({
22593
23321
  memoryId: external_exports.string().describe("Deletes one specific memory by UUID").optional(),
22594
23322
  filterSelector: external_exports.object({
@@ -22597,18 +23325,18 @@ function registerTools() {
22597
23325
  filter: external_exports.string().describe("Optional metadata filter expression").optional()
22598
23326
  }).passthrough().describe("Deletes a filtered set of memories within a specific space").optional()
22599
23327
  }).passthrough()).describe("Array of delete selectors")
22600
- }, async (args) => {
23328
+ }, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
22601
23329
  const body = {};
22602
23330
  if (args.requests !== void 0)
22603
23331
  body["requests"] = args.requests;
22604
23332
  const result = await callApi("POST", `/v1/memories:batchDelete`, body);
22605
23333
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22606
23334
  });
22607
- server.tool("goodmem_memories_batch_get", "Get multiple memories by ID\n\nRetrieves multiple memories in a single operation, with individual success/failure results.", {
23335
+ server.tool("goodmem_memories_batch_get", "Get multiple memories by ID\n\nRetrieves multiple memories in a single operation, with individual success/failure results. Each item requires READ_MEMORY on the requested memory; authority may be granted through DIRECT_MEMBERS_OF its containing space.", {
22608
23336
  memory_ids: external_exports.array(external_exports.string()).describe("Array of memory IDs to retrieve"),
22609
23337
  include_content: external_exports.boolean().describe("Whether to include the original content in the response").optional(),
22610
23338
  include_processing_history: external_exports.boolean().describe("Whether to include background job processing history for each memory").optional()
22611
- }, async (args) => {
23339
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
22612
23340
  const body = {};
22613
23341
  if (args.memory_ids !== void 0)
22614
23342
  body["memoryIds"] = args.memory_ids;
@@ -22619,10 +23347,10 @@ function registerTools() {
22619
23347
  const result = await callApi("POST", `/v1/memories:batchGet`, body);
22620
23348
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22621
23349
  });
22622
- server.tool("goodmem_memories_content", "Download memory content\n\nReturns the original binary payload for a memory. The response uses the memory's stored content type when available. Returns 404 when the memory does not have inline content; clients can check originalContentRef from the metadata endpoint to locate external content.", {
23350
+ server.tool("goodmem_memories_content", "Download memory content\n\nReturns the original binary payload for a memory. The response uses the memory's stored content type when available. Returns 404 when the memory does not have inline content; clients can check originalContentRef from the metadata endpoint to locate external content. Requires READ_MEMORY on the requested memory; authority may be granted through DIRECT_MEMBERS_OF its containing space.", {
22623
23351
  id: external_exports.string().describe("The UUID of the memory to download"),
22624
23352
  max_inline_bytes: external_exports.number().int().positive().describe("Maximum bytes to deliver inline as a content block. Defaults to 8388608. Hard server ceiling: 67108864. If the payload exceeds this cap the tool errors with the actual size \u2014 retry with a larger value (up to the ceiling) to receive it.").optional()
22625
- }, async (args) => {
23353
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
22626
23354
  if (typeof args.id !== "string" || args.id === "") {
22627
23355
  throw new Error("'id' must be a non-empty string");
22628
23356
  }
@@ -22649,7 +23377,7 @@ function registerTools() {
22649
23377
  original_content_b64: external_exports.string().describe("Original content as base64-encoded binary data (use either this or original_content)").optional(),
22650
23378
  original_content_ref: external_exports.string().describe("Reference to external content location").optional(),
22651
23379
  content_type: external_exports.string().describe("MIME type of the content (defaults to text/plain when original_content is provided)").optional(),
22652
- metadata: external_exports.record(external_exports.string(), external_exports.any()).describe("Additional metadata for the memory").optional(),
23380
+ metadata: external_exports.record(external_exports.string(), external_exports.any()).describe("Additional metadata for the memory. A top-level textual title is an optional document-title hint for embedding providers that support one; blank or non-string titles are ignored. Later metadata edits do not automatically re-embed existing chunks.").optional(),
22653
23381
  chunking_config: external_exports.object({
22654
23382
  none: external_exports.any().describe("No chunking strategy - preserve original content as single unit").optional(),
22655
23383
  recursive: external_exports.object({
@@ -22668,7 +23396,7 @@ function registerTools() {
22668
23396
  }).passthrough().describe("Sentence-based chunking strategy with language detection").optional()
22669
23397
  }).passthrough().describe("Chunking strategy for this memory (if not provided, uses space default)").optional(),
22670
23398
  extract_page_images: external_exports.boolean().describe("Optional hint to extract page images for eligible document types (for example, PDFs)").optional()
22671
- }, async (args) => {
23399
+ }, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
22672
23400
  const _set = [args.original_content !== void 0 && args.original_content !== null, args.original_content_b64 !== void 0 && args.original_content_b64 !== null].filter(Boolean).length;
22673
23401
  if (_set !== 1) {
22674
23402
  throw new Error("exactly one of original_content or original_content_b64 must be set");
@@ -22701,20 +23429,20 @@ function registerTools() {
22701
23429
  const result = await callApi("POST", `/v1/memories`, body);
22702
23430
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22703
23431
  });
22704
- server.tool("goodmem_memories_delete", "Delete a memory\n\nPermanently deletes a memory and its associated chunks. This operation cannot be undone and immediately removes the memory record from the database. IDEMPOTENCY: This operation is safe to retry - may return NOT_FOUND if the memory was already deleted or never existed. Requires DELETE_MEMORY_OWN permission for memories in spaces you own (or DELETE_MEMORY_ANY for admin users to delete any memory). Side effects include permanent removal of the memory record and all associated chunk data.", {
23432
+ server.tool("goodmem_memories_delete", "Delete a memory\n\nPermanently deletes a memory and its associated chunks. This operation cannot be undone and immediately removes the memory record from the database. IDEMPOTENCY: This operation is safe to retry - may return NOT_FOUND if the memory was already deleted or never existed. AUTHORIZATION: Requires DELETE_MEMORY on the requested memory; authority may be granted through DIRECT_MEMBERS_OF its containing space. Side effects include permanent removal of the memory record and all associated chunk data.", {
22705
23433
  id: external_exports.string().describe("The UUID of the memory to delete")
22706
- }, async (args) => {
23434
+ }, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
22707
23435
  if (typeof args.id !== "string" || args.id === "") {
22708
23436
  throw new Error("'id' must be a non-empty string");
22709
23437
  }
22710
23438
  await callApi("DELETE", `/v1/memories/${encodeURIComponent(String(args.id))}`, void 0);
22711
23439
  return { content: [{ type: "text", text: "Deleted successfully." }] };
22712
23440
  });
22713
- server.tool("goodmem_memories_get", "Get a memory by ID\n\nRetrieves a single memory by its ID. PERMISSION CLARIFICATION: With READ_MEMORY_OWN permission, access is granted if you own the parent space OR if the parent space is public (public_read=true). With READ_MEMORY_ANY permission, you can access any memory regardless of ownership. This is a read-only operation with no side effects and is safe to retry. Returns NOT_FOUND if the memory or its parent space does not exist.", {
23441
+ server.tool("goodmem_memories_get", "Get a memory by ID\n\nRetrieves a single memory by its ID. AUTHORIZATION: Requires READ_MEMORY on the requested memory; authority may be granted directly or through DIRECT_MEMBERS_OF its containing space. This is a read-only operation with no side effects and is safe to retry. Returns NOT_FOUND if the memory or its parent space does not exist.", {
22714
23442
  id: external_exports.string().describe("The UUID of the memory to retrieve"),
22715
23443
  include_content: external_exports.boolean().describe("Whether to include the original content in the response (defaults to false).").optional(),
22716
23444
  include_processing_history: external_exports.boolean().describe("Whether to include background job processing history in the response (defaults to false).").optional()
22717
- }, async (args) => {
23445
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
22718
23446
  if (typeof args.id !== "string" || args.id === "") {
22719
23447
  throw new Error("'id' must be a non-empty string");
22720
23448
  }
@@ -22736,7 +23464,7 @@ function registerTools() {
22736
23464
  next_token: external_exports.string().describe("Opaque pagination token for the next page. URL-safe Base64 without padding; do not parse or construct it.").optional(),
22737
23465
  sort_by: external_exports.enum(["created_at", "updated_at", "content_type", "processing_status"]).describe("Field to sort by (e.g., 'created_at').").optional(),
22738
23466
  sort_order: external_exports.enum(["ASCENDING", "DESCENDING", "SORT_ORDER_UNSPECIFIED"]).describe("Sort direction (ASCENDING or DESCENDING).").optional()
22739
- }, async (args) => {
23467
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
22740
23468
  if (typeof args.space_id !== "string" || args.space_id === "") {
22741
23469
  throw new Error("'space_id' must be a non-empty string");
22742
23470
  }
@@ -22760,7 +23488,7 @@ function registerTools() {
22760
23488
  const result = await callApi("GET", `/v1/spaces/${encodeURIComponent(String(args.space_id))}/memories`, void 0, query);
22761
23489
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22762
23490
  });
22763
- server.tool("goodmem_memories_pages", "List memory page images\n\nLists extracted page-image metadata for a memory with optional filters and pagination.", {
23491
+ server.tool("goodmem_memories_pages", "List memory page images\n\nLists extracted page-image metadata for a memory with optional filters and pagination. Requires READ_MEMORY on the containing memory; authority may be granted through DIRECT_MEMBERS_OF its containing space.", {
22764
23492
  id: external_exports.string().describe("Memory UUID"),
22765
23493
  start_page_index: external_exports.number().int().describe("Optional lower bound for returned page indices, inclusive.").optional(),
22766
23494
  end_page_index: external_exports.number().int().describe("Optional upper bound for returned page indices, inclusive.").optional(),
@@ -22768,7 +23496,7 @@ function registerTools() {
22768
23496
  content_type: external_exports.string().describe("Optional rendition filter for page-image MIME type, such as image/png.").optional(),
22769
23497
  max_results: external_exports.number().int().describe("Maximum number of results per page.").optional(),
22770
23498
  next_token: external_exports.string().describe("Opaque pagination token for the next page. Do not parse or construct it.").optional()
22771
- }, async (args) => {
23499
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
22772
23500
  if (typeof args.id !== "string" || args.id === "") {
22773
23501
  throw new Error("'id' must be a non-empty string");
22774
23502
  }
@@ -22788,13 +23516,13 @@ function registerTools() {
22788
23516
  const result = await callApi("GET", `/v1/memories/${encodeURIComponent(String(args.id))}/pages`, void 0, query);
22789
23517
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22790
23518
  });
22791
- server.tool("goodmem_memories_pages_image", "Download memory page image content\n\nDownloads inline bytes for one page image. The page index is required. The optional dpi and content type query parameters act as rendition filters; if omitted, the server returns the unique rendition for that page or rejects ambiguous matches.", {
23519
+ server.tool("goodmem_memories_pages_image", "Download memory page image content\n\nDownloads inline bytes for one page image. The page index is required. The optional dpi and content type query parameters act as rendition filters; if omitted, the server returns the unique rendition for that page or rejects ambiguous matches. Requires READ_MEMORY on the containing memory; authority may be granted through DIRECT_MEMBERS_OF its containing space.", {
22792
23520
  id: external_exports.string().describe("Memory UUID"),
22793
23521
  page_index: external_exports.number().int().describe("0-based page index"),
22794
23522
  dpi: external_exports.number().int().describe("Optional rendition filter. If omitted, the unique page-image rendition for the page is returned; if multiple renditions exist, specify dpi and/or content_type.").optional(),
22795
23523
  content_type: external_exports.string().describe("Optional rendition filter. MIME type of the desired page image, such as image/png.").optional(),
22796
23524
  max_inline_bytes: external_exports.number().int().positive().describe("Maximum bytes to deliver inline as a content block. Defaults to 8388608. Hard server ceiling: 67108864. If the payload exceeds this cap the tool errors with the actual size \u2014 retry with a larger value (up to the ceiling) to receive it.").optional()
22797
- }, async (args) => {
23525
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
22798
23526
  if (typeof args.id !== "string" || args.id === "") {
22799
23527
  throw new Error("'id' must be a non-empty string");
22800
23528
  }
@@ -22855,12 +23583,8 @@ function registerTools() {
22855
23583
  name: external_exports.string().describe("Fully qualified factory class name of the post-processor to apply."),
22856
23584
  config: external_exports.record(external_exports.string(), external_exports.any()).describe("Configuration parameters for the post-processor. Fields depend on the selected processor; see the linked documentation for the built-in ChatPostProcessor schema.").optional()
22857
23585
  }).passthrough().describe("Optional post-processor configuration to transform retrieval results.").optional(),
22858
- logging: external_exports.object({
22859
- enabled: external_exports.boolean().describe("Opts this request in to durable server-side request logging. False or omitted does not opt out of admin policy logging.").optional(),
22860
- callerAttributes: external_exports.record(external_exports.string(), external_exports.any()).describe("Optional flat scalar attributes attached if this request is persisted by caller opt-in or admin policy logging. Supported value types are string, integer, floating-point, and boolean. At most 32 entries are accepted.").optional()
22861
- }).passthrough().describe("Optional durable request logging block for POST retrieve requests. Set logging.enabled=true to opt in, or supply flat scalar logging.callerAttributes to attach if caller opt-in or an admin policy logs the request.").optional(),
22862
23586
  timeout_sec: external_exports.number().int().describe("Maximum seconds to wait for the streaming response (default: 200).").optional()
22863
- }, async (args) => {
23587
+ }, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
22864
23588
  const body = {};
22865
23589
  if (args.message !== void 0)
22866
23590
  body["message"] = args.message;
@@ -22880,8 +23604,6 @@ function registerTools() {
22880
23604
  body["hnsw"] = args.hnsw;
22881
23605
  if (args.post_processor !== void 0)
22882
23606
  body["postProcessor"] = args.post_processor;
22883
- if (args.logging !== void 0)
22884
- body["logging"] = args.logging;
22885
23607
  const result = await callApiNdjson("POST", `/v1/memories:retrieve`, body, void 0, args.timeout_sec);
22886
23608
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22887
23609
  });
@@ -22892,7 +23614,7 @@ function registerTools() {
22892
23614
  include_markdown: external_exports.boolean().describe("Include markdown rendering in the response").optional(),
22893
23615
  start_page: external_exports.number().int().describe("0-based inclusive start page").optional(),
22894
23616
  end_page: external_exports.number().int().describe("0-based inclusive end page").optional()
22895
- }, async (args) => {
23617
+ }, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
22896
23618
  const body = {};
22897
23619
  if (args.content !== void 0)
22898
23620
  body["content"] = args.content;
@@ -22909,14 +23631,14 @@ function registerTools() {
22909
23631
  const result = await callApi("POST", `/v1/ocr:document`, body);
22910
23632
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22911
23633
  });
22912
- server.tool("goodmem_ping_once", "Run a single ping probe\n\nRuns a single ping probe and returns the probe result.", {
23634
+ server.tool("goodmem_ping_once", "Run a single ping probe\n\nRuns a single ping probe and returns the probe result. Requires both the target-specific PING operation and its corresponding EXECUTE operation: PING_EMBEDDER plus EXECUTE_EMBEDDER, PING_RERANKER plus EXECUTE_RERANKER, or PING_LLM plus EXECUTE_LLM.", {
22913
23635
  target_id: external_exports.string().describe("Target resource ID (UUID)"),
22914
23636
  target_type_hint: external_exports.enum(["TARGET_TYPE_UNSPECIFIED", "EMBEDDER", "RERANKER", "LLM"]).describe("Optional hint for the target resource type").optional(),
22915
23637
  payload_type: external_exports.enum(["PAYLOAD_TYPE_UNSPECIFIED", "TEXT", "JSON", "BINARY"]).describe("Desired payload type (defaults to provider-specific value)").optional(),
22916
23638
  payload: external_exports.string().describe("Explicit UTF-8 payload to send with the probe (mutually exclusive with payload_size_bytes)").optional(),
22917
23639
  payload_size_bytes: external_exports.number().int().describe("Synthetic payload size in bytes (mutually exclusive with payload)").optional(),
22918
23640
  timeout_ms: external_exports.number().int().describe("Per-probe timeout in milliseconds (0 uses server default)").optional()
22919
- }, async (args) => {
23641
+ }, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
22920
23642
  const _set = [args.payload !== void 0 && args.payload !== null, args.payload_size_bytes !== void 0 && args.payload_size_bytes !== null].filter(Boolean).length;
22921
23643
  if (_set > 1) {
22922
23644
  throw new Error("at most one of payload or payload_size_bytes may be set");
@@ -22937,7 +23659,7 @@ function registerTools() {
22937
23659
  const result = await callApi("POST", `/v1/ping:once`, body, void 0, (args.timeout_ms ?? 5e3) + 3e4);
22938
23660
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22939
23661
  });
22940
- server.tool("goodmem_ping_stream", "Stream ping probe results\n\nOpens a streaming ping session and returns per-probe results plus a terminal summary.", {
23662
+ server.tool("goodmem_ping_stream", "Stream ping probe results\n\nOpens a streaming ping session and returns per-probe results plus a terminal summary. Requires both the target-specific PING operation and its corresponding EXECUTE operation: PING_EMBEDDER plus EXECUTE_EMBEDDER, PING_RERANKER plus EXECUTE_RERANKER, or PING_LLM plus EXECUTE_LLM.", {
22941
23663
  target_id: external_exports.string().describe("Target resource ID (UUID)"),
22942
23664
  target_type_hint: external_exports.enum(["TARGET_TYPE_UNSPECIFIED", "EMBEDDER", "RERANKER", "LLM"]).describe("Optional hint for the target resource type").optional(),
22943
23665
  count: external_exports.number().int().describe("Number of probes to run (0 uses server default)").optional(),
@@ -22950,7 +23672,7 @@ function registerTools() {
22950
23672
  jitter: external_exports.boolean().describe("Add jitter to probe scheduling").optional(),
22951
23673
  labels: external_exports.record(external_exports.string(), external_exports.string()).describe("Optional labels to attach to the ping session").optional(),
22952
23674
  timeout_sec: external_exports.number().int().describe("Maximum seconds to wait for the streaming response (default: 200).").optional()
22953
- }, async (args) => {
23675
+ }, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
22954
23676
  const _set = [args.payload !== void 0 && args.payload !== null, args.payload_size_bytes !== void 0 && args.payload_size_bytes !== null].filter(Boolean).length;
22955
23677
  if (_set > 1) {
22956
23678
  throw new Error("at most one of payload or payload_size_bytes may be set");
@@ -22981,10 +23703,10 @@ function registerTools() {
22981
23703
  const result = await callApiNdjson("POST", `/v1/ping:stream`, body, void 0, args.timeout_sec ?? Math.ceil(((args.count || 100) * (args.interval_ms || 1e3) + (args.timeout_ms || 5e3) + 6e4) / 1e3));
22982
23704
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22983
23705
  });
22984
- server.tool("goodmem_rerankers_create", "Create a new reranker\n\nCreates a new reranker configuration for ranking search results. Rerankers represent connections to different reranking API services (like TEI, OpenAI, etc.) and include all the necessary configuration to use them for result ranking. DUPLICATE DETECTION: Returns HTTP 409 Conflict (ALREADY_EXISTS) if another reranker exists with identical {owner_id, provider_type, endpoint_url, api_path, model_identifier, credentialsFingerprint} after URL canonicalization. Uniqueness is enforced per-owner, allowing different users to have identical configurations. Credentials are hashed (SHA-256) for uniqueness while remaining encrypted. DEFAULTS: api_path defaults to '/v2/rerank' for Cohere and '/rerank' for other providers if omitted; supported_modalities defaults to [TEXT] if omitted. Requires CREATE_RERANKER_OWN permission (or CREATE_RERANKER_ANY for admin users). This operation is NOT idempotent - each request creates a new reranker record.", {
23706
+ server.tool("goodmem_rerankers_create", "Create a new reranker\n\nCreates a new reranker configuration for ranking search results. Rerankers represent connections to different reranking API services (like TEI, OpenAI, etc.) and include all the necessary configuration to use them for result ranking. DUPLICATE DETECTION: Returns HTTP 409 Conflict (ALREADY_EXISTS) if another reranker exists with the same effective provider connection and model configuration for this owner after endpoint canonicalization and provider-default resolution. Equivalent credentials participate in the comparison. DEFAULTS: api_path defaults to '/v2/rerank' for Cohere and '/rerank' for other providers if omitted; supported_modalities defaults to [TEXT] if omitted. OWNER DEFAULTS: Owner defaults to the authenticated principal unless owner_id is provided; CREATE_RERANKER is evaluated against the proposed reranker and owner. This operation is NOT idempotent - each request creates a new reranker record.", {
22985
23707
  display_name: external_exports.string().describe("User-facing name of the reranker"),
22986
23708
  description: external_exports.string().describe("Description of the reranker").optional(),
22987
- provider_type: external_exports.enum(["OPENAI", "VLLM", "TEI", "LLAMA_CPP", "VOYAGE", "COHERE", "JINA"]).describe("Type of reranking provider (auto-inferred from model_identifier if omitted)").optional(),
23709
+ provider_type: external_exports.enum(["VLLM", "TEI", "LLAMA_CPP", "VOYAGE", "COHERE", "JINA", "DASHSCOPE"]).describe("Type of reranking provider (auto-inferred from model_identifier if omitted)").optional(),
22988
23710
  endpoint_url: external_exports.string().describe("API endpoint URL (auto-inferred from model_identifier if omitted)").optional(),
22989
23711
  api_path: external_exports.string().describe("API path for reranking request (defaults: Cohere /v2/rerank, Jina /v1/rerank, others /rerank)").optional(),
22990
23712
  model_identifier: external_exports.string().describe("Model identifier"),
@@ -23009,9 +23731,10 @@ function registerTools() {
23009
23731
  labels: external_exports.record(external_exports.string(), external_exports.string()).describe("User-defined labels for categorization").optional(),
23010
23732
  version: external_exports.string().describe("Version information").optional(),
23011
23733
  monitoring_endpoint: external_exports.string().describe("Monitoring endpoint URL").optional(),
23012
- owner_id: external_exports.string().describe("Optional owner ID. If not provided, derived from the authentication context. Requires CREATE_RERANKER_ANY permission if specified.").optional(),
23013
- reranker_id: external_exports.string().describe("Optional client-provided UUID for idempotent creation. If not provided, server generates a new UUID. Returns ALREADY_EXISTS if ID is already in use.").optional()
23014
- }, async (args) => {
23734
+ owner_id: external_exports.string().describe("Optional owner principal UUID. If omitted, defaults to the authenticated principal. CREATE_RERANKER is evaluated against the proposed reranker and owner.").optional(),
23735
+ reranker_id: external_exports.string().describe("Optional client-provided UUID for idempotent creation. If not provided, server generates a new UUID. Returns ALREADY_EXISTS if ID is already in use.").optional(),
23736
+ dashscope_api_dialect: external_exports.enum(["RERANK_NATIVE_NESTED", "RERANK_COMPATIBLE_FLAT"]).describe("DashScope request and response API dialect. Valid only for the DASHSCOPE provider. Omit to infer the dialect from api_path, the model catalog, or the native reranking default.").optional()
23737
+ }, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
23015
23738
  const inferred = autoInferFromRegistry("rerankers", args);
23016
23739
  checkCredentialRequired(inferred);
23017
23740
  const body = {};
@@ -23041,32 +23764,38 @@ function registerTools() {
23041
23764
  body["ownerId"] = inferred.owner_id;
23042
23765
  if (inferred.reranker_id !== void 0)
23043
23766
  body["rerankerId"] = inferred.reranker_id;
23767
+ if (inferred.dashscope_api_dialect !== void 0)
23768
+ body["dashscopeApiDialect"] = inferred.dashscope_api_dialect;
23044
23769
  const result = await callApi("POST", `/v1/rerankers`, body);
23045
23770
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
23046
23771
  });
23047
- server.tool("goodmem_rerankers_delete", "Delete a reranker\n\nPermanently deletes a reranker configuration. This operation cannot be undone and immediately removes the reranker record from the database. SIDE EFFECTS: Invalidates any cached references to this reranker; does not affect historical usage data or audit logs. Requires DELETE_RERANKER_OWN permission for rerankers you own (or DELETE_RERANKER_ANY for admin users). This operation is safe to retry - may return NOT_FOUND if already deleted.", {
23772
+ server.tool("goodmem_rerankers_delete", "Delete a reranker\n\nPermanently deletes a reranker configuration. This operation cannot be undone and immediately removes the reranker record from the database. SIDE EFFECTS: Invalidates any cached references to this reranker; does not affect historical usage data or audit logs. Requires DELETE_RERANKER on the requested reranker. This operation is safe to retry - may return NOT_FOUND if already deleted.", {
23048
23773
  id: external_exports.string().describe("The unique identifier of the reranker to delete")
23049
- }, async (args) => {
23774
+ }, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
23050
23775
  if (typeof args.id !== "string" || args.id === "") {
23051
23776
  throw new Error("'id' must be a non-empty string");
23052
23777
  }
23053
23778
  await callApi("DELETE", `/v1/rerankers/${encodeURIComponent(String(args.id))}`, void 0);
23054
23779
  return { content: [{ type: "text", text: "Deleted successfully." }] };
23055
23780
  });
23056
- server.tool("goodmem_rerankers_get", "Get a reranker by ID\n\nRetrieves the details of a specific reranker configuration by its unique identifier. Response payloads include stored credentials, matching gRPC response semantics. Requires READ_RERANKER_OWN permission for rerankers you own (or READ_RERANKER_ANY for admin users). This is a read-only operation with no side effects and is safe to retry.", {
23057
- id: external_exports.string().describe("The unique identifier of the reranker to retrieve")
23058
- }, async (args) => {
23781
+ server.tool("goodmem_rerankers_get", "Get a reranker by ID\n\nRetrieves the details of a specific reranker configuration by its unique identifier. Stored credentials are omitted unless include_credentials is true and the caller also has READ_RERANKER_CREDENTIALS. Requires READ_RERANKER on the requested reranker. The service distinguishes a missing reranker from an existing reranker the caller cannot read. This is a read-only operation with no side effects and is safe to retry.", {
23782
+ id: external_exports.string().describe("The unique identifier of the reranker to retrieve"),
23783
+ include_credentials: external_exports.boolean().describe("Whether to return stored credentials. Also accepts include_credentials. Requires READ_RERANKER_CREDENTIALS in addition to READ_RERANKER.").optional()
23784
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
23059
23785
  if (typeof args.id !== "string" || args.id === "") {
23060
23786
  throw new Error("'id' must be a non-empty string");
23061
23787
  }
23062
- const result = await callApi("GET", `/v1/rerankers/${encodeURIComponent(String(args.id))}`, void 0);
23788
+ const query = {};
23789
+ if (args.include_credentials !== void 0)
23790
+ query["includeCredentials"] = String(args.include_credentials);
23791
+ const result = await callApi("GET", `/v1/rerankers/${encodeURIComponent(String(args.id))}`, void 0, query);
23063
23792
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
23064
23793
  });
23065
- server.tool("goodmem_rerankers_list", "List rerankers\n\nRetrieves a list of reranker configurations accessible to the caller, with optional filtering. IMPORTANT: Pagination is NOT supported - all matching results are returned. Results are ordered by created_at descending. Responses include stored credentials, matching gRPC response semantics. LABEL FILTERS: Label filters accept either label.<key>=<value> or label[key]=value (for example, label.environment=production or label[environment]=production). PERMISSION-BASED FILTERING: With LIST_RERANKER_OWN permission, only your own rerankers are shown. With LIST_RERANKER_ANY permission, you can see all rerankers or filter by any owner_id. Specifying owner_id without LIST_RERANKER_ANY permission returns PERMISSION_DENIED.", {
23066
- owner_id: external_exports.string().describe("Filter rerankers by owner ID. With LIST_RERANKER_ANY permission, omitting this shows all accessible rerankers; providing it filters by that owner. With LIST_RERANKER_OWN permission, only your own rerankers are shown regardless of this parameter (PERMISSION_DENIED if set to another user).").optional(),
23067
- provider_type: external_exports.enum(["OPENAI", "VLLM", "TEI", "LLAMA_CPP", "VOYAGE", "COHERE", "JINA"]).describe("Filter rerankers by provider type. Allowed values match the ProviderType schema.").optional(),
23794
+ server.tool("goodmem_rerankers_list", "List rerankers\n\nRetrieves a list of reranker configurations accessible to the caller, with optional filtering. IMPORTANT: Pagination is NOT supported - all matching results are returned. Results are ordered by created_at descending. Stored credentials are never included in list responses. LABEL FILTERS: Label filters accept either label.<key>=<value> or label[key]=value (for example, label.environment=production or label[environment]=production). AUTHORIZATION: Requires LIST_RERANKER on the GoodMem instance. Each returned reranker must also be visible through READ_RERANKER; unauthorized rerankers are filtered in PostgreSQL. The owner_id parameter filters that already-authorized result set and does not grant additional visibility.", {
23795
+ owner_id: external_exports.string().describe("Filter the already-authorized result set by owner principal UUID. Omitting this parameter does not bypass per-reranker READ_RERANKER filtering.").optional(),
23796
+ provider_type: external_exports.enum(["OPENAI", "VLLM", "TEI", "LLAMA_CPP", "VOYAGE", "COHERE", "JINA", "DASHSCOPE", "GEMINI"]).describe("Filter rerankers by provider type. Allowed values match the ProviderType schema.").optional(),
23068
23797
  label: external_exports.record(external_exports.string(), external_exports.string()).describe("Filter by label. Each key-value pair is sent as label[key]=value query parameter.").optional()
23069
- }, async (args) => {
23798
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
23070
23799
  const query = {};
23071
23800
  if (args.owner_id !== void 0)
23072
23801
  query["ownerId"] = String(args.owner_id);
@@ -23079,7 +23808,7 @@ function registerTools() {
23079
23808
  const result = await callApi("GET", `/v1/rerankers`, void 0, query);
23080
23809
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
23081
23810
  });
23082
- server.tool("goodmem_rerankers_update", "Update a reranker\n\nUpdates an existing reranker configuration including display information, endpoint configuration, model parameters, credentials, and labels. All fields are optional - only specified fields will be updated. IMMUTABLE FIELDS: providerType and ownerId cannot be changed after creation. SUPPORTED_MODALITIES UPDATE: If the array contains >=1 elements, it replaces the stored set; if empty or omitted, no change occurs and it does not count as an update by itself. Returns ALREADY_EXISTS if update would create duplicate with same {ownerId, providerType, endpoint_url, api_path, model_identifier, credentialsFingerprint} after URL canonicalization (HTTP 409 Conflict / ALREADY_EXISTS). Requires UPDATE_RERANKER_OWN permission for rerankers you own (or UPDATE_RERANKER_ANY for admin users). This operation is idempotent.", {
23811
+ server.tool("goodmem_rerankers_update", "Update a reranker\n\nUpdates an existing reranker configuration including display information, endpoint configuration, model parameters, credentials, and labels. All fields are optional - only specified fields will be updated. IMMUTABLE FIELDS: providerType and ownerId cannot be changed after creation. SUPPORTED_MODALITIES UPDATE: If the array contains >=1 elements, it replaces the stored set; if empty or omitted, no change occurs and it does not count as an update by itself. Returns ALREADY_EXISTS if update would create an equivalent reranker configuration for this owner after endpoint canonicalization and provider-default resolution (HTTP 409 Conflict / ALREADY_EXISTS). Requires UPDATE_RERANKER on the requested reranker. This operation is idempotent.", {
23083
23812
  id: external_exports.string().describe("The unique identifier of the reranker to update"),
23084
23813
  display_name: external_exports.string().describe("User-facing name of the reranker").optional(),
23085
23814
  description: external_exports.string().describe("Description of the reranker").optional(),
@@ -23103,12 +23832,13 @@ function registerTools() {
23103
23832
  quotaProjectId: external_exports.string().describe("Optional quota project used for billing").optional()
23104
23833
  }).passthrough().describe("Configuration when kind is CREDENTIAL_KIND_GCP_ADC").optional(),
23105
23834
  labels: external_exports.record(external_exports.string(), external_exports.string()).describe('Optional annotations to aid operators (e.g., "owner=vertex")').optional()
23106
- }).passthrough().describe("Structured credential payload describing how to authenticate with the provider. Omit to keep existing credentials.").optional(),
23835
+ }).passthrough().describe("Replace stored credentials. Omit this field to preserve the current credentials; a present empty payload is invalid and never clears them.").optional(),
23107
23836
  replace_labels: external_exports.record(external_exports.string(), external_exports.string()).describe("Replace all existing labels with these (mutually exclusive with merge_labels)").optional(),
23108
23837
  merge_labels: external_exports.record(external_exports.string(), external_exports.string()).describe("Merge these labels with existing ones (mutually exclusive with replace_labels)").optional(),
23109
23838
  version: external_exports.string().describe("Version information").optional(),
23110
- monitoring_endpoint: external_exports.string().describe("Monitoring endpoint URL").optional()
23111
- }, async (args) => {
23839
+ monitoring_endpoint: external_exports.string().describe("Monitoring endpoint URL").optional(),
23840
+ dashscope_api_dialect: external_exports.enum(["RERANK_NATIVE_NESTED", "RERANK_COMPATIBLE_FLAT"]).describe("Update the DashScope request and response API dialect. Valid only for the DASHSCOPE provider. Omit to preserve the stored dialect. Changing api_path to a recognized canonical DashScope dialect infers its matching dialect; a custom path preserves an existing dialect, while a legacy null dialect is inferred.").optional()
23841
+ }, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
23112
23842
  if (typeof args.id !== "string" || args.id === "") {
23113
23843
  throw new Error("'id' must be a non-empty string");
23114
23844
  }
@@ -23139,18 +23869,117 @@ function registerTools() {
23139
23869
  body["version"] = args.version;
23140
23870
  if (args.monitoring_endpoint !== void 0)
23141
23871
  body["monitoringEndpoint"] = args.monitoring_endpoint;
23872
+ if (args.dashscope_api_dialect !== void 0)
23873
+ body["dashscopeApiDialect"] = args.dashscope_api_dialect;
23142
23874
  const result = await callApi("PUT", `/v1/rerankers/${encodeURIComponent(String(args.id))}`, body);
23143
23875
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
23144
23876
  });
23145
- server.tool("goodmem_spaces_create", "Create a new Space\n\nCreates a new space with the provided name, labels, and embedder configuration. A space is a container for organizing related memories. OWNER DEFAULTS: Owner defaults to authenticated user unless owner_id is provided (requires CREATE_SPACE_ANY if differs). EMBEDDER REQUIREMENTS: At least one embedder configuration must be specified. DUPLICATE DETECTION: Returns ALREADY_EXISTS if another space exists with identical {owner_id, name} (case-sensitive). Requires CREATE_SPACE_OWN permission (or CREATE_SPACE_ANY for admin users). This operation is NOT idempotent.", {
23877
+ server.tool("goodmem_service_identities_create", "Create a service identity\n\nCreates one production-workload identity owned by the authenticated human. No API key, role, grant, or authentication mapping is created implicitly.", {
23878
+ service_identity_id: external_exports.string().describe("Optional client-provided UUID; generated by the server when omitted.").optional(),
23879
+ display_name: external_exports.string().describe("Globally unique, nonblank operator-facing name."),
23880
+ description: external_exports.string().describe("Optional operator description.").optional(),
23881
+ labels: external_exports.record(external_exports.string(), external_exports.string()).describe("Optional labels for organization and filtering. At most 20 entries; keys and values contain at most 255 characters; keys use [a-z0-9._-].").optional()
23882
+ }, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
23883
+ const body = {};
23884
+ if (args.service_identity_id !== void 0)
23885
+ body["serviceIdentityId"] = args.service_identity_id;
23886
+ if (args.display_name !== void 0)
23887
+ body["displayName"] = args.display_name;
23888
+ if (args.description !== void 0)
23889
+ body["description"] = args.description;
23890
+ if (args.labels !== void 0)
23891
+ body["labels"] = args.labels;
23892
+ const result = await callApi("POST", `/v1/service-identities`, body);
23893
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
23894
+ });
23895
+ server.tool("goodmem_service_identities_delete", "Delete a service identity\n\nPermanently soft-deletes the principal. Its stored credentials remain audit records but can no longer authenticate because their subject is deleted. Repeating an authorized delete succeeds without rewriting audit data.", {
23896
+ id: external_exports.string().describe("Service-identity UUID")
23897
+ }, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
23898
+ if (typeof args.id !== "string" || args.id === "") {
23899
+ throw new Error("'id' must be a non-empty string");
23900
+ }
23901
+ await callApi("DELETE", `/v1/service-identities/${encodeURIComponent(String(args.id))}`, void 0);
23902
+ return { content: [{ type: "text", text: "Deleted successfully." }] };
23903
+ });
23904
+ server.tool("goodmem_service_identities_get", "Get a service identity\n\nReturns a service identity after applying READ_SERVICE_IDENTITY authority. include_deleted permits an authorized caller to inspect a permanent tombstone; it does not grant additional authority.", {
23905
+ id: external_exports.string().describe("Service-identity UUID"),
23906
+ include_deleted: external_exports.boolean().describe("Permit an authorized read of a permanent tombstone").optional()
23907
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
23908
+ if (typeof args.id !== "string" || args.id === "") {
23909
+ throw new Error("'id' must be a non-empty string");
23910
+ }
23911
+ const query = {};
23912
+ if (args.include_deleted !== void 0)
23913
+ query["includeDeleted"] = String(args.include_deleted);
23914
+ const result = await callApi("GET", `/v1/service-identities/${encodeURIComponent(String(args.id))}`, void 0, query);
23915
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
23916
+ });
23917
+ server.tool("goodmem_service_identities_list", "List service identities\n\nRequires LIST_SERVICE_IDENTITY on the GoodMem instance and READ_SERVICE_IDENTITY on each returned row. Owner and label filters, lifecycle filtering, authorization, and keyset pagination execute in PostgreSQL. LABEL FILTERS: Label filters accept either label.<key>=<value> or label[key]=value (for example, label.environment=production or label[environment]=production).", {
23918
+ owner_principal_id: external_exports.string().describe("Exact current administrative-owner principal UUID").optional(),
23919
+ include_deleted: external_exports.boolean().describe("Include readable permanent tombstones").optional(),
23920
+ max_results: external_exports.number().int().describe("Page size; defaults to 50 and must be between 1 and 1000").optional(),
23921
+ next_token: external_exports.string().describe("Opaque continuation token").optional(),
23922
+ label: external_exports.record(external_exports.string(), external_exports.string()).describe("Filter by label. Each key-value pair is sent as label[key]=value query parameter.").optional()
23923
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
23924
+ const query = {};
23925
+ if (args.owner_principal_id !== void 0)
23926
+ query["ownerPrincipalId"] = String(args.owner_principal_id);
23927
+ if (args.include_deleted !== void 0)
23928
+ query["includeDeleted"] = String(args.include_deleted);
23929
+ if (args.max_results !== void 0)
23930
+ query["maxResults"] = String(args.max_results);
23931
+ if (args.next_token !== void 0)
23932
+ query["nextToken"] = String(args.next_token);
23933
+ if (args.label) {
23934
+ for (const [k, v] of Object.entries(args.label))
23935
+ query[`label[${k}]`] = v;
23936
+ }
23937
+ const result = await callApi("GET", `/v1/service-identities`, void 0, query);
23938
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
23939
+ });
23940
+ server.tool("goodmem_service_identities_transfer_ownership", "Transfer service-identity ownership\n\nTransfers administrative ownership to another active principal. A service identity cannot own itself. The service identity's subject, immutable creator, credentials, grants, and roles are unchanged.", {
23941
+ id: external_exports.string().describe("Service-identity UUID"),
23942
+ new_owner_id: external_exports.string().describe("Existing principal UUID that will become the new owner.")
23943
+ }, { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false }, async (args) => {
23944
+ if (typeof args.id !== "string" || args.id === "") {
23945
+ throw new Error("'id' must be a non-empty string");
23946
+ }
23947
+ const body = {};
23948
+ if (args.new_owner_id !== void 0)
23949
+ body["newOwnerId"] = args.new_owner_id;
23950
+ const result = await callApi("POST", `/v1/service-identities/${encodeURIComponent(String(args.id))}:transferOwnership`, body);
23951
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
23952
+ });
23953
+ server.tool("goodmem_service_identities_update", "Update a service identity\n\nUpdates only fields present in the request. Empty description clears that optional field. Ownership changes use the dedicated transfer endpoint.", {
23954
+ id: external_exports.string().describe("Service-identity UUID"),
23955
+ display_name: external_exports.string().describe("Replacement display name. A present blank value is invalid.").optional(),
23956
+ description: external_exports.string().describe("Replacement description. An empty string clears the description.").optional(),
23957
+ replace_labels: external_exports.record(external_exports.string(), external_exports.string()).describe("Complete replacement label map; an empty map clears all labels and is mutually exclusive with merge_labels. At most 20 entries; keys and values contain at most 255 characters; keys use [a-z0-9._-].").optional(),
23958
+ merge_labels: external_exports.record(external_exports.string(), external_exports.string()).describe("Labels to upsert; must contain at least one entry and is mutually exclusive with replace_labels. The final stored map may contain at most 20 entries; keys and values contain at most 255 characters; keys use [a-z0-9._-].").optional()
23959
+ }, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
23960
+ if (typeof args.id !== "string" || args.id === "") {
23961
+ throw new Error("'id' must be a non-empty string");
23962
+ }
23963
+ const body = {};
23964
+ if (args.display_name !== void 0)
23965
+ body["displayName"] = args.display_name;
23966
+ if (args.description !== void 0)
23967
+ body["description"] = args.description;
23968
+ if (args.replace_labels !== void 0)
23969
+ body["replaceLabels"] = args.replace_labels;
23970
+ if (args.merge_labels !== void 0)
23971
+ body["mergeLabels"] = args.merge_labels;
23972
+ const result = await callApi("PUT", `/v1/service-identities/${encodeURIComponent(String(args.id))}`, body);
23973
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
23974
+ });
23975
+ server.tool("goodmem_spaces_create", "Create a new Space\n\nCreates a new space with the provided name, labels, and embedder configuration. A space is a container for organizing related memories. OWNER DEFAULTS: Owner defaults to the authenticated principal unless owner_id is provided; CREATE_SPACE is evaluated against the proposed space and owner. EMBEDDER REQUIREMENTS: At least one embedder configuration must be specified. Every referenced embedder must exist, and the caller must have EXECUTE_EMBEDDER on each one. DUPLICATE DETECTION: Returns ALREADY_EXISTS if another space exists with identical {owner_id, name} (case-sensitive). This operation is NOT idempotent.", {
23146
23976
  name: external_exports.string().describe("The desired name for the space. Must be unique within the user's scope."),
23147
23977
  labels: external_exports.record(external_exports.string(), external_exports.string()).describe("A set of key-value pairs to categorize or tag the space. Used for filtering and organizational purposes.").optional(),
23148
23978
  space_embedders: external_exports.array(external_exports.object({
23149
23979
  embedderId: external_exports.string().describe("The UUID for the embedder to associate with the space."),
23150
23980
  defaultRetrievalWeight: external_exports.number().describe("Relative weight for this embedder used by default during retrieval. If omitted, defaults to 1.0; values need not sum to 1 and can be overridden per request.").optional()
23151
23981
  }).passthrough()).describe("List of embedder configurations to associate with this space. At least one embedder configuration is required. Each specifies an embedder ID and a relative default retrieval weight used when no per-request overrides are provided."),
23152
- public_read: external_exports.boolean().describe("Indicates if the space and its memories can be read by unauthenticated users or users other than the owner. Defaults to false.").optional(),
23153
- owner_id: external_exports.string().describe("Optional owner ID. If not provided, derived from the authentication context. Requires CREATE_SPACE_ANY permission if specified.").optional(),
23982
+ owner_id: external_exports.string().describe("Optional owner principal UUID. If omitted, defaults to the authenticated principal. CREATE_SPACE is evaluated against the proposed space and owner.").optional(),
23154
23983
  default_chunking_config: external_exports.object({
23155
23984
  none: external_exports.any().describe("No chunking strategy - preserve original content as single unit").optional(),
23156
23985
  recursive: external_exports.object({
@@ -23169,7 +23998,7 @@ function registerTools() {
23169
23998
  }).passthrough().describe("Sentence-based chunking strategy with language detection").optional()
23170
23999
  }).passthrough().describe("Default chunking strategy for memories in this space"),
23171
24000
  space_id: external_exports.string().describe("Optional client-provided UUID for idempotent creation. If not provided, server generates a new UUID. Returns ALREADY_EXISTS if ID is already in use.").optional()
23172
- }, async (args) => {
24001
+ }, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
23173
24002
  const body = {};
23174
24003
  if (args.name !== void 0)
23175
24004
  body["name"] = args.name;
@@ -23177,8 +24006,6 @@ function registerTools() {
23177
24006
  body["labels"] = args.labels;
23178
24007
  if (args.space_embedders !== void 0)
23179
24008
  body["spaceEmbedders"] = args.space_embedders;
23180
- if (args.public_read !== void 0)
23181
- body["publicRead"] = args.public_read;
23182
24009
  if (args.owner_id !== void 0)
23183
24010
  body["ownerId"] = args.owner_id;
23184
24011
  if (args.default_chunking_config !== void 0)
@@ -23188,18 +24015,18 @@ function registerTools() {
23188
24015
  const result = await callApi("POST", `/v1/spaces`, body);
23189
24016
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
23190
24017
  });
23191
- server.tool("goodmem_spaces_delete", "Delete a space\n\nPermanently deletes a space and all associated content. This operation cannot be undone. CASCADE DELETION: Removes the space record and cascades deletion to associated memories, chunks, and embedder associations. Requires DELETE_SPACE_OWN permission for spaces you own (or DELETE_SPACE_ANY for admin users). This operation is safe to retry - may return NOT_FOUND if already deleted.", {
24018
+ server.tool("goodmem_spaces_delete", "Delete a space\n\nPermanently deletes a space and all associated content. This operation cannot be undone. CASCADE DELETION: Removes the space record and cascades deletion to associated memories, chunks, and embedder associations. Requires DELETE_SPACE on the requested space. This operation is safe to retry and may return NOT_FOUND if the space was already deleted.", {
23192
24019
  id: external_exports.string().describe("The unique identifier of the space to delete")
23193
- }, async (args) => {
24020
+ }, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
23194
24021
  if (typeof args.id !== "string" || args.id === "") {
23195
24022
  throw new Error("'id' must be a non-empty string");
23196
24023
  }
23197
24024
  await callApi("DELETE", `/v1/spaces/${encodeURIComponent(String(args.id))}`, void 0);
23198
24025
  return { content: [{ type: "text", text: "Deleted successfully." }] };
23199
24026
  });
23200
- server.tool("goodmem_spaces_get", "Get a space by ID\n\nRetrieves a specific space by its unique identifier. Returns the complete space information, including name, labels, embedder configuration, and metadata. PUBLIC SPACE ACCESS: When public_read=true, any authenticated user can retrieve the space metadata, bypassing ownership checks. Otherwise, requires ownership or DISPLAY_SPACE_ANY permission. Requires DISPLAY_SPACE_OWN permission for owned spaces (or DISPLAY_SPACE_ANY for admin users to view any space). This is a read-only operation safe to retry.", {
24027
+ server.tool("goodmem_spaces_get", "Get a space by ID\n\nRetrieves a specific space by its unique identifier. Returns the complete space information, including name, labels, embedder configuration, and metadata. Requires READ_SPACE on the requested space. The service distinguishes a missing space from an existing space the caller cannot read. This is a read-only operation safe to retry.", {
23201
24028
  id: external_exports.string().describe("The unique identifier of the space to retrieve")
23202
- }, async (args) => {
24029
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
23203
24030
  if (typeof args.id !== "string" || args.id === "") {
23204
24031
  throw new Error("'id' must be a non-empty string");
23205
24032
  }
@@ -23207,14 +24034,14 @@ function registerTools() {
23207
24034
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
23208
24035
  });
23209
24036
  server.tool("goodmem_spaces_list", "List spaces accessible to the caller, with optional filtering by owner, labels, and name. Results are paginated; pass the response's `next_token` back as a query parameter to fetch subsequent pages.", {
23210
- owner_id: external_exports.string().describe("Filter spaces by owner ID. With LIST_SPACE_ANY permission and owner_id omitted, returns all visible spaces. Otherwise returns caller-owned spaces only. Specifying owner_id without LIST_SPACE_ANY returns PERMISSION_DENIED.").optional(),
24037
+ owner_id: external_exports.string().describe("Filter the already-authorized result set by owner principal UUID. Omitting this parameter does not bypass per-space READ_SPACE filtering.").optional(),
23211
24038
  name_filter: external_exports.string().describe("Filter spaces by name using glob pattern matching").optional(),
23212
24039
  max_results: external_exports.number().int().describe("Maximum number of results to return in a single page (defaults to 50, clamped to [1, 1000])").optional(),
23213
24040
  next_token: external_exports.string().describe("Pagination token for retrieving the next set of results").optional(),
23214
24041
  sort_by: external_exports.enum(["created_time", "updated_time", "name"]).describe("Field to sort by: 'created_time', 'updated_time', or 'name' (default: 'created_time'). Unsupported values return INVALID_ARGUMENT.").optional(),
23215
24042
  sort_order: external_exports.enum(["ASCENDING", "DESCENDING", "SORT_ORDER_UNSPECIFIED"]).describe("Sort order (ASCENDING or DESCENDING, default: DESCENDING)").optional(),
23216
24043
  label: external_exports.record(external_exports.string(), external_exports.string()).describe("Filter by label. Each key-value pair is sent as label[key]=value query parameter.").optional()
23217
- }, async (args) => {
24044
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
23218
24045
  const query = {};
23219
24046
  if (args.owner_id !== void 0)
23220
24047
  query["ownerId"] = String(args.owner_id);
@@ -23235,13 +24062,25 @@ function registerTools() {
23235
24062
  const result = await callApi("GET", `/v1/spaces`, void 0, query);
23236
24063
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
23237
24064
  });
23238
- server.tool("goodmem_spaces_update", "Update a space\n\nUpdates an existing space with new values for the specified fields. Only name, public_read, and labels can be updated. Fields not included in the request remain unchanged. IMMUTABLE FIELDS: space_embedders, default_chunking_config, and ownerId cannot be modified after creation. NAME UNIQUENESS: Name must be unique per owner - returns ALREADY_EXISTS if name conflicts with existing space. Requires UPDATE_SPACE_OWN permission for spaces you own (or UPDATE_SPACE_ANY for admin users). This operation is idempotent.", {
24065
+ server.tool("goodmem_spaces_transfer_ownership", "Transfer ownership of a space\n\nTransfers an existing space to another active human or service principal. The current space owner, the GoodMem instance owner, or an instance administrator may transfer it; a space-scoped administrator cannot. Only owner and update-audit fields change. Memories, embedder associations, grants, and role assignments remain unchanged. This operation is not idempotent under response semantics: after an unknown outcome, read the space before retrying.", {
24066
+ id: external_exports.string().describe("UUID of the existing space to transfer"),
24067
+ new_owner_id: external_exports.string().describe("Existing principal UUID that will become the new owner.")
24068
+ }, { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false }, async (args) => {
24069
+ if (typeof args.id !== "string" || args.id === "") {
24070
+ throw new Error("'id' must be a non-empty string");
24071
+ }
24072
+ const body = {};
24073
+ if (args.new_owner_id !== void 0)
24074
+ body["newOwnerId"] = args.new_owner_id;
24075
+ const result = await callApi("POST", `/v1/spaces/${encodeURIComponent(String(args.id))}:transferOwnership`, body);
24076
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
24077
+ });
24078
+ server.tool("goodmem_spaces_update", "Update a space\n\nUpdates an existing space with new values for the specified fields. Only name and labels can be updated. Fields not included in the request remain unchanged. IMMUTABLE FIELDS: space_embedders, default_chunking_config, and ownerId cannot be modified after creation. NAME UNIQUENESS: Name must be unique per owner - returns ALREADY_EXISTS if name conflicts with an existing space. Requires UPDATE_SPACE on the requested space. This operation is idempotent.", {
23239
24079
  id: external_exports.string().describe("The unique identifier of the space to update"),
23240
24080
  name: external_exports.string().describe("The new name for the space.").optional(),
23241
- public_read: external_exports.boolean().describe("Whether the space is publicly readable by all users.").optional(),
23242
24081
  replace_labels: external_exports.record(external_exports.string(), external_exports.string()).describe("Labels to replace all existing labels. Mutually exclusive with merge_labels.").optional(),
23243
24082
  merge_labels: external_exports.record(external_exports.string(), external_exports.string()).describe("Labels to merge with existing labels. Mutually exclusive with replace_labels.").optional()
23244
- }, async (args) => {
24083
+ }, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
23245
24084
  if (typeof args.id !== "string" || args.id === "") {
23246
24085
  throw new Error("'id' must be a non-empty string");
23247
24086
  }
@@ -23252,8 +24091,6 @@ function registerTools() {
23252
24091
  const body = {};
23253
24092
  if (args.name !== void 0)
23254
24093
  body["name"] = args.name;
23255
- if (args.public_read !== void 0)
23256
- body["publicRead"] = args.public_read;
23257
24094
  if (args.replace_labels !== void 0)
23258
24095
  body["replaceLabels"] = args.replace_labels;
23259
24096
  if (args.merge_labels !== void 0)
@@ -23261,18 +24098,80 @@ function registerTools() {
23261
24098
  const result = await callApi("PUT", `/v1/spaces/${encodeURIComponent(String(args.id))}`, body);
23262
24099
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
23263
24100
  });
23264
- server.tool("goodmem_system_info", "Retrieve server build metadata\n\nReturns the server's advertised semantic version, git metadata, build timestamp, and optional capability flags. The endpoint is intentionally unauthenticated so bootstrap tooling can call it before API keys exist.", {}, async (args) => {
24101
+ server.tool("goodmem_system_info", "Retrieve server build metadata\n\nReturns the server's advertised semantic version, git metadata, build timestamp, and optional capability flags. The endpoint is intentionally unauthenticated so bootstrap tooling can call it before API keys exist.", {}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
23265
24102
  const result = await callApi("GET", `/v1/system/info`, void 0);
23266
24103
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
23267
24104
  });
23268
- server.tool("goodmem_system_init", "Initialize the system\n\nInitializes the system by creating a root user and API key. This endpoint should only be called once during first-time setup. If the system is already initialized, the endpoint will return a success response without creating new credentials.", {}, async (args) => {
24105
+ server.tool("goodmem_system_init", "Initialize the system\n\nInitializes the system by creating a root user and API key. This endpoint should only be called once during first-time setup. If the system is already initialized, the endpoint will return a success response without creating new credentials.", {}, { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }, async (args) => {
23269
24106
  const result = await callApi("POST", `/v1/system/init`, void 0);
23270
24107
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
23271
24108
  });
24109
+ server.tool("goodmem_user_enrollments_complete", "Complete human-user enrollment\n\nExchanges a one-time enrollment credential for the human's initial self-owned API key. This endpoint does not use ordinary API-key authentication. Omit both optional key fields for server generation; a fresh response discloses the raw key exactly once and cannot be safely retried after an ambiguous outcome. Supply and retain both fields for client mode, where only an exact tuple retry is safe and raw material is never echoed. Status precedence is request-shape errors (400), then the same generic authentication failure for every nonblank unusable enrollment (401); key conflicts or concurrent lifecycle winners return 409, rate limits return 429, and unexpected failures return 500.", {
24110
+ enrollment_token: external_exports.string().describe("Required one-time enrollment credential. Never log or persist it."),
24111
+ api_key_id: external_exports.string().describe("Optional client-generated API-key UUID retained for exact retries. Must be supplied together with raw_api_key, or both fields must be omitted.").optional(),
24112
+ raw_api_key: external_exports.string().describe("Optional canonical client-generated API key. Must be supplied together with api_key_id, never logged, and retained for exact retries; omit both fields for server generation.").optional()
24113
+ }, { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false }, async (args) => {
24114
+ const body = {};
24115
+ if (args.enrollment_token !== void 0)
24116
+ body["enrollmentToken"] = args.enrollment_token;
24117
+ if (args.api_key_id !== void 0)
24118
+ body["apiKeyId"] = args.api_key_id;
24119
+ if (args.raw_api_key !== void 0)
24120
+ body["rawApiKey"] = args.raw_api_key;
24121
+ const result = await callApi("POST", `/v1/user-enrollments:complete`, body);
24122
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
24123
+ });
24124
+ server.tool("goodmem_users_create", "Create a human user\n\nCreates one dormant human user after requiring instance-wide CREATE_USER authority. Creation does not issue a credential or create a role, grant, or authentication mapping.", {
24125
+ user_id: external_exports.string().describe("Optional client-provided user UUID; generated by the server when omitted.").optional(),
24126
+ email: external_exports.string().describe("Unique, nonempty email address."),
24127
+ username: external_exports.string().describe("Optional unique username.").optional(),
24128
+ display_name: external_exports.string().describe("Optional human-facing display name.").optional(),
24129
+ labels: external_exports.record(external_exports.string(), external_exports.string()).describe("Optional labels for organization and filtering. At most 20 entries; keys and values contain at most 255 characters; keys use [a-z0-9._-].").optional()
24130
+ }, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
24131
+ const body = {};
24132
+ if (args.user_id !== void 0)
24133
+ body["userId"] = args.user_id;
24134
+ if (args.email !== void 0)
24135
+ body["email"] = args.email;
24136
+ if (args.username !== void 0)
24137
+ body["username"] = args.username;
24138
+ if (args.display_name !== void 0)
24139
+ body["displayName"] = args.display_name;
24140
+ if (args.labels !== void 0)
24141
+ body["labels"] = args.labels;
24142
+ const result = await callApi("POST", `/v1/users`, body);
24143
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
24144
+ });
24145
+ server.tool("goodmem_users_create_enrollment", "Create a human-user enrollment\n\nCreates a short-lived, one-time enrollment credential for an existing dormant human. Requires MANAGE_USER_ENROLLMENT with ANY or EXACT authority on the target user. The raw credential is returned only once. rotate_existing atomically revokes and replaces a live enrollment; an expired enrollment is replaced automatically.", {
24146
+ user_id: external_exports.string().describe("Human-user UUID"),
24147
+ enrollment_id: external_exports.string().describe("Optional client-provided enrollment UUID; generated when omitted.").optional(),
24148
+ rotate_existing: external_exports.boolean().describe("Whether an existing live enrollment may be revoked and atomically replaced.").optional()
24149
+ }, { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false }, async (args) => {
24150
+ if (typeof args.user_id !== "string" || args.user_id === "") {
24151
+ throw new Error("'user_id' must be a non-empty string");
24152
+ }
24153
+ const body = {};
24154
+ if (args.enrollment_id !== void 0)
24155
+ body["enrollmentId"] = args.enrollment_id;
24156
+ if (args.rotate_existing !== void 0)
24157
+ body["rotateExisting"] = args.rotate_existing;
24158
+ const result = await callApi("POST", `/v1/users/${encodeURIComponent(String(args.user_id))}/enrollments`, body);
24159
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
24160
+ });
24161
+ server.tool("goodmem_users_delete", "Delete a human user\n\nPermanently soft-deletes the user and invalidates credentials acting for that subject. The GoodMem instance owner cannot be deleted; transfer ownership first. Repeating an authorized delete succeeds without rewriting audit data.", {
24162
+ id: external_exports.string().describe("User UUID")
24163
+ }, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
24164
+ if (typeof args.id !== "string" || args.id === "") {
24165
+ throw new Error("'id' must be a non-empty string");
24166
+ }
24167
+ await callApi("DELETE", `/v1/users/${encodeURIComponent(String(args.id))}`, void 0);
24168
+ return { content: [{ type: "text", text: "Deleted successfully." }] };
24169
+ });
23272
24170
  server.tool("goodmem_users_get", "Retrieves a user by ID or email address. Exactly one of `id` and `email` must be provided. For getting your own profile, use the `goodmem_users_me` tool.", {
23273
- id: external_exports.string().describe("The UUID of the user to retrieve").optional(),
23274
- email: external_exports.string().describe("The email address of the user to retrieve").optional()
23275
- }, async (args) => {
24171
+ id: external_exports.string().describe("User UUID").optional(),
24172
+ email: external_exports.string().describe("URL-encoded email address").optional(),
24173
+ include_deleted: external_exports.boolean().describe("Permit an authorized read of a permanent tombstone").optional()
24174
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
23276
24175
  const _hasId = typeof args.id === "string" && args.id !== "";
23277
24176
  const _hasEmail = typeof args.email === "string" && args.email !== "";
23278
24177
  if (_hasId && _hasEmail)
@@ -23280,13 +24179,119 @@ function registerTools() {
23280
24179
  if (!_hasId && !_hasEmail)
23281
24180
  throw new Error("Exactly one of 'id', 'email' must be provided (non-empty).");
23282
24181
  const url = _hasEmail ? `/v1/users/email/${encodeURIComponent(String(args.email))}` : `/v1/users/${encodeURIComponent(String(args.id))}`;
23283
- const result = await callApi("GET", url, void 0);
24182
+ const query = {};
24183
+ if (args.include_deleted !== void 0)
24184
+ query["includeDeleted"] = String(args.include_deleted);
24185
+ const result = await callApi("GET", url, void 0, query);
24186
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
24187
+ });
24188
+ server.tool("goodmem_users_get_by_username", "Get user by username\n\nReturns a user selected by exact username after applying READ_USER authority. include_deleted permits an authorized caller to inspect a permanent tombstone; it does not grant additional authority. Missing and unauthorized matches both return 404 so this guessable identifier cannot reveal whether a user exists.", {
24189
+ username: external_exports.string().describe("Exact username"),
24190
+ include_deleted: external_exports.boolean().describe("Permit an authorized read of a permanent tombstone").optional()
24191
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
24192
+ if (typeof args.username !== "string" || args.username === "") {
24193
+ throw new Error("'username' must be a non-empty string");
24194
+ }
24195
+ const query = {};
24196
+ if (args.include_deleted !== void 0)
24197
+ query["includeDeleted"] = String(args.include_deleted);
24198
+ const result = await callApi("GET", `/v1/users/username/${encodeURIComponent(String(args.username))}`, void 0, query);
23284
24199
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
23285
24200
  });
23286
- server.tool("goodmem_users_me", "Get current user profile\n\nRetrieves the authenticated user's profile information including email, display name, and creation time. This endpoint does not require any parameters as it automatically returns the caller's information.", {}, async (args) => {
24201
+ server.tool("goodmem_users_get_enrollment", "Get a human-user enrollment\n\nReturns non-secret metadata for one current or historical enrollment. The target user is resolved before the enrollment, and MANAGE_USER_ENROLLMENT with ANY or EXACT authority on that user is required. Raw enrollment tokens are never returned.", {
24202
+ user_id: external_exports.string().describe("Human-user UUID"),
24203
+ enrollment_id: external_exports.string().describe("Enrollment UUID")
24204
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
24205
+ if (typeof args.user_id !== "string" || args.user_id === "") {
24206
+ throw new Error("'user_id' must be a non-empty string");
24207
+ }
24208
+ if (typeof args.enrollment_id !== "string" || args.enrollment_id === "") {
24209
+ throw new Error("'enrollment_id' must be a non-empty string");
24210
+ }
24211
+ const result = await callApi("GET", `/v1/users/${encodeURIComponent(String(args.user_id))}/enrollments/${encodeURIComponent(String(args.enrollment_id))}`, void 0);
24212
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
24213
+ });
24214
+ server.tool("goodmem_users_list", "List human users\n\nRequires LIST_USER on the GoodMem instance and READ_USER on each returned row. Authorization, label filtering, lifecycle filtering, and keyset pagination run in PostgreSQL. include_deleted expands the lifecycle view but grants no access. include_enrollment_summary requests non-secret bootstrap posture only on active rows where MANAGE_USER_ENROLLMENT is independently authorized. LABEL FILTERS: Label filters accept either label.<key>=<value> or label[key]=value (for example, label.environment=production or label[environment]=production).", {
24215
+ include_deleted: external_exports.boolean().describe("Include readable permanent tombstones").optional(),
24216
+ include_enrollment_summary: external_exports.boolean().describe("Request non-secret enrollment posture on active rows where the caller also has MANAGE_USER_ENROLLMENT").optional(),
24217
+ max_results: external_exports.number().int().describe("Page size; defaults to 50 and must be between 1 and 1000").optional(),
24218
+ next_token: external_exports.string().describe("Opaque continuation token").optional(),
24219
+ label: external_exports.record(external_exports.string(), external_exports.string()).describe("Filter by label. Each key-value pair is sent as label[key]=value query parameter.").optional()
24220
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
24221
+ const query = {};
24222
+ if (args.include_deleted !== void 0)
24223
+ query["includeDeleted"] = String(args.include_deleted);
24224
+ if (args.include_enrollment_summary !== void 0)
24225
+ query["includeEnrollmentSummary"] = String(args.include_enrollment_summary);
24226
+ if (args.max_results !== void 0)
24227
+ query["maxResults"] = String(args.max_results);
24228
+ if (args.next_token !== void 0)
24229
+ query["nextToken"] = String(args.next_token);
24230
+ if (args.label) {
24231
+ for (const [k, v] of Object.entries(args.label))
24232
+ query[`label[${k}]`] = v;
24233
+ }
24234
+ const result = await callApi("GET", `/v1/users`, void 0, query);
24235
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
24236
+ });
24237
+ server.tool("goodmem_users_list_enrollments", "List a human user's enrollments\n\nReturns one newest-first page containing pending, expired, consumed, and revoked enrollment metadata. MANAGE_USER_ENROLLMENT with ANY or EXACT authority on the target user is required. The page never contains raw credentials.", {
24238
+ user_id: external_exports.string().describe("Human-user UUID"),
24239
+ max_results: external_exports.number().int().describe("Page size; defaults to 50 and must be between 1 and 1000").optional(),
24240
+ next_token: external_exports.string().describe("Opaque continuation token").optional()
24241
+ }, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
24242
+ if (typeof args.user_id !== "string" || args.user_id === "") {
24243
+ throw new Error("'user_id' must be a non-empty string");
24244
+ }
24245
+ const query = {};
24246
+ if (args.max_results !== void 0)
24247
+ query["maxResults"] = String(args.max_results);
24248
+ if (args.next_token !== void 0)
24249
+ query["nextToken"] = String(args.next_token);
24250
+ const result = await callApi("GET", `/v1/users/${encodeURIComponent(String(args.user_id))}/enrollments`, void 0, query);
24251
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
24252
+ });
24253
+ server.tool("goodmem_users_me", "Get current user profile\n\nReturns the human-user profile associated with the authenticated principal. Service principals do not have a human-user profile.", {}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
23287
24254
  const result = await callApi("GET", `/v1/users/me`, void 0);
23288
24255
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
23289
24256
  });
24257
+ server.tool("goodmem_users_revoke_enrollment", "Revoke a human-user enrollment\n\nPermanently revokes one outstanding enrollment after requiring MANAGE_USER_ENROLLMENT on its target user. Repeating an authorized revocation succeeds without replacing its original audit provenance. Consumed enrollments cannot be revoked.", {
24258
+ user_id: external_exports.string().describe("Human-user UUID"),
24259
+ enrollment_id: external_exports.string().describe("Enrollment UUID")
24260
+ }, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
24261
+ if (typeof args.user_id !== "string" || args.user_id === "") {
24262
+ throw new Error("'user_id' must be a non-empty string");
24263
+ }
24264
+ if (typeof args.enrollment_id !== "string" || args.enrollment_id === "") {
24265
+ throw new Error("'enrollment_id' must be a non-empty string");
24266
+ }
24267
+ await callApi("DELETE", `/v1/users/${encodeURIComponent(String(args.user_id))}/enrollments/${encodeURIComponent(String(args.enrollment_id))}`, void 0);
24268
+ return { content: [{ type: "text", text: "Deleted successfully." }] };
24269
+ });
24270
+ server.tool("goodmem_users_update", "Update a human user\n\nUpdates only fields present in the request. Empty username or display_name values clear those optional fields. Updating a deleted user fails with 412.", {
24271
+ id: external_exports.string().describe("User UUID"),
24272
+ email: external_exports.string().describe("Replacement email. A present empty value is invalid.").optional(),
24273
+ username: external_exports.string().describe("Replacement username. An empty string clears the username.").optional(),
24274
+ display_name: external_exports.string().describe("Replacement display name. An empty string clears the display name.").optional(),
24275
+ replace_labels: external_exports.record(external_exports.string(), external_exports.string()).describe("Complete replacement label map; an empty map clears all labels and is mutually exclusive with merge_labels. At most 20 entries; keys and values contain at most 255 characters; keys use [a-z0-9._-].").optional(),
24276
+ merge_labels: external_exports.record(external_exports.string(), external_exports.string()).describe("Labels to upsert; must contain at least one entry and is mutually exclusive with replace_labels. The final stored map may contain at most 20 entries; keys and values contain at most 255 characters; keys use [a-z0-9._-].").optional()
24277
+ }, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
24278
+ if (typeof args.id !== "string" || args.id === "") {
24279
+ throw new Error("'id' must be a non-empty string");
24280
+ }
24281
+ const body = {};
24282
+ if (args.email !== void 0)
24283
+ body["email"] = args.email;
24284
+ if (args.username !== void 0)
24285
+ body["username"] = args.username;
24286
+ if (args.display_name !== void 0)
24287
+ body["displayName"] = args.display_name;
24288
+ if (args.replace_labels !== void 0)
24289
+ body["replaceLabels"] = args.replace_labels;
24290
+ if (args.merge_labels !== void 0)
24291
+ body["mergeLabels"] = args.merge_labels;
24292
+ const result = await callApi("PUT", `/v1/users/${encodeURIComponent(String(args.id))}`, body);
24293
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
24294
+ });
23290
24295
  }
23291
24296
  registerTools();
23292
24297
  async function healthCheck() {