loopctl-mcp-server 2.38.0 → 2.39.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 +4 -3
- package/index.js +83 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -121,7 +121,7 @@ REST endpoint (`PATCH /api/v1/tenants/me/llm-config`), and the docs — so you (
|
|
|
121
121
|
autonomous agent) can self-remediate without a human. Full agent-tenant lifecycle:
|
|
122
122
|
[`docs/onboarding-agent-tenant.md`](../docs/onboarding-agent-tenant.md).
|
|
123
123
|
|
|
124
|
-
## Tools (
|
|
124
|
+
## Tools (84)
|
|
125
125
|
|
|
126
126
|
### Project Tools
|
|
127
127
|
|
|
@@ -214,6 +214,7 @@ autonomous agent) can self-remediate without a human. Full agent-tenant lifecycl
|
|
|
214
214
|
| `knowledge_conflicts` | List potential-conflict article pairs — published articles flagged "too similar to comfortably coexist" by the auto-linker / nightly lint sweep, highest-overlap first. The KB only FLAGS the pair; it does NOT decide redundancy-vs-contradiction — that's your call with live context. Each entry has both articles (id/title/status/category) + similarity. Then merge (supersede one, `knowledge_create` the merged article, or PATCH) or reconcile if they genuinely disagree. Paginated with `total_count` in meta. Agent role. Optional: `limit` (default 50, max 1000, clamped), `offset`. |
|
|
215
215
|
| `knowledge_resolve_conflict` | Record YOUR verdict on a potential-conflict pair (from `knowledge_conflicts`). Dispositions: `dismiss` (false positive, drops from queue), `supersede` (one wins — pass `authoritative_article_id`; nightly executor links + retires loser, only at `confidence:"high"`, reversible/audited), `merge` (at high confidence an LLM synthesizes both into ONE new DRAFT, sources preserved, never auto-published). Non-destructive at agent role — you record intent, the privileged nightly job executes. Last-write-wins per pair. Required: `source_article_id`, `target_article_id`, `disposition`. Optional: `authoritative_article_id`, `classification`, `evidence`, `confidence`. |
|
|
216
216
|
| `knowledge_create` | Create a new knowledge article. File findings, document patterns, or record decisions. **Published immediately by default** (visible per `metadata.visibility` — default `owner` for agent authors, only visible to that agent; `shared` for visibility to all agents) — the response `note` says which outcome occurred. Pass `draft: true` to stage it for later review instead (publish afterwards with `knowledge_publish`). Pass `metadata: {visibility: "shared"}` to make the article visible to other agents; higher roles can set visibility and agent_id explicitly. Pass `idempotency_key` for idempotent capture (re-creating with the same key is a no-op returning the existing article — no partial duplicates). Optional: `category`, `tags`, `project_id`, `draft`, `idempotency_key`, `source_type`, `source_id`, `metadata`. |
|
|
217
|
+
| `knowledge_update` | Edit an EXISTING article IN PLACE, **preserving its ID** (IDs are load-bearing — cited in project CLAUDE.mds and cross-links). Fold in a new fact, tidy a hub, retag, or reclassify without churning a new row. Send only the fields to change; `tags` REPLACES the whole array. A changed body/tags re-triggers embedding + auto-linking. Agent role — KB-content curation (reversible + audited); visibility-scoped, so another agent's private/owner memory 404s. Required: `article_id`. Optional: `title`, `body`, `category`, `tags`, `metadata`. |
|
|
217
218
|
| `knowledge_okf_export` | **Requires `LOOPCTL_USER_KEY`.** Export the wiki as a portable OKF (Open Knowledge Format) v0.1 bundle of markdown files. Writes to `out_dir`, or returns `{files, meta}` inline. |
|
|
218
219
|
| `knowledge_okf_import` | **Requires `LOOPCTL_USER_KEY`.** Import an OKF v0.1 bundle from a local directory. Creates or (with `merge`) updates articles; tolerates and preserves unknown frontmatter. |
|
|
219
220
|
|
|
@@ -243,8 +244,8 @@ it is enforced server-side and a no-op for a non-superadmin key — see below.)
|
|
|
243
244
|
| `knowledge_bulk_publish` | **Requires `LOOPCTL_USER_KEY`.** Publish drafts, partial-success style: every valid draft publishes; others are reported per-id as `skipped` (already published — idempotent — or archived/superseded), `not_found`, or `errored`. No 100-id cap (auto-chunked); duplicates ignored; safe to retry. `meta.count` = published; `meta.counts`/`meta.results` give the breakdown. Required: `article_ids` (array). |
|
|
244
245
|
| `knowledge_unpublish` | **Requires `LOOPCTL_USER_KEY`.** Revert a published article back to draft (hidden from search/context, not deleted). Required: `article_id`. |
|
|
245
246
|
| `knowledge_bulk_unpublish` | **Requires `LOOPCTL_USER_KEY`.** Revert published articles to draft in bulk, partial-success style (mirror of `knowledge_bulk_publish`): per-id `unpublished`/`skipped` (already draft, or archived/superseded)/`not_found`/`errored`. No 100-id cap (auto-chunked, ≤5000); duplicates ignored; safe to retry. Not deleted (re-publish to restore; `knowledge_bulk_delete` to archive). `meta.count`/`meta.counts`/`meta.results` give the breakdown. Required: `article_ids` (array). |
|
|
246
|
-
| `knowledge_archive` |
|
|
247
|
-
| `knowledge_delete` |
|
|
247
|
+
| `knowledge_archive` | Soft-delete an article (draft or published). Row retained for audit; hidden from all reads (reversible — re-publish/edit it back). Agent role — KB-content curation, visibility-scoped (another agent's private/owner memory 404s). Required: `article_id`. |
|
|
248
|
+
| `knowledge_delete` | Alias for `knowledge_archive` — DELETE verb on the REST API archives under the hood (soft delete, reversible, audited). Agent role. (Irreversible HARD delete is `knowledge_bulk_delete hard:true`, which stays `LOOPCTL_USER_KEY`.) Required: `article_id`. |
|
|
248
249
|
| `knowledge_bulk_delete` | **Requires `LOOPCTL_USER_KEY`.** Bulk archive (default, reversible) or IRREVERSIBLE hard-delete by selector. Provide exactly one selector: `article_ids` (list), `source_type`+`source_id` (every active article from a source), or `tag`+`confirm:true` (every active article with the tag — high blast radius). Default = set-based soft archive (idempotent; `meta.count`=archived, `meta.counts`/`meta.results` give the breakdown; ≤5000). **Dry-run** (`dry_run:true`) mutates nothing, returns `meta.would_affect` (with `hard:true` also a single-use `meta.token`, or `meta.confirm_hash` for oversized selectors). **Hard delete** (irreversible): dry-run with `hard:true` for a token, then call again with `hard:true`+`token` to FK-correctly delete the frozen id-set (links first, access events cascade). |
|
|
249
250
|
| `knowledge_drafts` | List draft (unpublished) knowledge articles with pagination. Optional: `limit` (default 20, max 1000 — over-max → 400, no silent clamp), `offset` (default 0), `project_id`. Returns `meta.total_count`. |
|
|
250
251
|
| `knowledge_lint` | Run a lint check on the knowledge wiki to identify stale or low-coverage articles. Optional: `project_id`, `stale_days`, `min_coverage`, `max_per_category` (default 50, max 500). True totals returned in `summary.total_per_category`. |
|
package/index.js
CHANGED
|
@@ -1065,6 +1065,26 @@ async function knowledgeCreate({
|
|
|
1065
1065
|
return toContent(result);
|
|
1066
1066
|
}
|
|
1067
1067
|
|
|
1068
|
+
// In-place edit of an existing article (#331). PATCH preserves the ID; only the
|
|
1069
|
+
// provided fields change. Agent role — KB-content curation, visibility-scoped
|
|
1070
|
+
// server-side (another agent's private/owner memory 404s).
|
|
1071
|
+
async function knowledgeUpdate({ article_id, title, body, category, tags, metadata }) {
|
|
1072
|
+
const payload = {};
|
|
1073
|
+
if (title != null) payload.title = title;
|
|
1074
|
+
if (body != null) payload.body = body;
|
|
1075
|
+
if (category != null) payload.category = category;
|
|
1076
|
+
if (tags != null) payload.tags = tags;
|
|
1077
|
+
if (metadata != null) payload.metadata = metadata;
|
|
1078
|
+
|
|
1079
|
+
const result = await apiCall(
|
|
1080
|
+
"PATCH",
|
|
1081
|
+
`/api/v1/articles/${article_id}`,
|
|
1082
|
+
payload,
|
|
1083
|
+
process.env.LOOPCTL_AGENT_KEY,
|
|
1084
|
+
);
|
|
1085
|
+
return toContent(result);
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1068
1088
|
// --- Agent Memory Tools (US-28.4) ---
|
|
1069
1089
|
//
|
|
1070
1090
|
// memory_* is YOUR own scoped, private, accumulated working state — recall
|
|
@@ -1237,22 +1257,25 @@ async function knowledgeUnpublish({ article_id }) {
|
|
|
1237
1257
|
return toContent(result);
|
|
1238
1258
|
}
|
|
1239
1259
|
|
|
1260
|
+
// #331: single-article archive is agent-role KB curation (reversible soft delete,
|
|
1261
|
+
// audited, visibility-scoped server-side).
|
|
1240
1262
|
async function knowledgeArchive({ article_id }) {
|
|
1241
1263
|
const result = await apiCall(
|
|
1242
1264
|
"POST",
|
|
1243
1265
|
`/api/v1/articles/${article_id}/archive`,
|
|
1244
1266
|
null,
|
|
1245
|
-
process.env.
|
|
1267
|
+
process.env.LOOPCTL_AGENT_KEY
|
|
1246
1268
|
);
|
|
1247
1269
|
return toContent(result);
|
|
1248
1270
|
}
|
|
1249
1271
|
|
|
1272
|
+
// #331: soft-delete (archive) is agent-role KB curation, same as knowledge_archive.
|
|
1250
1273
|
async function knowledgeDelete({ article_id }) {
|
|
1251
1274
|
const result = await apiCall(
|
|
1252
1275
|
"DELETE",
|
|
1253
1276
|
`/api/v1/articles/${article_id}`,
|
|
1254
1277
|
null,
|
|
1255
|
-
process.env.
|
|
1278
|
+
process.env.LOOPCTL_AGENT_KEY
|
|
1256
1279
|
);
|
|
1257
1280
|
return toContent(result);
|
|
1258
1281
|
}
|
|
@@ -3157,6 +3180,54 @@ const TOOLS = [
|
|
|
3157
3180
|
required: ["title", "body"],
|
|
3158
3181
|
},
|
|
3159
3182
|
},
|
|
3183
|
+
{
|
|
3184
|
+
name: "knowledge_update",
|
|
3185
|
+
description:
|
|
3186
|
+
"Edit an EXISTING knowledge article IN PLACE, preserving its ID. Use this to fold a " +
|
|
3187
|
+
"new fact into a canonical article, tidy a hub, retag, or reclassify — WITHOUT " +
|
|
3188
|
+
"creating a new row and churning the article ID (IDs are load-bearing: cited in " +
|
|
3189
|
+
"project CLAUDE.mds and cross-links). Send only the fields you want to change; every " +
|
|
3190
|
+
"field is optional except article_id. `tags` REPLACES the whole array (send the full " +
|
|
3191
|
+
"desired set, not a delta). A changed body/tags re-triggers embedding + auto-linking. " +
|
|
3192
|
+
"Agent role — this is KB-content curation (reversible + audited). Visibility-scoped: " +
|
|
3193
|
+
"you can only edit an article you can see, so another agent's private/owner memory " +
|
|
3194
|
+
"returns 404. `tenant_id` is never accepted. Returns the full updated article. To " +
|
|
3195
|
+
"instead retire/replace an article, use knowledge_archive or knowledge_resolve_conflict.",
|
|
3196
|
+
inputSchema: {
|
|
3197
|
+
type: "object",
|
|
3198
|
+
properties: {
|
|
3199
|
+
article_id: {
|
|
3200
|
+
type: "string",
|
|
3201
|
+
description: "The UUID of the article to edit (preserved across the update).",
|
|
3202
|
+
},
|
|
3203
|
+
title: {
|
|
3204
|
+
type: "string",
|
|
3205
|
+
description: "Optional: new title.",
|
|
3206
|
+
},
|
|
3207
|
+
body: {
|
|
3208
|
+
type: "string",
|
|
3209
|
+
description: "Optional: new body (Markdown). Re-triggers embedding + linking.",
|
|
3210
|
+
},
|
|
3211
|
+
category: {
|
|
3212
|
+
type: "string",
|
|
3213
|
+
description: "Optional: new category.",
|
|
3214
|
+
},
|
|
3215
|
+
tags: {
|
|
3216
|
+
type: "array",
|
|
3217
|
+
items: { type: "string" },
|
|
3218
|
+
description: "Optional: REPLACES the whole tags array (send the full desired set).",
|
|
3219
|
+
},
|
|
3220
|
+
metadata: {
|
|
3221
|
+
type: "object",
|
|
3222
|
+
description:
|
|
3223
|
+
"Optional: extensible JSONB (merged/replaced wholesale). For an agent key, an " +
|
|
3224
|
+
"agent-memory `agent_id` is stamped from your verified key identity — you cannot " +
|
|
3225
|
+
"re-attribute a memory to another agent.",
|
|
3226
|
+
},
|
|
3227
|
+
},
|
|
3228
|
+
required: ["article_id"],
|
|
3229
|
+
},
|
|
3230
|
+
},
|
|
3160
3231
|
|
|
3161
3232
|
// Agent Memory Tools (US-28.4)
|
|
3162
3233
|
{
|
|
@@ -3421,9 +3492,10 @@ const TOOLS = [
|
|
|
3421
3492
|
name: "knowledge_archive",
|
|
3422
3493
|
description:
|
|
3423
3494
|
"Archive an article (soft delete). The article is hidden from search, context, " +
|
|
3424
|
-
"and the index but the row is retained for audit/history
|
|
3425
|
-
"
|
|
3426
|
-
"
|
|
3495
|
+
"and the index but the row is retained for audit/history (reversible — re-publish " +
|
|
3496
|
+
"or edit it back). Works for drafts and published articles. Agent role — KB-content " +
|
|
3497
|
+
"curation. Visibility-scoped: you can only archive an article you can see, so " +
|
|
3498
|
+
"another agent's private/owner memory returns 404.",
|
|
3427
3499
|
inputSchema: {
|
|
3428
3500
|
type: "object",
|
|
3429
3501
|
properties: {
|
|
@@ -3440,8 +3512,9 @@ const TOOLS = [
|
|
|
3440
3512
|
description:
|
|
3441
3513
|
"Delete an article. Under the hood this performs the same soft-delete (archive) " +
|
|
3442
3514
|
"as knowledge_archive — use whichever name is clearer at the call site. The row " +
|
|
3443
|
-
"is retained for audit; there is no hard delete
|
|
3444
|
-
"
|
|
3515
|
+
"is retained for audit; there is no hard delete (that is knowledge_bulk_delete " +
|
|
3516
|
+
"hard:true, which stays user-gated). Agent role — KB-content curation, reversible + " +
|
|
3517
|
+
"audited, visibility-scoped (another agent's private/owner memory 404s).",
|
|
3445
3518
|
inputSchema: {
|
|
3446
3519
|
type: "object",
|
|
3447
3520
|
properties: {
|
|
@@ -4544,6 +4617,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
4544
4617
|
case "knowledge_create":
|
|
4545
4618
|
return await knowledgeCreate(args);
|
|
4546
4619
|
|
|
4620
|
+
case "knowledge_update":
|
|
4621
|
+
return await knowledgeUpdate(args);
|
|
4622
|
+
|
|
4547
4623
|
// Agent Memory Tools
|
|
4548
4624
|
case "memory_remember":
|
|
4549
4625
|
return await memoryRemember(args);
|