@pairsystems/goodmem-mcp 0.1.17 → 0.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -3
- package/dist/index.js +797 -155
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -20988,7 +20988,21 @@ var StdioServerTransport = class {
|
|
|
20988
20988
|
// dist/index.js
|
|
20989
20989
|
var goodmemBaseUrl = process.env.GOODMEM_BASE_URL || "";
|
|
20990
20990
|
var goodmemApiKey = process.env.GOODMEM_API_KEY || "";
|
|
20991
|
-
var
|
|
20991
|
+
var DEFAULT_STREAM_MAX_LINE_BYTES = 8 * 1024 * 1024;
|
|
20992
|
+
function _parseStreamMaxLineBytes(raw) {
|
|
20993
|
+
if (!raw)
|
|
20994
|
+
return 0;
|
|
20995
|
+
const n = Number(raw);
|
|
20996
|
+
return Number.isFinite(n) && n > 0 ? Math.floor(n) : 0;
|
|
20997
|
+
}
|
|
20998
|
+
var goodmemStreamMaxLineBytes = _parseStreamMaxLineBytes(process.env.GOODMEM_STREAM_MAX_LINE_BYTES);
|
|
20999
|
+
function _concatBytes(a, b) {
|
|
21000
|
+
const out = new Uint8Array(a.length + b.length);
|
|
21001
|
+
out.set(a, 0);
|
|
21002
|
+
out.set(b, a.length);
|
|
21003
|
+
return out;
|
|
21004
|
+
}
|
|
21005
|
+
var BASED_ON_GOODMEM_COMMIT = "d868460a59745bd219c2b630d843ef6b555e7d35";
|
|
20992
21006
|
function isConfigured() {
|
|
20993
21007
|
return goodmemBaseUrl !== "";
|
|
20994
21008
|
}
|
|
@@ -21104,18 +21118,61 @@ async function callApiNdjson(method, path, body, query, timeoutSec) {
|
|
|
21104
21118
|
...body ? { body: JSON.stringify(body) } : {}
|
|
21105
21119
|
});
|
|
21106
21120
|
if (!resp.ok) {
|
|
21107
|
-
const
|
|
21108
|
-
throw new Error(`GoodMem API error ${resp.status}: ${_trimBody(
|
|
21121
|
+
const text = await resp.text();
|
|
21122
|
+
throw new Error(`GoodMem API error ${resp.status}: ${_trimBody(text)}`);
|
|
21109
21123
|
}
|
|
21110
|
-
const
|
|
21111
|
-
const
|
|
21112
|
-
|
|
21124
|
+
const maxLineBytes = goodmemStreamMaxLineBytes > 0 ? goodmemStreamMaxLineBytes : DEFAULT_STREAM_MAX_LINE_BYTES;
|
|
21125
|
+
const results = [];
|
|
21126
|
+
if (resp.body === null) {
|
|
21127
|
+
return results;
|
|
21128
|
+
}
|
|
21129
|
+
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`);
|
|
21130
|
+
const reader = resp.body.getReader();
|
|
21131
|
+
const decoder = new TextDecoder("utf-8");
|
|
21132
|
+
let pending = new Uint8Array(0);
|
|
21133
|
+
let lineNo = 0;
|
|
21134
|
+
const _emit = (bytes) => {
|
|
21135
|
+
const line = decoder.decode(bytes).trim();
|
|
21136
|
+
if (!line)
|
|
21137
|
+
return;
|
|
21138
|
+
lineNo++;
|
|
21113
21139
|
try {
|
|
21114
|
-
|
|
21140
|
+
results.push(JSON.parse(line));
|
|
21115
21141
|
} catch (e) {
|
|
21116
|
-
throw new Error(`NDJSON parse error on line ${
|
|
21142
|
+
throw new Error(`NDJSON parse error on line ${lineNo}: ${e instanceof Error ? e.message : String(e)}`);
|
|
21117
21143
|
}
|
|
21118
|
-
}
|
|
21144
|
+
};
|
|
21145
|
+
try {
|
|
21146
|
+
for (; ; ) {
|
|
21147
|
+
const { done, value } = await reader.read();
|
|
21148
|
+
if (done)
|
|
21149
|
+
break;
|
|
21150
|
+
if (value === void 0 || value.length === 0)
|
|
21151
|
+
continue;
|
|
21152
|
+
pending = pending.length === 0 ? value : _concatBytes(pending, value);
|
|
21153
|
+
let nl;
|
|
21154
|
+
while ((nl = pending.indexOf(10)) !== -1) {
|
|
21155
|
+
const lineBytes = pending.subarray(0, nl);
|
|
21156
|
+
if (lineBytes.length > maxLineBytes)
|
|
21157
|
+
throw _overCap();
|
|
21158
|
+
_emit(lineBytes);
|
|
21159
|
+
pending = pending.subarray(nl + 1);
|
|
21160
|
+
}
|
|
21161
|
+
if (pending.length > maxLineBytes)
|
|
21162
|
+
throw _overCap();
|
|
21163
|
+
}
|
|
21164
|
+
if (pending.length > 0) {
|
|
21165
|
+
if (pending.length > maxLineBytes)
|
|
21166
|
+
throw _overCap();
|
|
21167
|
+
_emit(pending);
|
|
21168
|
+
}
|
|
21169
|
+
} finally {
|
|
21170
|
+
try {
|
|
21171
|
+
reader.releaseLock();
|
|
21172
|
+
} catch {
|
|
21173
|
+
}
|
|
21174
|
+
}
|
|
21175
|
+
return results;
|
|
21119
21176
|
}
|
|
21120
21177
|
var MODEL_REGISTRY = {
|
|
21121
21178
|
"embedders": {
|
|
@@ -21220,15 +21277,74 @@ var MODEL_REGISTRY = {
|
|
|
21220
21277
|
],
|
|
21221
21278
|
"maxSequenceLength": 512
|
|
21222
21279
|
},
|
|
21223
|
-
"jina-embeddings-
|
|
21280
|
+
"jina-embeddings-v5-text-small": {
|
|
21224
21281
|
"providerType": "JINA",
|
|
21225
21282
|
"dimensions": {
|
|
21226
21283
|
"default": 1024,
|
|
21227
|
-
"
|
|
21228
|
-
|
|
21229
|
-
|
|
21230
|
-
|
|
21231
|
-
|
|
21284
|
+
"range": [
|
|
21285
|
+
1,
|
|
21286
|
+
1024
|
|
21287
|
+
]
|
|
21288
|
+
},
|
|
21289
|
+
"supportedModalities": [
|
|
21290
|
+
"TEXT"
|
|
21291
|
+
],
|
|
21292
|
+
"maxSequenceLength": 32768
|
|
21293
|
+
},
|
|
21294
|
+
"jina-embeddings-v5-text-nano": {
|
|
21295
|
+
"providerType": "JINA",
|
|
21296
|
+
"dimensions": {
|
|
21297
|
+
"default": 768,
|
|
21298
|
+
"range": [
|
|
21299
|
+
1,
|
|
21300
|
+
768
|
|
21301
|
+
]
|
|
21302
|
+
},
|
|
21303
|
+
"supportedModalities": [
|
|
21304
|
+
"TEXT"
|
|
21305
|
+
],
|
|
21306
|
+
"maxSequenceLength": 8192
|
|
21307
|
+
},
|
|
21308
|
+
"jina-embeddings-v5-omni-small": {
|
|
21309
|
+
"providerType": "JINA",
|
|
21310
|
+
"dimensions": {
|
|
21311
|
+
"default": 1024,
|
|
21312
|
+
"range": [
|
|
21313
|
+
1,
|
|
21314
|
+
1024
|
|
21315
|
+
]
|
|
21316
|
+
},
|
|
21317
|
+
"supportedModalities": [
|
|
21318
|
+
"TEXT",
|
|
21319
|
+
"IMAGE",
|
|
21320
|
+
"AUDIO",
|
|
21321
|
+
"VIDEO"
|
|
21322
|
+
],
|
|
21323
|
+
"maxSequenceLength": 32768
|
|
21324
|
+
},
|
|
21325
|
+
"jina-embeddings-v5-omni-nano": {
|
|
21326
|
+
"providerType": "JINA",
|
|
21327
|
+
"dimensions": {
|
|
21328
|
+
"default": 768,
|
|
21329
|
+
"range": [
|
|
21330
|
+
1,
|
|
21331
|
+
768
|
|
21332
|
+
]
|
|
21333
|
+
},
|
|
21334
|
+
"supportedModalities": [
|
|
21335
|
+
"TEXT",
|
|
21336
|
+
"IMAGE",
|
|
21337
|
+
"AUDIO",
|
|
21338
|
+
"VIDEO"
|
|
21339
|
+
],
|
|
21340
|
+
"maxSequenceLength": 8192
|
|
21341
|
+
},
|
|
21342
|
+
"jina-embeddings-v4": {
|
|
21343
|
+
"providerType": "JINA",
|
|
21344
|
+
"dimensions": {
|
|
21345
|
+
"default": 1536,
|
|
21346
|
+
"range": [
|
|
21347
|
+
1,
|
|
21232
21348
|
2048
|
|
21233
21349
|
]
|
|
21234
21350
|
},
|
|
@@ -21238,17 +21354,40 @@ var MODEL_REGISTRY = {
|
|
|
21238
21354
|
],
|
|
21239
21355
|
"maxSequenceLength": 32768
|
|
21240
21356
|
},
|
|
21357
|
+
"jina-code-embeddings-1.5b": {
|
|
21358
|
+
"providerType": "JINA",
|
|
21359
|
+
"dimensions": {
|
|
21360
|
+
"default": 1536,
|
|
21361
|
+
"range": [
|
|
21362
|
+
1,
|
|
21363
|
+
1536
|
|
21364
|
+
]
|
|
21365
|
+
},
|
|
21366
|
+
"supportedModalities": [
|
|
21367
|
+
"TEXT"
|
|
21368
|
+
],
|
|
21369
|
+
"maxSequenceLength": 32768
|
|
21370
|
+
},
|
|
21371
|
+
"jina-code-embeddings-0.5b": {
|
|
21372
|
+
"providerType": "JINA",
|
|
21373
|
+
"dimensions": {
|
|
21374
|
+
"default": 896,
|
|
21375
|
+
"range": [
|
|
21376
|
+
1,
|
|
21377
|
+
896
|
|
21378
|
+
]
|
|
21379
|
+
},
|
|
21380
|
+
"supportedModalities": [
|
|
21381
|
+
"TEXT"
|
|
21382
|
+
],
|
|
21383
|
+
"maxSequenceLength": 32768
|
|
21384
|
+
},
|
|
21241
21385
|
"jina-embeddings-v3": {
|
|
21242
21386
|
"providerType": "JINA",
|
|
21243
21387
|
"dimensions": {
|
|
21244
21388
|
"default": 1024,
|
|
21245
|
-
"
|
|
21246
|
-
|
|
21247
|
-
64,
|
|
21248
|
-
128,
|
|
21249
|
-
256,
|
|
21250
|
-
512,
|
|
21251
|
-
768,
|
|
21389
|
+
"range": [
|
|
21390
|
+
1,
|
|
21252
21391
|
1024
|
|
21253
21392
|
]
|
|
21254
21393
|
},
|
|
@@ -21340,12 +21479,8 @@ var MODEL_REGISTRY = {
|
|
|
21340
21479
|
"providerType": "JINA",
|
|
21341
21480
|
"dimensions": {
|
|
21342
21481
|
"default": 1024,
|
|
21343
|
-
"
|
|
21344
|
-
|
|
21345
|
-
128,
|
|
21346
|
-
256,
|
|
21347
|
-
512,
|
|
21348
|
-
768,
|
|
21482
|
+
"range": [
|
|
21483
|
+
1,
|
|
21349
21484
|
1024
|
|
21350
21485
|
]
|
|
21351
21486
|
},
|
|
@@ -21403,6 +21538,22 @@ var MODEL_REGISTRY = {
|
|
|
21403
21538
|
],
|
|
21404
21539
|
"maxSequenceLength": 32e3
|
|
21405
21540
|
},
|
|
21541
|
+
"voyage-code-4": {
|
|
21542
|
+
"providerType": "VOYAGE",
|
|
21543
|
+
"dimensions": {
|
|
21544
|
+
"default": 1024,
|
|
21545
|
+
"allowed": [
|
|
21546
|
+
256,
|
|
21547
|
+
512,
|
|
21548
|
+
1024,
|
|
21549
|
+
2048
|
|
21550
|
+
]
|
|
21551
|
+
},
|
|
21552
|
+
"supportedModalities": [
|
|
21553
|
+
"TEXT"
|
|
21554
|
+
],
|
|
21555
|
+
"maxSequenceLength": 32e3
|
|
21556
|
+
},
|
|
21406
21557
|
"voyage-code-3": {
|
|
21407
21558
|
"providerType": "VOYAGE",
|
|
21408
21559
|
"dimensions": {
|
|
@@ -21876,12 +22027,13 @@ function autoInferFromRegistry(registryType, args) {
|
|
|
21876
22027
|
}
|
|
21877
22028
|
var server = new McpServer({
|
|
21878
22029
|
name: "goodmem",
|
|
21879
|
-
version: "0.1.
|
|
22030
|
+
version: "0.1.18"
|
|
21880
22031
|
});
|
|
21881
22032
|
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
22033
|
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
|
-
|
|
22034
|
+
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(),
|
|
22035
|
+
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()
|
|
22036
|
+
}, { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true }, async (args) => {
|
|
21885
22037
|
if (!/^https?:\/\//i.test(args.base_url)) {
|
|
21886
22038
|
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
22039
|
}
|
|
@@ -21904,6 +22056,9 @@ server.tool("goodmem_configure", "Configure GoodMem server connection. Call this
|
|
|
21904
22056
|
const info = await resp.json().catch(() => ({}));
|
|
21905
22057
|
goodmemBaseUrl = args.base_url;
|
|
21906
22058
|
goodmemApiKey = args.api_key ?? "";
|
|
22059
|
+
if (args.stream_max_line_bytes !== void 0) {
|
|
22060
|
+
goodmemStreamMaxLineBytes = args.stream_max_line_bytes;
|
|
22061
|
+
}
|
|
21907
22062
|
const keyNote = goodmemApiKey === "" ? " (bootstrap-only session \u2014 no api_key set)" : "";
|
|
21908
22063
|
return { content: [{ type: "text", text: `Connected to GoodMem at ${goodmemBaseUrl}. Server version: ${info.version ?? "unknown"}.${keyNote}` }] };
|
|
21909
22064
|
} catch (err) {
|
|
@@ -21918,7 +22073,7 @@ server.tool("goodmem_configure", "Configure GoodMem server connection. Call this
|
|
|
21918
22073
|
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
22074
|
model_identifier: external_exports.string().describe("Model name to look up (e.g., text-embedding-3-large, gpt-4o, rerank-2)"),
|
|
21920
22075
|
type: external_exports.enum(["embedder", "llm", "reranker"]).describe("Model type to search").optional()
|
|
21921
|
-
}, async (args) => {
|
|
22076
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
21922
22077
|
const searches = args.type ? [[args.type + "s", args.type]] : [["embedders", "embedder"], ["llms", "llm"], ["rerankers", "reranker"]];
|
|
21923
22078
|
for (const [registryKey, label] of searches) {
|
|
21924
22079
|
const registry2 = MODEL_REGISTRY[registryKey] ?? {};
|
|
@@ -21956,19 +22111,166 @@ server.tool("goodmem_lookup_model", "Look up a model in the registry to see its
|
|
|
21956
22111
|
return { content: [{ type: "text", text: `Model "${args.model_identifier}" not found in ${scope}. Available models:
|
|
21957
22112
|
${allModels.join("\n")}` }] };
|
|
21958
22113
|
});
|
|
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 () => {
|
|
22114
|
+
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
22115
|
return { content: [{ type: "text", text: JSON.stringify({
|
|
21961
|
-
mcp_version: "0.1.
|
|
22116
|
+
mcp_version: "0.1.18",
|
|
21962
22117
|
based_on_goodmem_commit: BASED_ON_GOODMEM_COMMIT
|
|
21963
22118
|
}, null, 2) }] };
|
|
21964
22119
|
});
|
|
21965
22120
|
function registerTools() {
|
|
22121
|
+
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.", {
|
|
22122
|
+
checks: external_exports.array(external_exports.object({
|
|
22123
|
+
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."),
|
|
22124
|
+
target: external_exports.object({
|
|
22125
|
+
kind: external_exports.enum(["INSTANCE", "USER", "SERVICE_IDENTITY", "SPACE", "API_KEY", "EMBEDDER", "RERANKER", "LLM", "MEMORY", "EXTENSION", "RETRIEVE_MEMORY_LOG_POLICY"]).describe("Concrete target kind."),
|
|
22126
|
+
resourceId: external_exports.string().describe("Concrete resource UUID; omitted for the singleton INSTANCE target.").optional()
|
|
22127
|
+
}).passthrough().describe("Target required by the operation: INSTANCE for top-level creates, parent SPACE for CREATE_MEMORY or LIST_MEMORY, otherwise the concrete resource.")
|
|
22128
|
+
}).passthrough()).describe("Concrete checks evaluated in request order.")
|
|
22129
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
22130
|
+
const body = {};
|
|
22131
|
+
if (args.checks !== void 0)
|
|
22132
|
+
body["checks"] = args.checks;
|
|
22133
|
+
const result = await callApi("POST", `/v1/access-policy:check`, body);
|
|
22134
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22135
|
+
});
|
|
22136
|
+
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.", {
|
|
22137
|
+
grant_id: external_exports.string().describe("Optional caller-provided grant UUID.").optional(),
|
|
22138
|
+
audience: external_exports.object({
|
|
22139
|
+
principalId: external_exports.string().describe("Active HUMAN or SERVICE principal UUID.").optional(),
|
|
22140
|
+
allAuthenticated: external_exports.literal(true).describe("Set to true to address every authenticated principal.").optional()
|
|
22141
|
+
}).passthrough().describe("Audience receiving the grant."),
|
|
22142
|
+
rule: external_exports.object({
|
|
22143
|
+
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."),
|
|
22144
|
+
selector: external_exports.enum(["ANY", "OWN", "EXACT", "DIRECT_MEMBERS_OF"]).describe("Resource-selection semantics."),
|
|
22145
|
+
assignedResource: external_exports.object({
|
|
22146
|
+
kind: external_exports.enum(["INSTANCE", "USER", "SERVICE_IDENTITY", "SPACE", "API_KEY", "EMBEDDER", "RERANKER", "LLM", "MEMORY", "EXTENSION", "RETRIEVE_MEMORY_LOG_POLICY"]).describe("Concrete target kind."),
|
|
22147
|
+
resourceId: external_exports.string().describe("Concrete resource UUID; omitted for the singleton INSTANCE target.").optional()
|
|
22148
|
+
}).passthrough().describe("Required exactly for EXACT and DIRECT_MEMBERS_OF selectors.").optional()
|
|
22149
|
+
}).passthrough().describe("Authorization descriptor to grant.")
|
|
22150
|
+
}, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
|
|
22151
|
+
const body = {};
|
|
22152
|
+
if (args.grant_id !== void 0)
|
|
22153
|
+
body["grantId"] = args.grant_id;
|
|
22154
|
+
if (args.audience !== void 0)
|
|
22155
|
+
body["audience"] = args.audience;
|
|
22156
|
+
if (args.rule !== void 0)
|
|
22157
|
+
body["rule"] = args.rule;
|
|
22158
|
+
const result = await callApi("POST", `/v1/access-policy/grants`, body);
|
|
22159
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22160
|
+
});
|
|
22161
|
+
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.", {
|
|
22162
|
+
id: external_exports.string().describe("Grant UUID")
|
|
22163
|
+
}, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
|
|
22164
|
+
if (typeof args.id !== "string" || args.id === "") {
|
|
22165
|
+
throw new Error("'id' must be a non-empty string");
|
|
22166
|
+
}
|
|
22167
|
+
await callApi("DELETE", `/v1/access-policy/grants/${encodeURIComponent(String(args.id))}`, void 0);
|
|
22168
|
+
return { content: [{ type: "text", text: "Deleted successfully." }] };
|
|
22169
|
+
});
|
|
22170
|
+
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.", {
|
|
22171
|
+
id: external_exports.string().describe("Grant UUID"),
|
|
22172
|
+
include_revoked: external_exports.boolean().describe("Include a revoked historical row").optional()
|
|
22173
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
22174
|
+
if (typeof args.id !== "string" || args.id === "") {
|
|
22175
|
+
throw new Error("'id' must be a non-empty string");
|
|
22176
|
+
}
|
|
22177
|
+
const query = {};
|
|
22178
|
+
if (args.include_revoked !== void 0)
|
|
22179
|
+
query["includeRevoked"] = String(args.include_revoked);
|
|
22180
|
+
const result = await callApi("GET", `/v1/access-policy/grants/${encodeURIComponent(String(args.id))}`, void 0, query);
|
|
22181
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22182
|
+
});
|
|
22183
|
+
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.", {
|
|
22184
|
+
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"),
|
|
22185
|
+
resource_id: external_exports.string().describe("Required target UUID except when resource_kind is INSTANCE").optional(),
|
|
22186
|
+
include_revoked: external_exports.boolean().describe("Include revoked history").optional(),
|
|
22187
|
+
max_results: external_exports.number().int().describe("Page size; 0 or omission uses the default of 50, maximum 1,000").optional(),
|
|
22188
|
+
next_token: external_exports.string().describe("Opaque continuation token").optional()
|
|
22189
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
22190
|
+
const query = {};
|
|
22191
|
+
if (args.resource_kind !== void 0)
|
|
22192
|
+
query["resourceKind"] = String(args.resource_kind);
|
|
22193
|
+
if (args.resource_id !== void 0)
|
|
22194
|
+
query["resourceId"] = String(args.resource_id);
|
|
22195
|
+
if (args.include_revoked !== void 0)
|
|
22196
|
+
query["includeRevoked"] = String(args.include_revoked);
|
|
22197
|
+
if (args.max_results !== void 0)
|
|
22198
|
+
query["maxResults"] = String(args.max_results);
|
|
22199
|
+
if (args.next_token !== void 0)
|
|
22200
|
+
query["nextToken"] = String(args.next_token);
|
|
22201
|
+
const result = await callApi("GET", `/v1/access-policy/grants`, void 0, query);
|
|
22202
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22203
|
+
});
|
|
22204
|
+
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.", {
|
|
22205
|
+
role_assignment_id: external_exports.string().describe("Optional caller-provided role-assignment UUID.").optional(),
|
|
22206
|
+
principal_id: external_exports.string().describe("Active principal receiving the role."),
|
|
22207
|
+
role: external_exports.enum(["ADMIN", "USER", "SPACE_VIEWER", "SPACE_CONTRIBUTOR", "SPACE_CONTENT_MANAGER", "SPACE_ADMIN"]).describe("Code-defined non-ROOT role to assign."),
|
|
22208
|
+
assigned_resource: external_exports.object({
|
|
22209
|
+
kind: external_exports.enum(["INSTANCE", "SPACE"]).describe("Role-assignment target kind."),
|
|
22210
|
+
resourceId: external_exports.string().describe("Memory-space UUID; omitted for the singleton INSTANCE target.").optional()
|
|
22211
|
+
}).passthrough().describe("INSTANCE or SPACE boundary receiving the assignment.")
|
|
22212
|
+
}, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
|
|
22213
|
+
const body = {};
|
|
22214
|
+
if (args.role_assignment_id !== void 0)
|
|
22215
|
+
body["roleAssignmentId"] = args.role_assignment_id;
|
|
22216
|
+
if (args.principal_id !== void 0)
|
|
22217
|
+
body["principalId"] = args.principal_id;
|
|
22218
|
+
if (args.role !== void 0)
|
|
22219
|
+
body["role"] = args.role;
|
|
22220
|
+
if (args.assigned_resource !== void 0)
|
|
22221
|
+
body["assignedResource"] = args.assigned_resource;
|
|
22222
|
+
const result = await callApi("POST", `/v1/access-policy/role-assignments`, body);
|
|
22223
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22224
|
+
});
|
|
22225
|
+
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.", {
|
|
22226
|
+
id: external_exports.string().describe("Role-assignment UUID")
|
|
22227
|
+
}, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
|
|
22228
|
+
if (typeof args.id !== "string" || args.id === "") {
|
|
22229
|
+
throw new Error("'id' must be a non-empty string");
|
|
22230
|
+
}
|
|
22231
|
+
await callApi("DELETE", `/v1/access-policy/role-assignments/${encodeURIComponent(String(args.id))}`, void 0);
|
|
22232
|
+
return { content: [{ type: "text", text: "Deleted successfully." }] };
|
|
22233
|
+
});
|
|
22234
|
+
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.", {
|
|
22235
|
+
id: external_exports.string().describe("Role-assignment UUID"),
|
|
22236
|
+
include_revoked: external_exports.boolean().describe("Include a revoked historical row").optional()
|
|
22237
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
22238
|
+
if (typeof args.id !== "string" || args.id === "") {
|
|
22239
|
+
throw new Error("'id' must be a non-empty string");
|
|
22240
|
+
}
|
|
22241
|
+
const query = {};
|
|
22242
|
+
if (args.include_revoked !== void 0)
|
|
22243
|
+
query["includeRevoked"] = String(args.include_revoked);
|
|
22244
|
+
const result = await callApi("GET", `/v1/access-policy/role-assignments/${encodeURIComponent(String(args.id))}`, void 0, query);
|
|
22245
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22246
|
+
});
|
|
22247
|
+
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.", {
|
|
22248
|
+
resource_kind: external_exports.enum(["INSTANCE", "SPACE"]).describe("Required INSTANCE or SPACE kind"),
|
|
22249
|
+
resource_id: external_exports.string().describe("Required space UUID; omitted for INSTANCE").optional(),
|
|
22250
|
+
include_revoked: external_exports.boolean().describe("Include revoked history").optional(),
|
|
22251
|
+
max_results: external_exports.number().int().describe("Page size; 0 or omission uses the default of 50, maximum 1,000").optional(),
|
|
22252
|
+
next_token: external_exports.string().describe("Opaque continuation token").optional()
|
|
22253
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
22254
|
+
const query = {};
|
|
22255
|
+
if (args.resource_kind !== void 0)
|
|
22256
|
+
query["resourceKind"] = String(args.resource_kind);
|
|
22257
|
+
if (args.resource_id !== void 0)
|
|
22258
|
+
query["resourceId"] = String(args.resource_id);
|
|
22259
|
+
if (args.include_revoked !== void 0)
|
|
22260
|
+
query["includeRevoked"] = String(args.include_revoked);
|
|
22261
|
+
if (args.max_results !== void 0)
|
|
22262
|
+
query["maxResults"] = String(args.max_results);
|
|
22263
|
+
if (args.next_token !== void 0)
|
|
22264
|
+
query["nextToken"] = String(args.next_token);
|
|
22265
|
+
const result = await callApi("GET", `/v1/access-policy/role-assignments`, void 0, query);
|
|
22266
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22267
|
+
});
|
|
21966
22268
|
server.tool("goodmem_admin_background_jobs_purge", "Purge completed background jobs\n\nDeletes terminal background jobs older than a retention cutoff.", {
|
|
21967
22269
|
older_than: external_exports.string().describe("ISO-8601 timestamp cutoff; only terminal jobs older than this instant are eligible."),
|
|
21968
22270
|
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
22271
|
dry_run: external_exports.boolean().describe("If true, report purge counts without deleting any rows.").optional(),
|
|
21970
22272
|
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) => {
|
|
22273
|
+
}, { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false }, async (args) => {
|
|
21972
22274
|
const body = {};
|
|
21973
22275
|
if (args.older_than !== void 0)
|
|
21974
22276
|
body["olderThan"] = args.older_than;
|
|
@@ -21985,7 +22287,7 @@ function registerTools() {
|
|
|
21985
22287
|
timeout_sec: external_exports.number().int().describe("Maximum seconds to wait for the server to quiesce before returning.").optional(),
|
|
21986
22288
|
reason: external_exports.string().describe("Human-readable reason for initiating drain mode.").optional(),
|
|
21987
22289
|
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) => {
|
|
22290
|
+
}, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
|
|
21989
22291
|
const body = {};
|
|
21990
22292
|
if (args.timeout_sec !== void 0)
|
|
21991
22293
|
body["timeoutSec"] = args.timeout_sec;
|
|
@@ -21996,7 +22298,7 @@ function registerTools() {
|
|
|
21996
22298
|
const result = await callApi("POST", `/v1/admin:drain`, body, void 0, ((args.timeout_sec ?? 900) + 30) * 1e3);
|
|
21997
22299
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
21998
22300
|
});
|
|
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) => {
|
|
22301
|
+
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
22302
|
const result = await callApi("POST", `/v1/admin/license:reload`, void 0);
|
|
22001
22303
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22002
22304
|
});
|
|
@@ -22017,7 +22319,7 @@ function registerTools() {
|
|
|
22017
22319
|
active_from: external_exports.number().int().describe("Inclusive activation time in milliseconds since epoch.").optional(),
|
|
22018
22320
|
active_until: external_exports.number().int().describe("Exclusive deactivation time in milliseconds since epoch.").optional(),
|
|
22019
22321
|
labels: external_exports.record(external_exports.string(), external_exports.string()).describe("Operator labels for listing and administration.").optional()
|
|
22020
|
-
}, async (args) => {
|
|
22322
|
+
}, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
|
|
22021
22323
|
const body = {};
|
|
22022
22324
|
if (args.policy_id !== void 0)
|
|
22023
22325
|
body["policyId"] = args.policy_id;
|
|
@@ -22039,7 +22341,7 @@ function registerTools() {
|
|
|
22039
22341
|
server.tool("goodmem_admin_retrieve_memory_log_policies_delete", "Delete a RetrieveMemory log policy\n\nIdempotently tombstones an immutable RetrieveMemory log policy.", {
|
|
22040
22342
|
id: external_exports.string().describe("The UUID of the policy to delete"),
|
|
22041
22343
|
reason: external_exports.string().describe("Optional tombstone reason.").optional()
|
|
22042
|
-
}, async (args) => {
|
|
22344
|
+
}, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
|
|
22043
22345
|
if (typeof args.id !== "string" || args.id === "") {
|
|
22044
22346
|
throw new Error("'id' must be a non-empty string");
|
|
22045
22347
|
}
|
|
@@ -22052,7 +22354,7 @@ function registerTools() {
|
|
|
22052
22354
|
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
22355
|
id: external_exports.string().describe("The UUID of the policy to retrieve"),
|
|
22054
22356
|
include_deleted: external_exports.boolean().describe("Whether to include tombstoned policies. Also accepts include_deleted.").optional()
|
|
22055
|
-
}, async (args) => {
|
|
22357
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
22056
22358
|
if (typeof args.id !== "string" || args.id === "") {
|
|
22057
22359
|
throw new Error("'id' must be a non-empty string");
|
|
22058
22360
|
}
|
|
@@ -22071,7 +22373,7 @@ function registerTools() {
|
|
|
22071
22373
|
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
22374
|
sort_order: external_exports.enum(["ASCENDING", "DESCENDING", "SORT_ORDER_UNSPECIFIED"]).describe("Sort order. Also accepts sort_order.").optional(),
|
|
22073
22375
|
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) => {
|
|
22376
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
22075
22377
|
const query = {};
|
|
22076
22378
|
if (args.include_deleted !== void 0)
|
|
22077
22379
|
query["includeDeleted"] = String(args.include_deleted);
|
|
@@ -22094,11 +22396,31 @@ function registerTools() {
|
|
|
22094
22396
|
const result = await callApi("GET", `/v1/admin/retrieve-memory-log-policies`, void 0, query);
|
|
22095
22397
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22096
22398
|
});
|
|
22097
|
-
server.tool("
|
|
22098
|
-
|
|
22099
|
-
|
|
22100
|
-
|
|
22101
|
-
|
|
22399
|
+
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.", {
|
|
22400
|
+
new_owner_id: external_exports.string().describe("Existing principal UUID that will become the new owner.")
|
|
22401
|
+
}, { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false }, async (args) => {
|
|
22402
|
+
const body = {};
|
|
22403
|
+
if (args.new_owner_id !== void 0)
|
|
22404
|
+
body["newOwnerId"] = args.new_owner_id;
|
|
22405
|
+
const result = await callApi("POST", `/v1/admin:transferInstanceOwnership`, body);
|
|
22406
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22407
|
+
});
|
|
22408
|
+
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.", {
|
|
22409
|
+
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(),
|
|
22410
|
+
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(),
|
|
22411
|
+
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(),
|
|
22412
|
+
subject_principal_id: external_exports.string().describe("Principal authenticated by this key. Omit to use the authenticated principal.").optional(),
|
|
22413
|
+
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(),
|
|
22414
|
+
ceiling: external_exports.array(external_exports.object({
|
|
22415
|
+
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."),
|
|
22416
|
+
selector: external_exports.enum(["ANY", "OWN", "EXACT", "DIRECT_MEMBERS_OF"]).describe("Resource-selection semantics."),
|
|
22417
|
+
assignedResource: external_exports.object({
|
|
22418
|
+
kind: external_exports.enum(["INSTANCE", "USER", "SERVICE_IDENTITY", "SPACE", "API_KEY", "EMBEDDER", "RERANKER", "LLM", "MEMORY", "EXTENSION", "RETRIEVE_MEMORY_LOG_POLICY"]).describe("Concrete target kind."),
|
|
22419
|
+
resourceId: external_exports.string().describe("Concrete resource UUID; omitted for the singleton INSTANCE target.").optional()
|
|
22420
|
+
}).passthrough().describe("Required exactly for EXACT and DIRECT_MEMBERS_OF selectors.").optional()
|
|
22421
|
+
}).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(),
|
|
22422
|
+
valid_from: external_exports.number().int().describe("Inclusive activation time in epoch milliseconds. Omit to activate at issuance time.").optional()
|
|
22423
|
+
}, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
|
|
22102
22424
|
const body = {};
|
|
22103
22425
|
if (args.labels !== void 0)
|
|
22104
22426
|
body["labels"] = args.labels;
|
|
@@ -22106,28 +22428,65 @@ function registerTools() {
|
|
|
22106
22428
|
body["expiresAt"] = args.expires_at;
|
|
22107
22429
|
if (args.api_key_id !== void 0)
|
|
22108
22430
|
body["apiKeyId"] = args.api_key_id;
|
|
22431
|
+
if (args.subject_principal_id !== void 0)
|
|
22432
|
+
body["subjectPrincipalId"] = args.subject_principal_id;
|
|
22433
|
+
if (args.authority_mode !== void 0)
|
|
22434
|
+
body["authorityMode"] = args.authority_mode;
|
|
22435
|
+
if (args.ceiling !== void 0)
|
|
22436
|
+
body["ceiling"] = args.ceiling;
|
|
22437
|
+
if (args.valid_from !== void 0)
|
|
22438
|
+
body["validFrom"] = args.valid_from;
|
|
22109
22439
|
const result = await callApi("POST", `/v1/apikeys`, body);
|
|
22110
22440
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22111
22441
|
});
|
|
22112
|
-
server.tool("goodmem_apikeys_delete", 'Delete an API key\n\nPermanently
|
|
22442
|
+
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
22443
|
id: external_exports.string().describe("The UUID of the API key to delete")
|
|
22114
|
-
}, async (args) => {
|
|
22444
|
+
}, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
|
|
22115
22445
|
if (typeof args.id !== "string" || args.id === "") {
|
|
22116
22446
|
throw new Error("'id' must be a non-empty string");
|
|
22117
22447
|
}
|
|
22118
22448
|
await callApi("DELETE", `/v1/apikeys/${encodeURIComponent(String(args.id))}`, void 0);
|
|
22119
22449
|
return { content: [{ type: "text", text: "Deleted successfully." }] };
|
|
22120
22450
|
});
|
|
22121
|
-
server.tool("
|
|
22122
|
-
|
|
22451
|
+
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.", {
|
|
22452
|
+
id: external_exports.string().describe("API-key UUID")
|
|
22453
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
22454
|
+
if (typeof args.id !== "string" || args.id === "") {
|
|
22455
|
+
throw new Error("'id' must be a non-empty string");
|
|
22456
|
+
}
|
|
22457
|
+
const result = await callApi("GET", `/v1/apikeys/${encodeURIComponent(String(args.id))}`, void 0);
|
|
22458
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22459
|
+
});
|
|
22460
|
+
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.", {
|
|
22461
|
+
subject_principal_id: external_exports.string().describe("Filter by exact subject-principal UUID").optional(),
|
|
22462
|
+
owner_principal_id: external_exports.string().describe("Filter by exact administrative-owner UUID").optional(),
|
|
22463
|
+
lifecycle_state: external_exports.enum(["NOT_YET_VALID", "USABLE", "EXPIRED", "REVOKED"]).describe("Filter by precise lifecycle state").optional(),
|
|
22464
|
+
view: external_exports.enum(["FULL", "BASIC"]).describe("Metadata projection; omission defaults to FULL").optional(),
|
|
22465
|
+
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(),
|
|
22466
|
+
next_token: external_exports.string().describe("Opaque continuation token returned by the preceding page").optional()
|
|
22467
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
22468
|
+
const query = {};
|
|
22469
|
+
if (args.subject_principal_id !== void 0)
|
|
22470
|
+
query["subjectPrincipalId"] = String(args.subject_principal_id);
|
|
22471
|
+
if (args.owner_principal_id !== void 0)
|
|
22472
|
+
query["ownerPrincipalId"] = String(args.owner_principal_id);
|
|
22473
|
+
if (args.lifecycle_state !== void 0)
|
|
22474
|
+
query["lifecycleState"] = String(args.lifecycle_state);
|
|
22475
|
+
if (args.view !== void 0)
|
|
22476
|
+
query["view"] = String(args.view);
|
|
22477
|
+
if (args.max_results !== void 0)
|
|
22478
|
+
query["maxResults"] = String(args.max_results);
|
|
22479
|
+
if (args.next_token !== void 0)
|
|
22480
|
+
query["nextToken"] = String(args.next_token);
|
|
22481
|
+
const result = await callApi("GET", `/v1/apikeys`, void 0, query);
|
|
22123
22482
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22124
22483
|
});
|
|
22125
|
-
server.tool("goodmem_apikeys_update", "Update an API key\n\nUpdates an existing API key
|
|
22484
|
+
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
22485
|
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.
|
|
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) => {
|
|
22486
|
+
status: external_exports.enum(["ACTIVE", "INACTIVE"]).describe("New status for the API key. INACTIVE is permanent; revoked keys cannot be reactivated.").optional(),
|
|
22487
|
+
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(),
|
|
22488
|
+
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()
|
|
22489
|
+
}, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
|
|
22131
22490
|
if (typeof args.id !== "string" || args.id === "") {
|
|
22132
22491
|
throw new Error("'id' must be a non-empty string");
|
|
22133
22492
|
}
|
|
@@ -22145,12 +22504,12 @@ function registerTools() {
|
|
|
22145
22504
|
const result = await callApi("PUT", `/v1/apikeys/${encodeURIComponent(String(args.id))}`, body);
|
|
22146
22505
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22147
22506
|
});
|
|
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.
|
|
22507
|
+
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. A SHA-256 credential fingerprint participates in uniqueness checking. The api_path field defaults to '/v2/embed' for Cohere, '/v1/embeddings' for Jina, '/embed' for TEI, and '/embeddings' for other providers when omitted. OWNER DEFAULTS: Owner defaults to the authenticated principal unless owner_id is provided; CREATE_EMBEDDER is evaluated against the proposed embedder and owner.", {
|
|
22149
22508
|
display_name: external_exports.string().describe("User-facing name of the embedder"),
|
|
22150
22509
|
description: external_exports.string().describe("Description of the embedder").optional(),
|
|
22151
22510
|
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
22511
|
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
|
|
22512
|
+
api_path: external_exports.string().describe("API path for embeddings requests (defaults: Cohere /v2/embed, Jina /v1/embeddings, TEI /embed, others /embeddings)").optional(),
|
|
22154
22513
|
model_identifier: external_exports.string().describe("Model identifier"),
|
|
22155
22514
|
dimensionality: external_exports.number().int().describe("Output vector dimensions (auto-inferred from model_identifier if omitted)").optional(),
|
|
22156
22515
|
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 +22535,9 @@ function registerTools() {
|
|
|
22176
22535
|
labels: external_exports.record(external_exports.string(), external_exports.string()).describe("User-defined labels for categorization").optional(),
|
|
22177
22536
|
version: external_exports.string().describe("Version information").optional(),
|
|
22178
22537
|
monitoring_endpoint: external_exports.string().describe("Monitoring endpoint URL").optional(),
|
|
22179
|
-
owner_id: external_exports.string().describe("Optional owner
|
|
22538
|
+
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(),
|
|
22180
22539
|
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) => {
|
|
22540
|
+
}, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
|
|
22182
22541
|
const inferred = autoInferFromRegistry("embedders", args);
|
|
22183
22542
|
checkCredentialRequired(inferred);
|
|
22184
22543
|
const body = {};
|
|
@@ -22217,29 +22576,33 @@ function registerTools() {
|
|
|
22217
22576
|
const result = await callApi("POST", `/v1/embedders`, body);
|
|
22218
22577
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22219
22578
|
});
|
|
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
|
|
22579
|
+
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
22580
|
id: external_exports.string().describe("The unique identifier of the embedder to delete")
|
|
22222
|
-
}, async (args) => {
|
|
22581
|
+
}, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
|
|
22223
22582
|
if (typeof args.id !== "string" || args.id === "") {
|
|
22224
22583
|
throw new Error("'id' must be a non-empty string");
|
|
22225
22584
|
}
|
|
22226
22585
|
await callApi("DELETE", `/v1/embedders/${encodeURIComponent(String(args.id))}`, void 0);
|
|
22227
22586
|
return { content: [{ type: "text", text: "Deleted successfully." }] };
|
|
22228
22587
|
});
|
|
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
|
|
22230
|
-
id: external_exports.string().describe("The unique identifier of the embedder to retrieve")
|
|
22231
|
-
|
|
22588
|
+
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.", {
|
|
22589
|
+
id: external_exports.string().describe("The unique identifier of the embedder to retrieve"),
|
|
22590
|
+
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()
|
|
22591
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
22232
22592
|
if (typeof args.id !== "string" || args.id === "") {
|
|
22233
22593
|
throw new Error("'id' must be a non-empty string");
|
|
22234
22594
|
}
|
|
22235
|
-
const
|
|
22595
|
+
const query = {};
|
|
22596
|
+
if (args.include_credentials !== void 0)
|
|
22597
|
+
query["includeCredentials"] = String(args.include_credentials);
|
|
22598
|
+
const result = await callApi("GET", `/v1/embedders/${encodeURIComponent(String(args.id))}`, void 0, query);
|
|
22236
22599
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22237
22600
|
});
|
|
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).
|
|
22239
|
-
owner_id: external_exports.string().describe("Filter
|
|
22601
|
+
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.", {
|
|
22602
|
+
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(),
|
|
22240
22603
|
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(),
|
|
22241
22604
|
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) => {
|
|
22605
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
22243
22606
|
const query = {};
|
|
22244
22607
|
if (args.owner_id !== void 0)
|
|
22245
22608
|
query["ownerId"] = String(args.owner_id);
|
|
@@ -22252,12 +22615,12 @@ function registerTools() {
|
|
|
22252
22615
|
const result = await callApi("GET", `/v1/embedders`, void 0, query);
|
|
22253
22616
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22254
22617
|
});
|
|
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
|
|
22618
|
+
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. Omitting api_path preserves the existing path; setting it blank resets it to '/v2/embed' for Cohere, '/v1/embeddings' for Jina, '/embed' for TEI, or '/embeddings' for other providers. 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 on the requested embedder.", {
|
|
22256
22619
|
id: external_exports.string().describe("The unique identifier of the embedder to update"),
|
|
22257
22620
|
display_name: external_exports.string().describe("User-facing name of the embedder").optional(),
|
|
22258
22621
|
description: external_exports.string().describe("Description of the embedder").optional(),
|
|
22259
22622
|
endpoint_url: external_exports.string().describe("API endpoint URL").optional(),
|
|
22260
|
-
api_path: external_exports.string().describe("API path for embeddings
|
|
22623
|
+
api_path: external_exports.string().describe("API path for embeddings requests. Omit to keep the existing path; set blank to reset to the provider default (Cohere /v2/embed, Jina /v1/embeddings, TEI /embed, others /embeddings).").optional(),
|
|
22261
22624
|
model_identifier: external_exports.string().describe("Model identifier").optional(),
|
|
22262
22625
|
dimensionality: external_exports.number().int().describe("Output vector dimensions").optional(),
|
|
22263
22626
|
distribution_type: external_exports.enum(["DENSE", "SPARSE"]).describe("Type of embedding distribution (DENSE or SPARSE)").optional(),
|
|
@@ -22278,12 +22641,12 @@ function registerTools() {
|
|
|
22278
22641
|
quotaProjectId: external_exports.string().describe("Optional quota project used for billing").optional()
|
|
22279
22642
|
}).passthrough().describe("Configuration when kind is CREDENTIAL_KIND_GCP_ADC").optional(),
|
|
22280
22643
|
labels: external_exports.record(external_exports.string(), external_exports.string()).describe('Optional annotations to aid operators (e.g., "owner=vertex")').optional()
|
|
22281
|
-
}).passthrough().describe("
|
|
22644
|
+
}).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
22645
|
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
22646
|
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
22647
|
version: external_exports.string().describe("Version information").optional(),
|
|
22285
22648
|
monitoring_endpoint: external_exports.string().describe("Monitoring endpoint URL").optional()
|
|
22286
|
-
}, async (args) => {
|
|
22649
|
+
}, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
|
|
22287
22650
|
if (typeof args.id !== "string" || args.id === "") {
|
|
22288
22651
|
throw new Error("'id' must be a non-empty string");
|
|
22289
22652
|
}
|
|
@@ -22321,7 +22684,11 @@ function registerTools() {
|
|
|
22321
22684
|
const result = await callApi("PUT", `/v1/embedders/${encodeURIComponent(String(args.id))}`, body);
|
|
22322
22685
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22323
22686
|
});
|
|
22324
|
-
server.tool("
|
|
22687
|
+
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) => {
|
|
22688
|
+
const result = await callApi("GET", `/v1/instance`, void 0);
|
|
22689
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22690
|
+
});
|
|
22691
|
+
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. OWNER DEFAULTS: Owner defaults to the authenticated principal unless owner_id is provided; CREATE_LLM is evaluated against the proposed LLM and owner.", {
|
|
22325
22692
|
display_name: external_exports.string().describe("User-facing name of the LLM"),
|
|
22326
22693
|
description: external_exports.string().describe("Description of the LLM").optional(),
|
|
22327
22694
|
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(),
|
|
@@ -22368,9 +22735,9 @@ function registerTools() {
|
|
|
22368
22735
|
}).passthrough().describe("Default sampling parameters for generation requests").optional(),
|
|
22369
22736
|
max_context_length: external_exports.number().int().describe("Maximum context window size in tokens").optional(),
|
|
22370
22737
|
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
|
|
22738
|
+
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(),
|
|
22372
22739
|
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) => {
|
|
22740
|
+
}, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
|
|
22374
22741
|
const inferred = autoInferFromRegistry("llms", args);
|
|
22375
22742
|
checkCredentialRequired(inferred);
|
|
22376
22743
|
const body = {};
|
|
@@ -22411,29 +22778,33 @@ function registerTools() {
|
|
|
22411
22778
|
const result = await callApi("POST", `/v1/llms`, body);
|
|
22412
22779
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22413
22780
|
});
|
|
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
|
|
22781
|
+
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
22782
|
id: external_exports.string().describe("The unique identifier of the LLM to delete")
|
|
22416
|
-
}, async (args) => {
|
|
22783
|
+
}, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
|
|
22417
22784
|
if (typeof args.id !== "string" || args.id === "") {
|
|
22418
22785
|
throw new Error("'id' must be a non-empty string");
|
|
22419
22786
|
}
|
|
22420
22787
|
await callApi("DELETE", `/v1/llms/${encodeURIComponent(String(args.id))}`, void 0);
|
|
22421
22788
|
return { content: [{ type: "text", text: "Deleted successfully." }] };
|
|
22422
22789
|
});
|
|
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
|
|
22424
|
-
id: external_exports.string().describe("The unique identifier of the LLM to retrieve")
|
|
22425
|
-
|
|
22790
|
+
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.", {
|
|
22791
|
+
id: external_exports.string().describe("The unique identifier of the LLM to retrieve"),
|
|
22792
|
+
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()
|
|
22793
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
22426
22794
|
if (typeof args.id !== "string" || args.id === "") {
|
|
22427
22795
|
throw new Error("'id' must be a non-empty string");
|
|
22428
22796
|
}
|
|
22429
|
-
const
|
|
22797
|
+
const query = {};
|
|
22798
|
+
if (args.include_credentials !== void 0)
|
|
22799
|
+
query["includeCredentials"] = String(args.include_credentials);
|
|
22800
|
+
const result = await callApi("GET", `/v1/llms/${encodeURIComponent(String(args.id))}`, void 0, query);
|
|
22430
22801
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22431
22802
|
});
|
|
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).
|
|
22433
|
-
owner_id: external_exports.string().describe("Filter
|
|
22803
|
+
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.", {
|
|
22804
|
+
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(),
|
|
22434
22805
|
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(),
|
|
22435
22806
|
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) => {
|
|
22807
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
22437
22808
|
const query = {};
|
|
22438
22809
|
if (args.owner_id !== void 0)
|
|
22439
22810
|
query["ownerId"] = String(args.owner_id);
|
|
@@ -22446,7 +22817,7 @@ function registerTools() {
|
|
|
22446
22817
|
const result = await callApi("GET", `/v1/llms`, void 0, query);
|
|
22447
22818
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22448
22819
|
});
|
|
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
|
|
22820
|
+
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
22821
|
id: external_exports.string().describe("The unique identifier of the LLM to update"),
|
|
22451
22822
|
display_name: external_exports.string().describe("Update display name").optional(),
|
|
22452
22823
|
description: external_exports.string().describe("Update description").optional(),
|
|
@@ -22470,7 +22841,7 @@ function registerTools() {
|
|
|
22470
22841
|
quotaProjectId: external_exports.string().describe("Optional quota project used for billing").optional()
|
|
22471
22842
|
}).passthrough().describe("Configuration when kind is CREDENTIAL_KIND_GCP_ADC").optional(),
|
|
22472
22843
|
labels: external_exports.record(external_exports.string(), external_exports.string()).describe('Optional annotations to aid operators (e.g., "owner=vertex")').optional()
|
|
22473
|
-
}).passthrough().describe("
|
|
22844
|
+
}).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
22845
|
version: external_exports.string().describe("Update version information").optional(),
|
|
22475
22846
|
monitoring_endpoint: external_exports.string().describe("Update monitoring endpoint URL").optional(),
|
|
22476
22847
|
capabilities: external_exports.object({
|
|
@@ -22494,7 +22865,7 @@ function registerTools() {
|
|
|
22494
22865
|
client_config: external_exports.record(external_exports.string(), external_exports.any()).describe("Update provider-specific client configuration (replaces entire config; no merging)").optional(),
|
|
22495
22866
|
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
22867
|
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) => {
|
|
22868
|
+
}, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
|
|
22498
22869
|
if (typeof args.id !== "string" || args.id === "") {
|
|
22499
22870
|
throw new Error("'id' must be a non-empty string");
|
|
22500
22871
|
}
|
|
@@ -22564,7 +22935,7 @@ function registerTools() {
|
|
|
22564
22935
|
}).passthrough().describe("Chunking strategy for this memory (if not provided, uses space default)").optional(),
|
|
22565
22936
|
extractPageImages: external_exports.boolean().describe("Optional hint to extract page images for eligible document types (for example, PDFs)").optional()
|
|
22566
22937
|
}).passthrough()).describe("Array of memory creation requests.")
|
|
22567
|
-
}, async (args) => {
|
|
22938
|
+
}, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
|
|
22568
22939
|
for (const [_i, _item] of (Array.isArray(args.requests) ? args.requests : []).entries()) {
|
|
22569
22940
|
const _itemSet = [_item?.["originalContent"] !== void 0 && _item?.["originalContent"] !== null, _item?.["originalContentB64"] !== void 0 && _item?.["originalContentB64"] !== null].filter(Boolean).length;
|
|
22570
22941
|
if (_itemSet !== 1) {
|
|
@@ -22588,7 +22959,7 @@ function registerTools() {
|
|
|
22588
22959
|
const result = await callApi("POST", `/v1/memories:batchCreate`, body);
|
|
22589
22960
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22590
22961
|
});
|
|
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.", {
|
|
22962
|
+
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
22963
|
requests: external_exports.array(external_exports.object({
|
|
22593
22964
|
memoryId: external_exports.string().describe("Deletes one specific memory by UUID").optional(),
|
|
22594
22965
|
filterSelector: external_exports.object({
|
|
@@ -22597,18 +22968,18 @@ function registerTools() {
|
|
|
22597
22968
|
filter: external_exports.string().describe("Optional metadata filter expression").optional()
|
|
22598
22969
|
}).passthrough().describe("Deletes a filtered set of memories within a specific space").optional()
|
|
22599
22970
|
}).passthrough()).describe("Array of delete selectors")
|
|
22600
|
-
}, async (args) => {
|
|
22971
|
+
}, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
|
|
22601
22972
|
const body = {};
|
|
22602
22973
|
if (args.requests !== void 0)
|
|
22603
22974
|
body["requests"] = args.requests;
|
|
22604
22975
|
const result = await callApi("POST", `/v1/memories:batchDelete`, body);
|
|
22605
22976
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22606
22977
|
});
|
|
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.", {
|
|
22978
|
+
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
22979
|
memory_ids: external_exports.array(external_exports.string()).describe("Array of memory IDs to retrieve"),
|
|
22609
22980
|
include_content: external_exports.boolean().describe("Whether to include the original content in the response").optional(),
|
|
22610
22981
|
include_processing_history: external_exports.boolean().describe("Whether to include background job processing history for each memory").optional()
|
|
22611
|
-
}, async (args) => {
|
|
22982
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
22612
22983
|
const body = {};
|
|
22613
22984
|
if (args.memory_ids !== void 0)
|
|
22614
22985
|
body["memoryIds"] = args.memory_ids;
|
|
@@ -22619,10 +22990,10 @@ function registerTools() {
|
|
|
22619
22990
|
const result = await callApi("POST", `/v1/memories:batchGet`, body);
|
|
22620
22991
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22621
22992
|
});
|
|
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.", {
|
|
22993
|
+
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
22994
|
id: external_exports.string().describe("The UUID of the memory to download"),
|
|
22624
22995
|
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) => {
|
|
22996
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
22626
22997
|
if (typeof args.id !== "string" || args.id === "") {
|
|
22627
22998
|
throw new Error("'id' must be a non-empty string");
|
|
22628
22999
|
}
|
|
@@ -22668,7 +23039,7 @@ function registerTools() {
|
|
|
22668
23039
|
}).passthrough().describe("Sentence-based chunking strategy with language detection").optional()
|
|
22669
23040
|
}).passthrough().describe("Chunking strategy for this memory (if not provided, uses space default)").optional(),
|
|
22670
23041
|
extract_page_images: external_exports.boolean().describe("Optional hint to extract page images for eligible document types (for example, PDFs)").optional()
|
|
22671
|
-
}, async (args) => {
|
|
23042
|
+
}, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
|
|
22672
23043
|
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
23044
|
if (_set !== 1) {
|
|
22674
23045
|
throw new Error("exactly one of original_content or original_content_b64 must be set");
|
|
@@ -22701,20 +23072,20 @@ function registerTools() {
|
|
|
22701
23072
|
const result = await callApi("POST", `/v1/memories`, body);
|
|
22702
23073
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22703
23074
|
});
|
|
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
|
|
23075
|
+
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
23076
|
id: external_exports.string().describe("The UUID of the memory to delete")
|
|
22706
|
-
}, async (args) => {
|
|
23077
|
+
}, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
|
|
22707
23078
|
if (typeof args.id !== "string" || args.id === "") {
|
|
22708
23079
|
throw new Error("'id' must be a non-empty string");
|
|
22709
23080
|
}
|
|
22710
23081
|
await callApi("DELETE", `/v1/memories/${encodeURIComponent(String(args.id))}`, void 0);
|
|
22711
23082
|
return { content: [{ type: "text", text: "Deleted successfully." }] };
|
|
22712
23083
|
});
|
|
22713
|
-
server.tool("goodmem_memories_get", "Get a memory by ID\n\nRetrieves a single memory by its ID.
|
|
23084
|
+
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
23085
|
id: external_exports.string().describe("The UUID of the memory to retrieve"),
|
|
22715
23086
|
include_content: external_exports.boolean().describe("Whether to include the original content in the response (defaults to false).").optional(),
|
|
22716
23087
|
include_processing_history: external_exports.boolean().describe("Whether to include background job processing history in the response (defaults to false).").optional()
|
|
22717
|
-
}, async (args) => {
|
|
23088
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
22718
23089
|
if (typeof args.id !== "string" || args.id === "") {
|
|
22719
23090
|
throw new Error("'id' must be a non-empty string");
|
|
22720
23091
|
}
|
|
@@ -22736,7 +23107,7 @@ function registerTools() {
|
|
|
22736
23107
|
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
23108
|
sort_by: external_exports.enum(["created_at", "updated_at", "content_type", "processing_status"]).describe("Field to sort by (e.g., 'created_at').").optional(),
|
|
22738
23109
|
sort_order: external_exports.enum(["ASCENDING", "DESCENDING", "SORT_ORDER_UNSPECIFIED"]).describe("Sort direction (ASCENDING or DESCENDING).").optional()
|
|
22739
|
-
}, async (args) => {
|
|
23110
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
22740
23111
|
if (typeof args.space_id !== "string" || args.space_id === "") {
|
|
22741
23112
|
throw new Error("'space_id' must be a non-empty string");
|
|
22742
23113
|
}
|
|
@@ -22760,7 +23131,7 @@ function registerTools() {
|
|
|
22760
23131
|
const result = await callApi("GET", `/v1/spaces/${encodeURIComponent(String(args.space_id))}/memories`, void 0, query);
|
|
22761
23132
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22762
23133
|
});
|
|
22763
|
-
server.tool("goodmem_memories_pages", "List memory page images\n\nLists extracted page-image metadata for a memory with optional filters and pagination.", {
|
|
23134
|
+
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
23135
|
id: external_exports.string().describe("Memory UUID"),
|
|
22765
23136
|
start_page_index: external_exports.number().int().describe("Optional lower bound for returned page indices, inclusive.").optional(),
|
|
22766
23137
|
end_page_index: external_exports.number().int().describe("Optional upper bound for returned page indices, inclusive.").optional(),
|
|
@@ -22768,7 +23139,7 @@ function registerTools() {
|
|
|
22768
23139
|
content_type: external_exports.string().describe("Optional rendition filter for page-image MIME type, such as image/png.").optional(),
|
|
22769
23140
|
max_results: external_exports.number().int().describe("Maximum number of results per page.").optional(),
|
|
22770
23141
|
next_token: external_exports.string().describe("Opaque pagination token for the next page. Do not parse or construct it.").optional()
|
|
22771
|
-
}, async (args) => {
|
|
23142
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
22772
23143
|
if (typeof args.id !== "string" || args.id === "") {
|
|
22773
23144
|
throw new Error("'id' must be a non-empty string");
|
|
22774
23145
|
}
|
|
@@ -22788,13 +23159,13 @@ function registerTools() {
|
|
|
22788
23159
|
const result = await callApi("GET", `/v1/memories/${encodeURIComponent(String(args.id))}/pages`, void 0, query);
|
|
22789
23160
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22790
23161
|
});
|
|
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.", {
|
|
23162
|
+
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
23163
|
id: external_exports.string().describe("Memory UUID"),
|
|
22793
23164
|
page_index: external_exports.number().int().describe("0-based page index"),
|
|
22794
23165
|
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
23166
|
content_type: external_exports.string().describe("Optional rendition filter. MIME type of the desired page image, such as image/png.").optional(),
|
|
22796
23167
|
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) => {
|
|
23168
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
22798
23169
|
if (typeof args.id !== "string" || args.id === "") {
|
|
22799
23170
|
throw new Error("'id' must be a non-empty string");
|
|
22800
23171
|
}
|
|
@@ -22855,12 +23226,8 @@ function registerTools() {
|
|
|
22855
23226
|
name: external_exports.string().describe("Fully qualified factory class name of the post-processor to apply."),
|
|
22856
23227
|
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
23228
|
}).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
23229
|
timeout_sec: external_exports.number().int().describe("Maximum seconds to wait for the streaming response (default: 200).").optional()
|
|
22863
|
-
}, async (args) => {
|
|
23230
|
+
}, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
|
|
22864
23231
|
const body = {};
|
|
22865
23232
|
if (args.message !== void 0)
|
|
22866
23233
|
body["message"] = args.message;
|
|
@@ -22880,8 +23247,6 @@ function registerTools() {
|
|
|
22880
23247
|
body["hnsw"] = args.hnsw;
|
|
22881
23248
|
if (args.post_processor !== void 0)
|
|
22882
23249
|
body["postProcessor"] = args.post_processor;
|
|
22883
|
-
if (args.logging !== void 0)
|
|
22884
|
-
body["logging"] = args.logging;
|
|
22885
23250
|
const result = await callApiNdjson("POST", `/v1/memories:retrieve`, body, void 0, args.timeout_sec);
|
|
22886
23251
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22887
23252
|
});
|
|
@@ -22892,7 +23257,7 @@ function registerTools() {
|
|
|
22892
23257
|
include_markdown: external_exports.boolean().describe("Include markdown rendering in the response").optional(),
|
|
22893
23258
|
start_page: external_exports.number().int().describe("0-based inclusive start page").optional(),
|
|
22894
23259
|
end_page: external_exports.number().int().describe("0-based inclusive end page").optional()
|
|
22895
|
-
}, async (args) => {
|
|
23260
|
+
}, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
|
|
22896
23261
|
const body = {};
|
|
22897
23262
|
if (args.content !== void 0)
|
|
22898
23263
|
body["content"] = args.content;
|
|
@@ -22909,14 +23274,14 @@ function registerTools() {
|
|
|
22909
23274
|
const result = await callApi("POST", `/v1/ocr:document`, body);
|
|
22910
23275
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22911
23276
|
});
|
|
22912
|
-
server.tool("goodmem_ping_once", "Run a single ping probe\n\nRuns a single ping probe and returns the probe result.", {
|
|
23277
|
+
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
23278
|
target_id: external_exports.string().describe("Target resource ID (UUID)"),
|
|
22914
23279
|
target_type_hint: external_exports.enum(["TARGET_TYPE_UNSPECIFIED", "EMBEDDER", "RERANKER", "LLM"]).describe("Optional hint for the target resource type").optional(),
|
|
22915
23280
|
payload_type: external_exports.enum(["PAYLOAD_TYPE_UNSPECIFIED", "TEXT", "JSON", "BINARY"]).describe("Desired payload type (defaults to provider-specific value)").optional(),
|
|
22916
23281
|
payload: external_exports.string().describe("Explicit UTF-8 payload to send with the probe (mutually exclusive with payload_size_bytes)").optional(),
|
|
22917
23282
|
payload_size_bytes: external_exports.number().int().describe("Synthetic payload size in bytes (mutually exclusive with payload)").optional(),
|
|
22918
23283
|
timeout_ms: external_exports.number().int().describe("Per-probe timeout in milliseconds (0 uses server default)").optional()
|
|
22919
|
-
}, async (args) => {
|
|
23284
|
+
}, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
|
|
22920
23285
|
const _set = [args.payload !== void 0 && args.payload !== null, args.payload_size_bytes !== void 0 && args.payload_size_bytes !== null].filter(Boolean).length;
|
|
22921
23286
|
if (_set > 1) {
|
|
22922
23287
|
throw new Error("at most one of payload or payload_size_bytes may be set");
|
|
@@ -22937,7 +23302,7 @@ function registerTools() {
|
|
|
22937
23302
|
const result = await callApi("POST", `/v1/ping:once`, body, void 0, (args.timeout_ms ?? 5e3) + 3e4);
|
|
22938
23303
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22939
23304
|
});
|
|
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.", {
|
|
23305
|
+
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
23306
|
target_id: external_exports.string().describe("Target resource ID (UUID)"),
|
|
22942
23307
|
target_type_hint: external_exports.enum(["TARGET_TYPE_UNSPECIFIED", "EMBEDDER", "RERANKER", "LLM"]).describe("Optional hint for the target resource type").optional(),
|
|
22943
23308
|
count: external_exports.number().int().describe("Number of probes to run (0 uses server default)").optional(),
|
|
@@ -22950,7 +23315,7 @@ function registerTools() {
|
|
|
22950
23315
|
jitter: external_exports.boolean().describe("Add jitter to probe scheduling").optional(),
|
|
22951
23316
|
labels: external_exports.record(external_exports.string(), external_exports.string()).describe("Optional labels to attach to the ping session").optional(),
|
|
22952
23317
|
timeout_sec: external_exports.number().int().describe("Maximum seconds to wait for the streaming response (default: 200).").optional()
|
|
22953
|
-
}, async (args) => {
|
|
23318
|
+
}, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
|
|
22954
23319
|
const _set = [args.payload !== void 0 && args.payload !== null, args.payload_size_bytes !== void 0 && args.payload_size_bytes !== null].filter(Boolean).length;
|
|
22955
23320
|
if (_set > 1) {
|
|
22956
23321
|
throw new Error("at most one of payload or payload_size_bytes may be set");
|
|
@@ -22981,7 +23346,7 @@ function registerTools() {
|
|
|
22981
23346
|
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
23347
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22983
23348
|
});
|
|
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.
|
|
23349
|
+
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. A SHA-256 credential fingerprint participates in uniqueness checking. 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
23350
|
display_name: external_exports.string().describe("User-facing name of the reranker"),
|
|
22986
23351
|
description: external_exports.string().describe("Description of the reranker").optional(),
|
|
22987
23352
|
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(),
|
|
@@ -23009,9 +23374,9 @@ function registerTools() {
|
|
|
23009
23374
|
labels: external_exports.record(external_exports.string(), external_exports.string()).describe("User-defined labels for categorization").optional(),
|
|
23010
23375
|
version: external_exports.string().describe("Version information").optional(),
|
|
23011
23376
|
monitoring_endpoint: external_exports.string().describe("Monitoring endpoint URL").optional(),
|
|
23012
|
-
owner_id: external_exports.string().describe("Optional owner
|
|
23377
|
+
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(),
|
|
23013
23378
|
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) => {
|
|
23379
|
+
}, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
|
|
23015
23380
|
const inferred = autoInferFromRegistry("rerankers", args);
|
|
23016
23381
|
checkCredentialRequired(inferred);
|
|
23017
23382
|
const body = {};
|
|
@@ -23044,29 +23409,33 @@ function registerTools() {
|
|
|
23044
23409
|
const result = await callApi("POST", `/v1/rerankers`, body);
|
|
23045
23410
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
23046
23411
|
});
|
|
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
|
|
23412
|
+
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
23413
|
id: external_exports.string().describe("The unique identifier of the reranker to delete")
|
|
23049
|
-
}, async (args) => {
|
|
23414
|
+
}, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
|
|
23050
23415
|
if (typeof args.id !== "string" || args.id === "") {
|
|
23051
23416
|
throw new Error("'id' must be a non-empty string");
|
|
23052
23417
|
}
|
|
23053
23418
|
await callApi("DELETE", `/v1/rerankers/${encodeURIComponent(String(args.id))}`, void 0);
|
|
23054
23419
|
return { content: [{ type: "text", text: "Deleted successfully." }] };
|
|
23055
23420
|
});
|
|
23056
|
-
server.tool("goodmem_rerankers_get", "Get a reranker by ID\n\nRetrieves the details of a specific reranker configuration by its unique identifier.
|
|
23057
|
-
id: external_exports.string().describe("The unique identifier of the reranker to retrieve")
|
|
23058
|
-
|
|
23421
|
+
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.", {
|
|
23422
|
+
id: external_exports.string().describe("The unique identifier of the reranker to retrieve"),
|
|
23423
|
+
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()
|
|
23424
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
23059
23425
|
if (typeof args.id !== "string" || args.id === "") {
|
|
23060
23426
|
throw new Error("'id' must be a non-empty string");
|
|
23061
23427
|
}
|
|
23062
|
-
const
|
|
23428
|
+
const query = {};
|
|
23429
|
+
if (args.include_credentials !== void 0)
|
|
23430
|
+
query["includeCredentials"] = String(args.include_credentials);
|
|
23431
|
+
const result = await callApi("GET", `/v1/rerankers/${encodeURIComponent(String(args.id))}`, void 0, query);
|
|
23063
23432
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
23064
23433
|
});
|
|
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.
|
|
23066
|
-
owner_id: external_exports.string().describe("Filter
|
|
23434
|
+
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.", {
|
|
23435
|
+
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(),
|
|
23067
23436
|
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(),
|
|
23068
23437
|
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) => {
|
|
23438
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
23070
23439
|
const query = {};
|
|
23071
23440
|
if (args.owner_id !== void 0)
|
|
23072
23441
|
query["ownerId"] = String(args.owner_id);
|
|
@@ -23079,7 +23448,7 @@ function registerTools() {
|
|
|
23079
23448
|
const result = await callApi("GET", `/v1/rerankers`, void 0, query);
|
|
23080
23449
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
23081
23450
|
});
|
|
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
|
|
23451
|
+
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 on the requested reranker. This operation is idempotent.", {
|
|
23083
23452
|
id: external_exports.string().describe("The unique identifier of the reranker to update"),
|
|
23084
23453
|
display_name: external_exports.string().describe("User-facing name of the reranker").optional(),
|
|
23085
23454
|
description: external_exports.string().describe("Description of the reranker").optional(),
|
|
@@ -23103,12 +23472,12 @@ function registerTools() {
|
|
|
23103
23472
|
quotaProjectId: external_exports.string().describe("Optional quota project used for billing").optional()
|
|
23104
23473
|
}).passthrough().describe("Configuration when kind is CREDENTIAL_KIND_GCP_ADC").optional(),
|
|
23105
23474
|
labels: external_exports.record(external_exports.string(), external_exports.string()).describe('Optional annotations to aid operators (e.g., "owner=vertex")').optional()
|
|
23106
|
-
}).passthrough().describe("
|
|
23475
|
+
}).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
23476
|
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
23477
|
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
23478
|
version: external_exports.string().describe("Version information").optional(),
|
|
23110
23479
|
monitoring_endpoint: external_exports.string().describe("Monitoring endpoint URL").optional()
|
|
23111
|
-
}, async (args) => {
|
|
23480
|
+
}, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
|
|
23112
23481
|
if (typeof args.id !== "string" || args.id === "") {
|
|
23113
23482
|
throw new Error("'id' must be a non-empty string");
|
|
23114
23483
|
}
|
|
@@ -23142,15 +23511,112 @@ function registerTools() {
|
|
|
23142
23511
|
const result = await callApi("PUT", `/v1/rerankers/${encodeURIComponent(String(args.id))}`, body);
|
|
23143
23512
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
23144
23513
|
});
|
|
23145
|
-
server.tool("
|
|
23514
|
+
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.", {
|
|
23515
|
+
service_identity_id: external_exports.string().describe("Optional client-provided UUID; generated by the server when omitted.").optional(),
|
|
23516
|
+
display_name: external_exports.string().describe("Globally unique, nonblank operator-facing name."),
|
|
23517
|
+
description: external_exports.string().describe("Optional operator description.").optional(),
|
|
23518
|
+
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()
|
|
23519
|
+
}, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
|
|
23520
|
+
const body = {};
|
|
23521
|
+
if (args.service_identity_id !== void 0)
|
|
23522
|
+
body["serviceIdentityId"] = args.service_identity_id;
|
|
23523
|
+
if (args.display_name !== void 0)
|
|
23524
|
+
body["displayName"] = args.display_name;
|
|
23525
|
+
if (args.description !== void 0)
|
|
23526
|
+
body["description"] = args.description;
|
|
23527
|
+
if (args.labels !== void 0)
|
|
23528
|
+
body["labels"] = args.labels;
|
|
23529
|
+
const result = await callApi("POST", `/v1/service-identities`, body);
|
|
23530
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
23531
|
+
});
|
|
23532
|
+
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.", {
|
|
23533
|
+
id: external_exports.string().describe("Service-identity UUID")
|
|
23534
|
+
}, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
|
|
23535
|
+
if (typeof args.id !== "string" || args.id === "") {
|
|
23536
|
+
throw new Error("'id' must be a non-empty string");
|
|
23537
|
+
}
|
|
23538
|
+
await callApi("DELETE", `/v1/service-identities/${encodeURIComponent(String(args.id))}`, void 0);
|
|
23539
|
+
return { content: [{ type: "text", text: "Deleted successfully." }] };
|
|
23540
|
+
});
|
|
23541
|
+
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.", {
|
|
23542
|
+
id: external_exports.string().describe("Service-identity UUID"),
|
|
23543
|
+
include_deleted: external_exports.boolean().describe("Permit an authorized read of a permanent tombstone").optional()
|
|
23544
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
23545
|
+
if (typeof args.id !== "string" || args.id === "") {
|
|
23546
|
+
throw new Error("'id' must be a non-empty string");
|
|
23547
|
+
}
|
|
23548
|
+
const query = {};
|
|
23549
|
+
if (args.include_deleted !== void 0)
|
|
23550
|
+
query["includeDeleted"] = String(args.include_deleted);
|
|
23551
|
+
const result = await callApi("GET", `/v1/service-identities/${encodeURIComponent(String(args.id))}`, void 0, query);
|
|
23552
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
23553
|
+
});
|
|
23554
|
+
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).", {
|
|
23555
|
+
owner_principal_id: external_exports.string().describe("Exact current administrative-owner principal UUID").optional(),
|
|
23556
|
+
include_deleted: external_exports.boolean().describe("Include readable permanent tombstones").optional(),
|
|
23557
|
+
max_results: external_exports.number().int().describe("Page size; defaults to 50 and must be between 1 and 1000").optional(),
|
|
23558
|
+
next_token: external_exports.string().describe("Opaque continuation token").optional(),
|
|
23559
|
+
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()
|
|
23560
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
23561
|
+
const query = {};
|
|
23562
|
+
if (args.owner_principal_id !== void 0)
|
|
23563
|
+
query["ownerPrincipalId"] = String(args.owner_principal_id);
|
|
23564
|
+
if (args.include_deleted !== void 0)
|
|
23565
|
+
query["includeDeleted"] = String(args.include_deleted);
|
|
23566
|
+
if (args.max_results !== void 0)
|
|
23567
|
+
query["maxResults"] = String(args.max_results);
|
|
23568
|
+
if (args.next_token !== void 0)
|
|
23569
|
+
query["nextToken"] = String(args.next_token);
|
|
23570
|
+
if (args.label) {
|
|
23571
|
+
for (const [k, v] of Object.entries(args.label))
|
|
23572
|
+
query[`label[${k}]`] = v;
|
|
23573
|
+
}
|
|
23574
|
+
const result = await callApi("GET", `/v1/service-identities`, void 0, query);
|
|
23575
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
23576
|
+
});
|
|
23577
|
+
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.", {
|
|
23578
|
+
id: external_exports.string().describe("Service-identity UUID"),
|
|
23579
|
+
new_owner_id: external_exports.string().describe("Existing principal UUID that will become the new owner.")
|
|
23580
|
+
}, { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false }, async (args) => {
|
|
23581
|
+
if (typeof args.id !== "string" || args.id === "") {
|
|
23582
|
+
throw new Error("'id' must be a non-empty string");
|
|
23583
|
+
}
|
|
23584
|
+
const body = {};
|
|
23585
|
+
if (args.new_owner_id !== void 0)
|
|
23586
|
+
body["newOwnerId"] = args.new_owner_id;
|
|
23587
|
+
const result = await callApi("POST", `/v1/service-identities/${encodeURIComponent(String(args.id))}:transferOwnership`, body);
|
|
23588
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
23589
|
+
});
|
|
23590
|
+
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.", {
|
|
23591
|
+
id: external_exports.string().describe("Service-identity UUID"),
|
|
23592
|
+
display_name: external_exports.string().describe("Replacement display name. A present blank value is invalid.").optional(),
|
|
23593
|
+
description: external_exports.string().describe("Replacement description. An empty string clears the description.").optional(),
|
|
23594
|
+
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(),
|
|
23595
|
+
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()
|
|
23596
|
+
}, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
|
|
23597
|
+
if (typeof args.id !== "string" || args.id === "") {
|
|
23598
|
+
throw new Error("'id' must be a non-empty string");
|
|
23599
|
+
}
|
|
23600
|
+
const body = {};
|
|
23601
|
+
if (args.display_name !== void 0)
|
|
23602
|
+
body["displayName"] = args.display_name;
|
|
23603
|
+
if (args.description !== void 0)
|
|
23604
|
+
body["description"] = args.description;
|
|
23605
|
+
if (args.replace_labels !== void 0)
|
|
23606
|
+
body["replaceLabels"] = args.replace_labels;
|
|
23607
|
+
if (args.merge_labels !== void 0)
|
|
23608
|
+
body["mergeLabels"] = args.merge_labels;
|
|
23609
|
+
const result = await callApi("PUT", `/v1/service-identities/${encodeURIComponent(String(args.id))}`, body);
|
|
23610
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
23611
|
+
});
|
|
23612
|
+
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
23613
|
name: external_exports.string().describe("The desired name for the space. Must be unique within the user's scope."),
|
|
23147
23614
|
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
23615
|
space_embedders: external_exports.array(external_exports.object({
|
|
23149
23616
|
embedderId: external_exports.string().describe("The UUID for the embedder to associate with the space."),
|
|
23150
23617
|
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
23618
|
}).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
|
-
|
|
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(),
|
|
23619
|
+
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
23620
|
default_chunking_config: external_exports.object({
|
|
23155
23621
|
none: external_exports.any().describe("No chunking strategy - preserve original content as single unit").optional(),
|
|
23156
23622
|
recursive: external_exports.object({
|
|
@@ -23169,7 +23635,7 @@ function registerTools() {
|
|
|
23169
23635
|
}).passthrough().describe("Sentence-based chunking strategy with language detection").optional()
|
|
23170
23636
|
}).passthrough().describe("Default chunking strategy for memories in this space"),
|
|
23171
23637
|
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) => {
|
|
23638
|
+
}, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
|
|
23173
23639
|
const body = {};
|
|
23174
23640
|
if (args.name !== void 0)
|
|
23175
23641
|
body["name"] = args.name;
|
|
@@ -23177,8 +23643,6 @@ function registerTools() {
|
|
|
23177
23643
|
body["labels"] = args.labels;
|
|
23178
23644
|
if (args.space_embedders !== void 0)
|
|
23179
23645
|
body["spaceEmbedders"] = args.space_embedders;
|
|
23180
|
-
if (args.public_read !== void 0)
|
|
23181
|
-
body["publicRead"] = args.public_read;
|
|
23182
23646
|
if (args.owner_id !== void 0)
|
|
23183
23647
|
body["ownerId"] = args.owner_id;
|
|
23184
23648
|
if (args.default_chunking_config !== void 0)
|
|
@@ -23188,18 +23652,18 @@ function registerTools() {
|
|
|
23188
23652
|
const result = await callApi("POST", `/v1/spaces`, body);
|
|
23189
23653
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
23190
23654
|
});
|
|
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
|
|
23655
|
+
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
23656
|
id: external_exports.string().describe("The unique identifier of the space to delete")
|
|
23193
|
-
}, async (args) => {
|
|
23657
|
+
}, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
|
|
23194
23658
|
if (typeof args.id !== "string" || args.id === "") {
|
|
23195
23659
|
throw new Error("'id' must be a non-empty string");
|
|
23196
23660
|
}
|
|
23197
23661
|
await callApi("DELETE", `/v1/spaces/${encodeURIComponent(String(args.id))}`, void 0);
|
|
23198
23662
|
return { content: [{ type: "text", text: "Deleted successfully." }] };
|
|
23199
23663
|
});
|
|
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.
|
|
23664
|
+
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
23665
|
id: external_exports.string().describe("The unique identifier of the space to retrieve")
|
|
23202
|
-
}, async (args) => {
|
|
23666
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
23203
23667
|
if (typeof args.id !== "string" || args.id === "") {
|
|
23204
23668
|
throw new Error("'id' must be a non-empty string");
|
|
23205
23669
|
}
|
|
@@ -23207,14 +23671,14 @@ function registerTools() {
|
|
|
23207
23671
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
23208
23672
|
});
|
|
23209
23673
|
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
|
|
23674
|
+
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
23675
|
name_filter: external_exports.string().describe("Filter spaces by name using glob pattern matching").optional(),
|
|
23212
23676
|
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
23677
|
next_token: external_exports.string().describe("Pagination token for retrieving the next set of results").optional(),
|
|
23214
23678
|
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
23679
|
sort_order: external_exports.enum(["ASCENDING", "DESCENDING", "SORT_ORDER_UNSPECIFIED"]).describe("Sort order (ASCENDING or DESCENDING, default: DESCENDING)").optional(),
|
|
23216
23680
|
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) => {
|
|
23681
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
23218
23682
|
const query = {};
|
|
23219
23683
|
if (args.owner_id !== void 0)
|
|
23220
23684
|
query["ownerId"] = String(args.owner_id);
|
|
@@ -23235,13 +23699,25 @@ function registerTools() {
|
|
|
23235
23699
|
const result = await callApi("GET", `/v1/spaces`, void 0, query);
|
|
23236
23700
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
23237
23701
|
});
|
|
23238
|
-
server.tool("
|
|
23702
|
+
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.", {
|
|
23703
|
+
id: external_exports.string().describe("UUID of the existing space to transfer"),
|
|
23704
|
+
new_owner_id: external_exports.string().describe("Existing principal UUID that will become the new owner.")
|
|
23705
|
+
}, { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false }, async (args) => {
|
|
23706
|
+
if (typeof args.id !== "string" || args.id === "") {
|
|
23707
|
+
throw new Error("'id' must be a non-empty string");
|
|
23708
|
+
}
|
|
23709
|
+
const body = {};
|
|
23710
|
+
if (args.new_owner_id !== void 0)
|
|
23711
|
+
body["newOwnerId"] = args.new_owner_id;
|
|
23712
|
+
const result = await callApi("POST", `/v1/spaces/${encodeURIComponent(String(args.id))}:transferOwnership`, body);
|
|
23713
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
23714
|
+
});
|
|
23715
|
+
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
23716
|
id: external_exports.string().describe("The unique identifier of the space to update"),
|
|
23240
23717
|
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
23718
|
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
23719
|
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) => {
|
|
23720
|
+
}, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
|
|
23245
23721
|
if (typeof args.id !== "string" || args.id === "") {
|
|
23246
23722
|
throw new Error("'id' must be a non-empty string");
|
|
23247
23723
|
}
|
|
@@ -23252,8 +23728,6 @@ function registerTools() {
|
|
|
23252
23728
|
const body = {};
|
|
23253
23729
|
if (args.name !== void 0)
|
|
23254
23730
|
body["name"] = args.name;
|
|
23255
|
-
if (args.public_read !== void 0)
|
|
23256
|
-
body["publicRead"] = args.public_read;
|
|
23257
23731
|
if (args.replace_labels !== void 0)
|
|
23258
23732
|
body["replaceLabels"] = args.replace_labels;
|
|
23259
23733
|
if (args.merge_labels !== void 0)
|
|
@@ -23261,18 +23735,80 @@ function registerTools() {
|
|
|
23261
23735
|
const result = await callApi("PUT", `/v1/spaces/${encodeURIComponent(String(args.id))}`, body);
|
|
23262
23736
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
23263
23737
|
});
|
|
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) => {
|
|
23738
|
+
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
23739
|
const result = await callApi("GET", `/v1/system/info`, void 0);
|
|
23266
23740
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
23267
23741
|
});
|
|
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) => {
|
|
23742
|
+
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
23743
|
const result = await callApi("POST", `/v1/system/init`, void 0);
|
|
23270
23744
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
23271
23745
|
});
|
|
23746
|
+
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.", {
|
|
23747
|
+
enrollment_token: external_exports.string().describe("Required one-time enrollment credential. Never log or persist it."),
|
|
23748
|
+
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(),
|
|
23749
|
+
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()
|
|
23750
|
+
}, { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false }, async (args) => {
|
|
23751
|
+
const body = {};
|
|
23752
|
+
if (args.enrollment_token !== void 0)
|
|
23753
|
+
body["enrollmentToken"] = args.enrollment_token;
|
|
23754
|
+
if (args.api_key_id !== void 0)
|
|
23755
|
+
body["apiKeyId"] = args.api_key_id;
|
|
23756
|
+
if (args.raw_api_key !== void 0)
|
|
23757
|
+
body["rawApiKey"] = args.raw_api_key;
|
|
23758
|
+
const result = await callApi("POST", `/v1/user-enrollments:complete`, body);
|
|
23759
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
23760
|
+
});
|
|
23761
|
+
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.", {
|
|
23762
|
+
user_id: external_exports.string().describe("Optional client-provided user UUID; generated by the server when omitted.").optional(),
|
|
23763
|
+
email: external_exports.string().describe("Unique, nonempty email address."),
|
|
23764
|
+
username: external_exports.string().describe("Optional unique username.").optional(),
|
|
23765
|
+
display_name: external_exports.string().describe("Optional human-facing display name.").optional(),
|
|
23766
|
+
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()
|
|
23767
|
+
}, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (args) => {
|
|
23768
|
+
const body = {};
|
|
23769
|
+
if (args.user_id !== void 0)
|
|
23770
|
+
body["userId"] = args.user_id;
|
|
23771
|
+
if (args.email !== void 0)
|
|
23772
|
+
body["email"] = args.email;
|
|
23773
|
+
if (args.username !== void 0)
|
|
23774
|
+
body["username"] = args.username;
|
|
23775
|
+
if (args.display_name !== void 0)
|
|
23776
|
+
body["displayName"] = args.display_name;
|
|
23777
|
+
if (args.labels !== void 0)
|
|
23778
|
+
body["labels"] = args.labels;
|
|
23779
|
+
const result = await callApi("POST", `/v1/users`, body);
|
|
23780
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
23781
|
+
});
|
|
23782
|
+
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.", {
|
|
23783
|
+
user_id: external_exports.string().describe("Human-user UUID"),
|
|
23784
|
+
enrollment_id: external_exports.string().describe("Optional client-provided enrollment UUID; generated when omitted.").optional(),
|
|
23785
|
+
rotate_existing: external_exports.boolean().describe("Whether an existing live enrollment may be revoked and atomically replaced.").optional()
|
|
23786
|
+
}, { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false }, async (args) => {
|
|
23787
|
+
if (typeof args.user_id !== "string" || args.user_id === "") {
|
|
23788
|
+
throw new Error("'user_id' must be a non-empty string");
|
|
23789
|
+
}
|
|
23790
|
+
const body = {};
|
|
23791
|
+
if (args.enrollment_id !== void 0)
|
|
23792
|
+
body["enrollmentId"] = args.enrollment_id;
|
|
23793
|
+
if (args.rotate_existing !== void 0)
|
|
23794
|
+
body["rotateExisting"] = args.rotate_existing;
|
|
23795
|
+
const result = await callApi("POST", `/v1/users/${encodeURIComponent(String(args.user_id))}/enrollments`, body);
|
|
23796
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
23797
|
+
});
|
|
23798
|
+
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.", {
|
|
23799
|
+
id: external_exports.string().describe("User UUID")
|
|
23800
|
+
}, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
|
|
23801
|
+
if (typeof args.id !== "string" || args.id === "") {
|
|
23802
|
+
throw new Error("'id' must be a non-empty string");
|
|
23803
|
+
}
|
|
23804
|
+
await callApi("DELETE", `/v1/users/${encodeURIComponent(String(args.id))}`, void 0);
|
|
23805
|
+
return { content: [{ type: "text", text: "Deleted successfully." }] };
|
|
23806
|
+
});
|
|
23272
23807
|
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("
|
|
23274
|
-
email: external_exports.string().describe("
|
|
23275
|
-
|
|
23808
|
+
id: external_exports.string().describe("User UUID").optional(),
|
|
23809
|
+
email: external_exports.string().describe("URL-encoded email address").optional(),
|
|
23810
|
+
include_deleted: external_exports.boolean().describe("Permit an authorized read of a permanent tombstone").optional()
|
|
23811
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
23276
23812
|
const _hasId = typeof args.id === "string" && args.id !== "";
|
|
23277
23813
|
const _hasEmail = typeof args.email === "string" && args.email !== "";
|
|
23278
23814
|
if (_hasId && _hasEmail)
|
|
@@ -23280,13 +23816,119 @@ function registerTools() {
|
|
|
23280
23816
|
if (!_hasId && !_hasEmail)
|
|
23281
23817
|
throw new Error("Exactly one of 'id', 'email' must be provided (non-empty).");
|
|
23282
23818
|
const url = _hasEmail ? `/v1/users/email/${encodeURIComponent(String(args.email))}` : `/v1/users/${encodeURIComponent(String(args.id))}`;
|
|
23283
|
-
const
|
|
23819
|
+
const query = {};
|
|
23820
|
+
if (args.include_deleted !== void 0)
|
|
23821
|
+
query["includeDeleted"] = String(args.include_deleted);
|
|
23822
|
+
const result = await callApi("GET", url, void 0, query);
|
|
23823
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
23824
|
+
});
|
|
23825
|
+
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.", {
|
|
23826
|
+
username: external_exports.string().describe("Exact username"),
|
|
23827
|
+
include_deleted: external_exports.boolean().describe("Permit an authorized read of a permanent tombstone").optional()
|
|
23828
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
23829
|
+
if (typeof args.username !== "string" || args.username === "") {
|
|
23830
|
+
throw new Error("'username' must be a non-empty string");
|
|
23831
|
+
}
|
|
23832
|
+
const query = {};
|
|
23833
|
+
if (args.include_deleted !== void 0)
|
|
23834
|
+
query["includeDeleted"] = String(args.include_deleted);
|
|
23835
|
+
const result = await callApi("GET", `/v1/users/username/${encodeURIComponent(String(args.username))}`, void 0, query);
|
|
23836
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
23837
|
+
});
|
|
23838
|
+
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.", {
|
|
23839
|
+
user_id: external_exports.string().describe("Human-user UUID"),
|
|
23840
|
+
enrollment_id: external_exports.string().describe("Enrollment UUID")
|
|
23841
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
23842
|
+
if (typeof args.user_id !== "string" || args.user_id === "") {
|
|
23843
|
+
throw new Error("'user_id' must be a non-empty string");
|
|
23844
|
+
}
|
|
23845
|
+
if (typeof args.enrollment_id !== "string" || args.enrollment_id === "") {
|
|
23846
|
+
throw new Error("'enrollment_id' must be a non-empty string");
|
|
23847
|
+
}
|
|
23848
|
+
const result = await callApi("GET", `/v1/users/${encodeURIComponent(String(args.user_id))}/enrollments/${encodeURIComponent(String(args.enrollment_id))}`, void 0);
|
|
23849
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
23850
|
+
});
|
|
23851
|
+
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).", {
|
|
23852
|
+
include_deleted: external_exports.boolean().describe("Include readable permanent tombstones").optional(),
|
|
23853
|
+
include_enrollment_summary: external_exports.boolean().describe("Request non-secret enrollment posture on active rows where the caller also has MANAGE_USER_ENROLLMENT").optional(),
|
|
23854
|
+
max_results: external_exports.number().int().describe("Page size; defaults to 50 and must be between 1 and 1000").optional(),
|
|
23855
|
+
next_token: external_exports.string().describe("Opaque continuation token").optional(),
|
|
23856
|
+
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()
|
|
23857
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
23858
|
+
const query = {};
|
|
23859
|
+
if (args.include_deleted !== void 0)
|
|
23860
|
+
query["includeDeleted"] = String(args.include_deleted);
|
|
23861
|
+
if (args.include_enrollment_summary !== void 0)
|
|
23862
|
+
query["includeEnrollmentSummary"] = String(args.include_enrollment_summary);
|
|
23863
|
+
if (args.max_results !== void 0)
|
|
23864
|
+
query["maxResults"] = String(args.max_results);
|
|
23865
|
+
if (args.next_token !== void 0)
|
|
23866
|
+
query["nextToken"] = String(args.next_token);
|
|
23867
|
+
if (args.label) {
|
|
23868
|
+
for (const [k, v] of Object.entries(args.label))
|
|
23869
|
+
query[`label[${k}]`] = v;
|
|
23870
|
+
}
|
|
23871
|
+
const result = await callApi("GET", `/v1/users`, void 0, query);
|
|
23872
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
23873
|
+
});
|
|
23874
|
+
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.", {
|
|
23875
|
+
user_id: external_exports.string().describe("Human-user UUID"),
|
|
23876
|
+
max_results: external_exports.number().int().describe("Page size; defaults to 50 and must be between 1 and 1000").optional(),
|
|
23877
|
+
next_token: external_exports.string().describe("Opaque continuation token").optional()
|
|
23878
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (args) => {
|
|
23879
|
+
if (typeof args.user_id !== "string" || args.user_id === "") {
|
|
23880
|
+
throw new Error("'user_id' must be a non-empty string");
|
|
23881
|
+
}
|
|
23882
|
+
const query = {};
|
|
23883
|
+
if (args.max_results !== void 0)
|
|
23884
|
+
query["maxResults"] = String(args.max_results);
|
|
23885
|
+
if (args.next_token !== void 0)
|
|
23886
|
+
query["nextToken"] = String(args.next_token);
|
|
23887
|
+
const result = await callApi("GET", `/v1/users/${encodeURIComponent(String(args.user_id))}/enrollments`, void 0, query);
|
|
23284
23888
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
23285
23889
|
});
|
|
23286
|
-
server.tool("goodmem_users_me", "Get current user profile\n\
|
|
23890
|
+
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
23891
|
const result = await callApi("GET", `/v1/users/me`, void 0);
|
|
23288
23892
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
23289
23893
|
});
|
|
23894
|
+
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.", {
|
|
23895
|
+
user_id: external_exports.string().describe("Human-user UUID"),
|
|
23896
|
+
enrollment_id: external_exports.string().describe("Enrollment UUID")
|
|
23897
|
+
}, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
|
|
23898
|
+
if (typeof args.user_id !== "string" || args.user_id === "") {
|
|
23899
|
+
throw new Error("'user_id' must be a non-empty string");
|
|
23900
|
+
}
|
|
23901
|
+
if (typeof args.enrollment_id !== "string" || args.enrollment_id === "") {
|
|
23902
|
+
throw new Error("'enrollment_id' must be a non-empty string");
|
|
23903
|
+
}
|
|
23904
|
+
await callApi("DELETE", `/v1/users/${encodeURIComponent(String(args.user_id))}/enrollments/${encodeURIComponent(String(args.enrollment_id))}`, void 0);
|
|
23905
|
+
return { content: [{ type: "text", text: "Deleted successfully." }] };
|
|
23906
|
+
});
|
|
23907
|
+
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.", {
|
|
23908
|
+
id: external_exports.string().describe("User UUID"),
|
|
23909
|
+
email: external_exports.string().describe("Replacement email. A present empty value is invalid.").optional(),
|
|
23910
|
+
username: external_exports.string().describe("Replacement username. An empty string clears the username.").optional(),
|
|
23911
|
+
display_name: external_exports.string().describe("Replacement display name. An empty string clears the display name.").optional(),
|
|
23912
|
+
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(),
|
|
23913
|
+
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()
|
|
23914
|
+
}, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }, async (args) => {
|
|
23915
|
+
if (typeof args.id !== "string" || args.id === "") {
|
|
23916
|
+
throw new Error("'id' must be a non-empty string");
|
|
23917
|
+
}
|
|
23918
|
+
const body = {};
|
|
23919
|
+
if (args.email !== void 0)
|
|
23920
|
+
body["email"] = args.email;
|
|
23921
|
+
if (args.username !== void 0)
|
|
23922
|
+
body["username"] = args.username;
|
|
23923
|
+
if (args.display_name !== void 0)
|
|
23924
|
+
body["displayName"] = args.display_name;
|
|
23925
|
+
if (args.replace_labels !== void 0)
|
|
23926
|
+
body["replaceLabels"] = args.replace_labels;
|
|
23927
|
+
if (args.merge_labels !== void 0)
|
|
23928
|
+
body["mergeLabels"] = args.merge_labels;
|
|
23929
|
+
const result = await callApi("PUT", `/v1/users/${encodeURIComponent(String(args.id))}`, body);
|
|
23930
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
23931
|
+
});
|
|
23290
23932
|
}
|
|
23291
23933
|
registerTools();
|
|
23292
23934
|
async function healthCheck() {
|