loopctl-mcp-server 2.62.0 → 2.64.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.
Files changed (3) hide show
  1. package/README.md +2 -2
  2. package/index.js +44 -6
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -255,7 +255,7 @@ Epic 39 Repo Coordination Bus — a lightweight, tenant-isolated channel for age
255
255
  | `knowledge_progressive_index` | Progressive disclosure — a **cheap, capped index** of what's relevant to a topic (compact stubs: `id`/`title`/`category`/`summary`, **no bodies**), curated-preferred and hub-enriched, capped at top-K (`meta.truncated` when the pool exceeded it). Survey a topic without flooding context, then open only what you need via `knowledge_progressive_drill`. Required: `topic`. Optional: `category`, `limit`. |
256
256
  | `knowledge_progressive_drill` | Open one stub from `knowledge_progressive_index` — returns the **full article body** for the given id, scope-enforced. Resolves both tenant-owned articles and published system canonicals (the same set the index surfaces). Required: `article_id`. |
257
257
  | `knowledge_list` | List articles (`id`, `title`, `category`, `status`, `tags`, `source_type`, `source_id`, `idempotency_key`, timestamps), filtered + paginated. **Body-less summary by default** (safe to page up to `limit=1000`); pass `include_body: true` to also return `body`, in which case the page is bounded by a ~5 MB byte budget — continue via `meta.next_offset` while `meta.has_more`. **Lag-free, all-status** read of the DB of record — unlike `knowledge_search` (ranked, published-only, lags writes) and `knowledge_index` (id/title/category only). The right tool to enumerate/dedup/repair and for idempotency/existence checks: filter by `tags`, `source_type`+`source_id`, or `idempotency_key` and read `meta.total_count` (exact). Single full body → `knowledge_get`; relevant bodies → `knowledge_context`; bulk dump → `knowledge_export`. Optional: `project_id`, `category`, `status`, `tags`, `source_type`, `source_id`, `idempotency_key`, `offset`, `limit`, `include_body`. |
258
- | `knowledge_get` | Get full article content by ID. Use after search to read an article in detail. Optional: `project_id`, `story_id` for attribution. |
258
+ | `knowledge_get` | Get full article content by ID. Use after search to read an article in detail. Each link carries only its FAR side (`article: {id, title}`, plus `similarity` when scored); both arrays are ranked (open conflicts first, then descending similarity, then oldest-first for the unscored) and capped at 25 per direction, with `links_total` / `links_truncated` reporting the truth (`count` returns both, so one cheap call tells you whether the full fetch is capped). Pass `links: "count"` or `"none"` when you only want the text — on a well-linked hub the link block is several times the body. `potential_conflicts` is returned in all three modes, itself capped at 25 with `conflicts_total` / `conflicts_truncated`. Optional: `links`, `project_id`, `story_id` for attribution. |
259
259
  | `knowledge_context` | Get relevance-and-recency-ranked full articles for a task query. Best knowledge for your current context. **Agent-memory scoping**: `memory_types` (comma-separated, OR — observation/finding/summary/decision/question/task), `agents` (comma-separated agent_ids, OR), `conversation_id` (exact) filter on article `metadata` (JSONB `@>`). Optional: `project_id`, `story_id` for attribution, `limit`, `recency_weight`. |
260
260
  | `knowledge_graph` | Multi-hop traversal of the published article-link graph from `article_id` (depth 1–3, default 1), **bounded to agent's visible articles**. Agent callers see only their own and `shared` articles. Bidirectional, cycle-safe, bounded to 100 nodes / 500 edges (`truncated` flags a cap). Returns `nodes` (`id`/`title`/`category`/`depth`) + `edges` (`source_article_id`/`target_article_id`/`relationship_type`). Explore typed connections beyond `knowledge_context`'s 1-hop links. Required: `article_id`. Optional: `depth`, `project_id`. |
261
261
  | `knowledge_suggest_links` | Ranked typed-link **candidates** for an article by embedding similarity among **visible articles** — **read-only** (creates nothing). Excludes the article itself + any already-linked article (either direction, any type); only embedded published articles visible to the caller. Agent callers see only their own and `shared` articles. Returns `{id, title, category, similarity_score}` highest-first, to create as a **typed** link (relates_to/derived_from/contradicts/supersedes). Required: `article_id`. Optional: `threshold` (cosine floor 0–1, default 0.5), `limit` (default 5). |
@@ -323,7 +323,7 @@ it is enforced server-side and a no-op for a non-superadmin key — see below.)
323
323
  | `knowledge_article_stats` | Per-article usage stats: total accesses, unique agents, by-type breakdown, recent events. Required: `article_id`. |
324
324
  | `knowledge_agent_usage` | Per-agent knowledge usage: total reads, unique articles, top read articles. Required: exactly one of `api_key_id` (credential) or `agent_id` (logical identity). Optional: `limit`, `since_days`. See Wiki Attribution section. |
