loopctl-mcp-server 2.53.1 → 2.54.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/index.js +69 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -297,7 +297,7 @@ it is enforced server-side and a no-op for a non-superadmin key — see below.)
|
|
|
297
297
|
| Tool | Description |
|
|
298
298
|
|---|---|
|
|
299
299
|
| `llm_config` | **Check your onboarding status.** Get the tenant's BYO LLM config: per-operation models and whether each key is set — `has_api_key` (Anthropic) / `has_embedding_key` (OpenAI embedding) — plus masked last-4 hints. Never returns a key. Requires **user** key. |
|
|
300
|
-
| `set_llm_config` | **First-time setup (do once).** Set/rotate the tenant's OWN **Anthropic `api_key`** (powers ingest) AND **OpenAI `embedding_api_key`** (powers semantic search) — both stored encrypted, never returned — plus the per-operation models (`extraction_model`/`classification_model`/`merge_model`/`embedding_model`). Any subset; partial-merge (omitting a key leaves it untouched). See [First-time setup](#first-time-setup--provision-your-byo-llm-keys). Requires **user** key. |
|
|
300
|
+
| `set_llm_config` | **First-time setup (do once).** Set/rotate the tenant's OWN **Anthropic `api_key`** (powers ingest) AND **OpenAI `embedding_api_key`** (powers semantic search) — both stored encrypted, never returned — plus the per-operation models (`extraction_model`/`classification_model`/`merge_model`/`embedding_model`). Any subset; partial-merge (omitting a key leaves it untouched). **Private tier (US-41.3):** also sets `chat_provider` (`anthropic` | `openai_compatible`), `chat_base_url`, `extraction_model` (required on that provider) and `chat_api_key` (optional — omit it for a keyless local server) so ingest extraction / classification / merge / memory promotion run against YOUR OWN OpenAI-compatible endpoint and document text never leaves your boundary. Every model the config resolves to is PROBED with a trivial completion before it is saved (a 422 persists nothing); the probe re-runs on any chat-surface change (provider, base_url, key rotation, model), a private-range host is refused by the SSRF guard, and moving to a NEW host needs a matching `chat_api_key` or `acknowledge_key_transmission: true`. See [First-time setup](#first-time-setup--provision-your-byo-llm-keys). Requires **user** key. |
|
|
301
301
|
| `knowledge_llm_usage` | Per-tenant LLM token-usage summary, grouped by operation + model + source_type + day over an optional `from`/`to` range (defaults to a 90-day lookback; effective window echoed in `meta.from`/`meta.to`), with `limit`/`offset` pagination. Record-only (no budget enforcement). Requires orchestrator key. |
|
|
302
302
|
|
|
303
303
|
### Knowledge Analytics Tools (orchestrator key)
|
package/index.js
CHANGED
|
@@ -1852,6 +1852,10 @@ async function setLlmConfig({
|
|
|
1852
1852
|
merge_model,
|
|
1853
1853
|
embedding_api_key,
|
|
1854
1854
|
embedding_model,
|
|
1855
|
+
chat_provider,
|
|
1856
|
+
chat_base_url,
|
|
1857
|
+
chat_api_key,
|
|
1858
|
+
acknowledge_key_transmission,
|
|
1855
1859
|
}) {
|
|
1856
1860
|
const body = {};
|
|
1857
1861
|
if (api_key != null) body.api_key = api_key;
|
|
@@ -1860,6 +1864,15 @@ async function setLlmConfig({
|
|
|
1860
1864
|
if (merge_model !== undefined) body.merge_model = merge_model;
|
|
1861
1865
|
if (embedding_api_key != null) body.embedding_api_key = embedding_api_key;
|
|
1862
1866
|
if (embedding_model !== undefined) body.embedding_model = embedding_model;
|
|
1867
|
+
// US-41.3: pluggable OpenAI-compatible chat endpoint. The server PROBES the
|
|
1868
|
+
// endpoint with a trivial completion before persisting, and refuses an endpoint
|
|
1869
|
+
// change that carries neither a matching chat_api_key nor an explicit
|
|
1870
|
+
// acknowledge_key_transmission.
|
|
1871
|
+
if (chat_provider !== undefined) body.chat_provider = chat_provider;
|
|
1872
|
+
if (chat_base_url !== undefined) body.chat_base_url = chat_base_url;
|
|
1873
|
+
if (chat_api_key != null) body.chat_api_key = chat_api_key;
|
|
1874
|
+
if (acknowledge_key_transmission === true)
|
|
1875
|
+
body.acknowledge_key_transmission = true;
|
|
1863
1876
|
// PATCH (partial-merge) + EXACT user key (review #12, #13).
|
|
1864
1877
|
const result = await apiCall(
|
|
1865
1878
|
"PATCH",
|
|
@@ -5114,7 +5127,11 @@ const TOOLS = [
|
|
|
5114
5127
|
"(extraction_model / classification_model / merge_model / embedding_model) are " +
|
|
5115
5128
|
"free-form (any model the key permits) and default server-side when omitted. Typical " +
|
|
5116
5129
|
'onboarding call: set_llm_config({api_key: "sk-ant-...", embedding_api_key: "sk-..."}). ' +
|
|
5117
|
-
"
|
|
5130
|
+
"PRIVATE TIER: set chat_provider 'openai_compatible' + chat_base_url + " +
|
|
5131
|
+
"extraction_model (+ chat_api_key unless your server is keyless) to run " +
|
|
5132
|
+
"extraction/classification/merge against YOUR OWN endpoint so document text " +
|
|
5133
|
+
"never leaves your boundary; every model it resolves to is probed before it is saved. " +
|
|
5134
|
+
"Verify anytime with llm_config (has_api_key / has_embedding_key / chat_provider). REQUIRES your " +
|
|
5118
5135
|
"user-role key LOOPCTL_USER_KEY (minted by the human-anchored signup ceremony); if it " +
|
|
5119
5136
|
"is unset the tool fails fast telling you to set it.",
|
|
5120
5137
|
inputSchema: {
|
|
@@ -5128,7 +5145,14 @@ const TOOLS = [
|
|
|
5128
5145
|
},
|
|
5129
5146
|
extraction_model: {
|
|
5130
5147
|
type: "string",
|
|
5131
|
-
description:
|
|
5148
|
+
description:
|
|
5149
|
+
"Model id for knowledge extraction (null → server default). REQUIRED with " +
|
|
5150
|
+
"chat_provider 'openai_compatible': the server default is an Anthropic model " +
|
|
5151
|
+
"id your endpoint cannot serve, so there is no safe fallback. It is also the " +
|
|
5152
|
+
"fallback for classification_model / merge_model on that provider. " +
|
|
5153
|
+
"HuggingFace repo ids are valid (e.g. 'meta-llama/Meta-Llama-3-8B-Instruct') " +
|
|
5154
|
+
"— it is sent verbatim as the OpenAI `model` field, so it must match the name " +
|
|
5155
|
+
"your server actually serves.",
|
|
5132
5156
|
},
|
|
5133
5157
|
classification_model: {
|
|
5134
5158
|
type: "string",
|
|
@@ -5150,6 +5174,48 @@ const TOOLS = [
|
|
|
5150
5174
|
description:
|
|
5151
5175
|
"Embedding model id (null → server default text-embedding-3-small).",
|
|
5152
5176
|
},
|
|
5177
|
+
chat_provider: {
|
|
5178
|
+
type: "string",
|
|
5179
|
+
enum: ["anthropic", "openai_compatible"],
|
|
5180
|
+
description:
|
|
5181
|
+
"Which provider serves the CHAT surface (ingest extraction, classification, " +
|
|
5182
|
+
"merge synthesis, memory promotion). Omit or 'anthropic' keeps the hardcoded " +
|
|
5183
|
+
"Anthropic endpoint and identical behaviour. 'openai_compatible' routes that " +
|
|
5184
|
+
"surface — the largest and most sensitive payload in the pipeline, your " +
|
|
5185
|
+
"documents' full text — to YOUR OWN endpoint instead.",
|
|
5186
|
+
},
|
|
5187
|
+
chat_base_url: {
|
|
5188
|
+
type: "string",
|
|
5189
|
+
description:
|
|
5190
|
+
"API base of your OpenAI-compatible server, e.g. " +
|
|
5191
|
+
"'https://llm.example.internal/v1' (the client appends /chat/completions). " +
|
|
5192
|
+
"Required with chat_provider 'openai_compatible'. PROBED with a trivial " +
|
|
5193
|
+
"completion per resolved model BEFORE it is saved: an unreachable host, a " +
|
|
5194
|
+
"rejected credential or a non-OpenAI-compatible response is a 422 and NOTHING " +
|
|
5195
|
+
"is persisted. A host resolving into a private/loopback/link-local range is " +
|
|
5196
|
+
"refused outright unless the OPERATOR allowlisted it (SSRF guard). Must be a " +
|
|
5197
|
+
"BARE base: no query string, no fragment, no user:pass@ credentials (this " +
|
|
5198
|
+
"column is NOT encrypted — anything in the URL is stored and echoed back). " +
|
|
5199
|
+
"Plaintext http is accepted ONLY for a host the egress policy classifies " +
|
|
5200
|
+
"network-local; a public http:// endpoint is refused because the request " +
|
|
5201
|
+
"carries your key and your documents' full text in cleartext.",
|
|
5202
|
+
},
|
|
5203
|
+
chat_api_key: {
|
|
5204
|
+
type: "string",
|
|
5205
|
+
description:
|
|
5206
|
+
"Credential for chat_base_url. Write-only; stored encrypted, never returned. " +
|
|
5207
|
+
"SEPARATE from api_key — your Anthropic key is never sent to your endpoint. " +
|
|
5208
|
+
"OPTIONAL: a local server that serves /chat/completions with no auth is " +
|
|
5209
|
+
"configured by omitting this, and no authorization header is then sent.",
|
|
5210
|
+
},
|
|
5211
|
+
acknowledge_key_transmission: {
|
|
5212
|
+
type: "boolean",
|
|
5213
|
+
description:
|
|
5214
|
+
"Required when CHANGING chat_base_url without supplying a matching " +
|
|
5215
|
+
"chat_api_key: explicitly acknowledges that the already-stored key will be " +
|
|
5216
|
+
"transmitted to the new host. The probe never ships an existing credential to " +
|
|
5217
|
+
"a new host silently. Not persisted.",
|
|
5218
|
+
},
|
|
5153
5219
|
},
|
|
5154
5220
|
required: [],
|
|
5155
5221
|
},
|
|
@@ -5157,7 +5223,7 @@ const TOOLS = [
|
|
|
5157
5223
|
{
|
|
5158
5224
|
name: "knowledge_llm_usage",
|
|
5159
5225
|
description:
|
|
5160
|
-
"Per-tenant LLM token-usage summary, grouped by operation + model + source_type + " +
|
|
5226
|
+
"Per-tenant LLM token-usage summary, grouped by operation + model + provider + source_type + " +
|
|
5161
5227
|
"day over an optional date range, newest day first, with offset/limit pagination " +
|
|
5162
5228
|
"over meta.total_count. When `from` is omitted it defaults to a 90-day lookback; the " +
|
|
5163
5229
|
"EFFECTIVE window is echoed in meta.from/meta.to so you can detect that older usage " +
|