325
325
  | `knowledge_unused_articles` | Published articles with zero accesses in the window. Optional: `days_unused` (default 30), `limit` (default 50, max 200). |
326
- | `knowledge_curation_log` | Concise human-readable log of KB CURATION adjustments — novelty-gate decisions (`gate_duplicate`/`gate_draft`) and conflict resolutions (`supersede`/`merge`/`dismiss`) — for analyzing the agents'-KB rollout, distinct from the verbose audit log. Each entry: `{at, kind, summary, refs, actor, confidence}`. **RECORDED ONLY when `settings.kb_curation_log` is on** (PATCH `/api/v1/admin/tenants/:id` with `settings:{kb_curation_log:true}`); off by default = no rows. Most recent first. Requires orchestrator role. Optional: `kind`, `since` (ISO8601), `limit` (default 50, max 500), `offset`. |
326
+ | `knowledge_curation_log` | Concise human-readable log of KB CURATION adjustments — novelty-gate decisions (`gate_duplicate`/`gate_draft`/`gate_skip`) and conflict resolutions (`supersede`/`merge`/`dismiss`) — for analyzing the agents'-KB rollout, distinct from the verbose audit log. Each entry: `{at, kind, summary, refs, actor, confidence}`. **RECORDED ONLY when `settings.kb_curation_log` is on** (PATCH `/api/v1/admin/tenants/:id` with `settings:{kb_curation_log:true}`); off by default = no rows. Most recent first. Requires orchestrator role. Optional: `kind`, `since` (ISO8601), `limit` (default 50, max 500), `offset`. |
327
327
  | `knowledge_retrieval_metrics` | Daily retrieval-PRECISION time series: for each day, the share of search results the agent then opened (search → get/context within a window). A proxy for whether retrieval is improving as the corpus is de-duplicated, better navigated (MOCs), and conflict-resolved. Most recent day first. Requires orchestrator role. Optional: `limit` (default 30, max 365), `offset`. |
328
328
 
329
329
  ### Egress / Privacy Tools (US-41.4)
package/index.js CHANGED
@@ -1461,10 +1461,11 @@ async function knowledgeList({
1461
1461
  return toContent(result);
1462
1462
  }
1463
1463
 
1464
- async function knowledgeGet({ article_id, project_id, story_id }) {
1464
+ async function knowledgeGet({ article_id, project_id, story_id, links }) {
1465
1465
  const params = new URLSearchParams();
1466
1466
  if (project_id) params.set("project_id", project_id);
1467
1467
  if (story_id) params.set("story_id", story_id);
1468
+ if (links) params.set("links", links);
1468
1469
  const qs = params.toString();
1469
1470
  const path = qs ? `/api/v1/articles/${article_id}?${qs}` : `/api/v1/articles/${article_id}`;
1470
1471
  const result = await apiCall("GET", path, null, process.env.LOOPCTL_AGENT_KEY);
@@ -1504,6 +1505,7 @@ async function knowledgeCreate({
1504
1505
  project_id,
1505
1506
  draft,
1506
1507
  force,
1508
+ skip_low_novelty,
1507
1509
  source_type,
1508
1510
  source_id,
1509
1511
  idempotency_key,
@@ -1525,8 +1527,10 @@ async function knowledgeCreate({
1525
1527
  if (draft) payload.draft = true;
1526
1528
 
1527
1529
  // The server-side novelty gate dedups the proposal against the corpus (verdict in
1528
- // the response `gate`). force:true bypasses it.
1530
+ // the response `gate`). force:true bypasses it; skip_low_novelty:true drops a
1531
+ // high-overlap proposal instead of banking it as a draft. Mutually exclusive (422).
1529
1532
  if (force) payload.force = true;
1533
+ if (skip_low_novelty) payload.skip_low_novelty = true;
1530
1534
 
1531
1535
  const result = await apiCall(
1532
1536
  "POST",
@@ -4508,7 +4512,20 @@ const TOOLS = [
4508
4512
  "material to your current task, act on it: read the peer, judge redundant/complementary/" +
4509
4513
  "contradictory against the live system, and knowledge_resolve_conflict (dismiss a false " +
4510
4514
  "positive, supersede when one clearly wins, merge when both should combine). If you can't " +
4511
- "tell which is right, leave it. See the 'Resolving knowledge conflicts' wiki playbook.",
4515
+ "tell which is right, leave it. See the 'Resolving knowledge conflicts' wiki playbook.\n\n" +
4516
+ "LINKS: each link carries only its FAR side as `article: {id, title}` (plus " +
4517
+ "`similarity` when the auto-linker scored it) — direction is already given by which " +
4518
+ "array it is in. Both arrays are ranked (open conflicts first, then descending " +
4519
+ "similarity, then oldest-first for the unscored) and capped at 25 per direction; " +
4520
+ "read `links_total` for the true count and `links_truncated` to know the cap bit — " +
4521
+ "both are returned by links: 'count' too, so one cheap call tells you whether the " +
4522
+ "full fetch is even complete. When you only want the article's TEXT, " +
4523
+ "pass links: 'count' (or 'none') — on a well-linked hub the link block is several " +
4524
+ "times the size of the body, and you are paying for it on every read. " +
4525
+ "`potential_conflicts` is returned in all three modes, so opting out of the link " +
4526
+ "list never hides a conflict from you; it is capped at 25 (strongest first) with " +
4527
+ "`conflicts_total` / `conflicts_truncated`. To actually traverse the graph, use " +
4528
+ "knowledge_graph rather than raising this cap.",
4512
4529
  inputSchema: {
4513
4530
  type: "object",
4514
4531
  properties: {
@@ -4517,6 +4534,15 @@ const TOOLS = [
4517
4534
  format: "uuid",
4518
4535
  description: "The UUID of the article.",
4519
4536
  },
4537
+ links: {
4538
+ type: "string",
4539
+ enum: ["full", "count", "none"],
4540
+ description:
4541
+ "Optional: how much of the link graph to return. 'full' (default) = ranked, " +
4542
+ "capped arrays; 'count' = just links_total + links_truncated; 'none' = omit " +
4543
+ "link fields. potential_conflicts (capped, with conflicts_total) is always " +
4544
+ "returned.",
4545
+ },
4520
4546
  project_id: {
4521
4547
  type: "string",
4522
4548
  format: "uuid",
@@ -4601,7 +4627,9 @@ const TOOLS = [
4601
4627
  "so NOTHING was created (HTTP 200, `deduplicated: true`); read/update the article at `data.id` " +
4602
4628
  "instead (its `gate.similarity` ~1.0). `gated_to_draft` means high overlap, so the article was " +
4603
4629
  "created as a DRAFT (not published) with the near-neighbors in metadata.proposal_novelty for you " +
4604
- "to merge or publish. `created` means it was novel and went through normally. Pass force: true to " +
4630
+ "to merge or publish unless you passed skip_low_novelty: true, in which case the verdict is " +
4631
+ "`skipped_low_novelty` and NOTHING was created (HTTP 200, `skipped: true`, `data: null`). " +
4632
+ "`created` means it was novel and went through normally. Pass force: true to " +
4605
4633
  "bypass the gate when you intentionally want an article near an existing one. " +
4606
4634
  "Concurrency-safe: if a create races/retries against an " +
4607
4635
  "existing article with the same title AND an identical body (ignoring surrounding whitespace), the " +
@@ -4645,6 +4673,15 @@ const TOOLS = [
4645
4673
  "semantic dedup and creates on the requested path even if a near-duplicate exists. " +
4646
4674
  "Use only when you've already checked and intend an article close to an existing one.",
4647
4675
  },
4676
+ skip_low_novelty: {
4677
+ type: "boolean",
4678
+ description:
4679
+ "Optional: on high overlap, create NOTHING instead of staging a draft (default " +
4680
+ "false → gated_to_draft). For an UNATTENDED writer with no reviewer behind it, " +
4681
+ "whose gated drafts would pile up unresolved; you get verdict skipped_low_novelty " +
4682
+ "with data:null. Mutually exclusive with force (422). An idempotency_key match or " +
4683
+ "an exact title collision is still answered as a dedup/409, never dropped.",
4684
+ },
4648
4685
  idempotency_key: {
4649
4686
  type: "string",
4650
4687
  description:
@@ -5905,7 +5942,7 @@ const TOOLS = [
5905
5942
  name: "knowledge_curation_log",
5906
5943
  description:
5907
5944
  "The concise, human-readable log of KB CURATION adjustments — novelty-gate decisions " +
5908
- "(gate_duplicate/gate_draft) and conflict resolutions (supersede/merge/dismiss) — for " +
5945
+ "(gate_duplicate/gate_draft/gate_skip) and conflict resolutions (supersede/merge/dismiss) — for " +
5909
5946
  "analyzing the agents'-KB rollout, distinct from the verbose audit log. Each entry is a " +
5910
5947
  "one-liner: {at, kind, summary, refs, actor, confidence}. RECORDED ONLY while the tenant " +
5911
5948
  "has the toggle on: settings.kb_curation_log (flip via the admin tenant API, " +
@@ -5917,7 +5954,8 @@ const TOOLS = [
5917
5954
  kind: {
5918
5955
  type: "string",
5919
5956
  description:
5920
- "Optional: filter by kind (gate_duplicate | gate_draft | supersede | merge | dismiss).",
5957
+ "Optional: filter by kind (gate_duplicate | gate_draft | gate_skip | supersede | merge | dismiss). " +
5958
+ "gate_skip = a high-overlap proposal DISCARDED under skip_low_novelty (dropped, not stored).",
5921
5959
  },
5922
5960
  since: {
5923
5961
  type: "string",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loopctl-mcp-server",
3
- "version": "2.62.0",
3
+ "version": "2.64.0",
4
4
  "description": "MCP server for loopctl \u2014 structural trust for AI development loops",
5
5
  "type": "module",
6
6
  "main": "index.js",