loopctl-mcp-server 2.45.0 → 2.47.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 +25 -2
- package/index.js +114 -6
- 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 (95)
|
|
125
125
|
|
|
126
126
|
> Plus **per-tenant generated Context Retriever tools** (`cr_*`) appended
|
|
127
127
|
> dynamically at runtime — see [Dynamic per-tenant Context Retriever
|
|
@@ -139,6 +139,25 @@ autonomous agent) can self-remediate without a human. Full agent-tenant lifecycl
|
|
|
139
139
|
| `get_progress` | Get progress summary for a project, including story counts by status. Pass `include_cost=true` for cost data. |
|
|
140
140
|
| `import_stories` | Import stories into a project from a structured payload (Epic 12 import format). Pass `merge: true` to add stories to epics that already exist (otherwise duplicates return 409). For large payloads, use `payload_path` to read JSON from disk instead of passing it inline. |
|
|
141
141
|
|
|
142
|
+
### KB Scope Tools (agent key)
|
|
143
|
+
|
|
144
|
+
Knowledge-only project scopes (`kind: kb`) partition knowledge articles by repo. Unlike `create_project` (work project, orchestrator+ / human-anchored), these are available to an agent-rooted (KB-tier) tenant on an agent key and carry NO chain-of-custody / work-breakdown surface.
|
|
145
|
+
|
|
146
|
+
| Tool | Description |
|
|
147
|
+
|---|---|
|
|
148
|
+
| `create_kb_scope` | Create a knowledge-only project scope (`kind: kb`) for the current tenant. A kb scope cannot host epics/stories/dispatch/ui-tests — it exists only to partition knowledge articles by repo. Resolve it via `resolve_project` and pass the returned id as `project_id` on article/knowledge writes. Counts toward the tenant's `max_projects` budget. |
|
|
149
|
+
| `archive_kb_scope` | Archive (reversible soft-delete) a kb scope you own. Frees the scope's slot in the tenant's `max_projects` budget; its articles remain readable/writable. Rejects a `kind: work` project (422). Idempotent on an already-archived scope. |
|
|
150
|
+
| `restore_kb_scope` | Restore (re-activate) an archived kb scope you own — the reverse of `archive_kb_scope`. Consumes an active `max_projects` slot, so it is rejected (422) when the tenant is at its cap. Rejects a `kind: work` project (422). |
|
|
151
|
+
|
|
152
|
+
### Repo Coordination Tools (agent key)
|
|
153
|
+
|
|
154
|
+
Epic 39 Repo Coordination Bus — a lightweight, tenant-isolated channel for agents to share working state. A channel IS a `project_id` (a work project or a kb scope); posts are RLS-scoped to the caller's tenant, so this is an agent-role coordination surface, not a chain-of-custody gate.
|
|
155
|
+
|
|
156
|
+
| Tool | Description |
|
|
157
|
+
|---|---|
|
|
158
|
+
| `channel_post` | Post a message to a repo coordination channel. Provide a `key` to upsert your per-session working-state slot (200) instead of appending a new post (201); omit it to append. `host` and `session_id` are proxy-supplied — do NOT pass them. Optional structured `refs` map (`file`, `pr`, `branch`, `commit`). Required: `project_id`, `body`. |
|
|
159
|
+
| `channel_recent` | Read recent posts from a repo coordination channel — RLS returns only your own tenant's channel (oracle-safe read). Use `since` (a full ISO8601 instant) to page forward and `limit` to cap results (default 25, max 100). Required: `project_id`. |
|
|
160
|
+
|
|
142
161
|
### Story Tools
|
|
143
162
|
|
|
144
163
|
| Tool | Description |
|
|
@@ -188,7 +207,7 @@ autonomous agent) can self-remediate without a human. Full agent-tenant lifecycl
|
|
|
188
207
|
| `get_cost_summary` | orch | Get cost/token usage summary for a project, optionally broken down by `agent`, `epic`, or `model`. |
|
|
189
208
|
| `get_story_token_usage` | orch | Get all token usage records for a single story. |
|
|
190
209
|
| `get_cost_anomalies` | orch | Get cost anomaly alerts — stories or agents exceeding expected budgets. Optionally filter by project. |
|
|
191
|
-
| `get_ingestion_anomalies` | orch | Get ingestion-health anomalies —
|
|
210
|
+
| `get_ingestion_anomalies` | orch | Get ingestion-health anomalies — capture_silence (a source_type stopped producing articles) and high_reject_rate (writes rejected at high rate, persisting no article row). Check whether knowledge capture is still landing AND being accepted. |
|
|
192
211
|
| `set_token_budget` | orch | Set a token budget (in millicents) for a project, epic, story, or agent scope. Requires orchestrator role. |
|
|
193
212
|
|
|
194
213
|
### Knowledge Wiki Tools (agent key)
|
|
@@ -323,6 +342,10 @@ Key distribution for the dispatch pattern (Epic 26): per-dispatch ephemeral keys
|
|
|
323
342
|
| `dispatch` | Mint an ephemeral, scoped api_key for a sub-agent dispatch, carrying its lineage path. The `raw_key` is returned ONCE — pass it to the sub-agent's launch args, never store it in env vars; it expires after `expires_in_seconds` (default 3600, max 14400). Required: `role` (`agent`/`orchestrator`), `agent_id`. Optional: `parent_dispatch_id`, `story_id`. |
|
|
324
343
|
| `recover_cap` | Re-mint a capability token for a story you're assigned to, after a session crash lost your cap. Required: `story_id`. Optional: `cap_type` (`start_cap`/`report_cap`, default `start_cap`), `lineage`. |
|
|
325
344
|
| `get_sth` | Get the latest Signed Tree Head for a tenant's tamper-evident audit chain. Public — no auth required. Required: `tenant_id`. |
|
|
345
|
+
| `request_authenticator_challenge` | **US-26.7.2.** Step 1 of the opt-in WebAuthn trust-tier upgrade ceremony: issues a registration challenge for enrolling a hardware authenticator against an EXISTING agent-rooted (KB-tier) tenant, promoting it to `human_anchored` on success. Requires an interactive WebAuthn client. |
|
|
346
|
+
| `enroll_authenticator` | Step 2 of the WebAuthn trust-tier upgrade ceremony: completes enrollment with the attestation produced by `navigator.credentials.create()` against the challenge from `request_authenticator_challenge`. On a tenant's first enrollment the tenant is promoted to `human_anchored`. |
|
|
347
|
+
| `request_authenticator_revoke_challenge` | Issues a fresh-assertion challenge to authorize revoking one of a tenant's enrolled WebAuthn authenticators. Requires an interactive WebAuthn client + human touch to produce the assertion `revoke_authenticator` needs. |
|
|
348
|
+
| `revoke_authenticator` | Revokes an enrolled authenticator using the assertion from `request_authenticator_revoke_challenge` (`navigator.credentials.get()` against an existing authenticator — human touch required). Refuses (409 `last_authenticator`) when it would leave a human-anchored tenant with no authenticators. |
|
|
326
349
|
|
|
327
350
|
## Wiki Attribution
|
|
328
351
|
|
package/index.js
CHANGED
|
@@ -437,6 +437,46 @@ async function restoreKbScope({ project_id }) {
|
|
|
437
437
|
return toContent(result);
|
|
438
438
|
}
|
|
439
439
|
|
|
440
|
+
async function channelPost({ project_id, body, key, refs }) {
|
|
441
|
+
// Repo Coordination Bus (Epic 39): post a coordination message to a channel
|
|
442
|
+
// (a channel IS a project_id — a work project or a kb scope). Agent-role, RLS
|
|
443
|
+
// tenant-scoped — posting to your own tenant's channel is coordination, NOT
|
|
444
|
+
// self-approval (owner decision #331), so it carries no chain-of-custody authority.
|
|
445
|
+
const payload = { project_id, body };
|
|
446
|
+
if (key) payload.key = key;
|
|
447
|
+
if (refs) payload.refs = refs;
|
|
448
|
+
// host + session_id are proxy-filled (NOT caller args). host from os.hostname();
|
|
449
|
+
// session_id from CLAUDE_SESSION_ID (the SAME id SessionStart sees) so US-39.6
|
|
450
|
+
// self-dedup can skip a session's own echoed posts. Omit session_id entirely when
|
|
451
|
+
// unset — it is client-supplied + informational, never a security dependency.
|
|
452
|
+
payload.host = os.hostname();
|
|
453
|
+
if (process.env.CLAUDE_SESSION_ID) payload.session_id = process.env.CLAUDE_SESSION_ID;
|
|
454
|
+
const result = await apiCall(
|
|
455
|
+
"POST",
|
|
456
|
+
"/api/v1/channel/posts",
|
|
457
|
+
payload,
|
|
458
|
+
process.env.LOOPCTL_AGENT_KEY,
|
|
459
|
+
);
|
|
460
|
+
return toContent(result);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
async function channelRecent({ project_id, since, limit }) {
|
|
464
|
+
// Read recent coordination posts for a channel (project_id) on the AGENT key.
|
|
465
|
+
// Oracle-safe read: returns the tenant's own channel only (RLS). `since` is a full
|
|
466
|
+
// ISO8601 instant (date-only is ignored server-side); limit defaults to 25, max 100.
|
|
467
|
+
const params = new URLSearchParams();
|
|
468
|
+
if (project_id) params.set("project_id", project_id);
|
|
469
|
+
if (since) params.set("since", since);
|
|
470
|
+
if (limit) params.set("limit", limit);
|
|
471
|
+
const result = await apiCall(
|
|
472
|
+
"GET",
|
|
473
|
+
`/api/v1/channel/posts?${params}`,
|
|
474
|
+
null,
|
|
475
|
+
process.env.LOOPCTL_AGENT_KEY,
|
|
476
|
+
);
|
|
477
|
+
return toContent(result);
|
|
478
|
+
}
|
|
479
|
+
|
|
440
480
|
async function deleteProject({ project_id }) {
|
|
441
481
|
const result = await apiCall(
|
|
442
482
|
"DELETE",
|
|
@@ -2207,6 +2247,62 @@ const TOOLS = [
|
|
|
2207
2247
|
required: ["project_id"],
|
|
2208
2248
|
},
|
|
2209
2249
|
},
|
|
2250
|
+
{
|
|
2251
|
+
name: "channel_post",
|
|
2252
|
+
description:
|
|
2253
|
+
"Post a message to a repo coordination channel (Epic 39 Repo Coordination Bus) on the agent key. A channel IS a project_id (a work project or a kb scope); posts are tenant-isolated by RLS. This is an agent-role COORDINATION surface, not chain-of-custody — posting to your own tenant's channel is not self-approval. host is auto-filled from the proxy's os.hostname() and session_id is auto-filled from the Claude Code session id (both proxy-supplied, informational only — do NOT pass them). Provide a key to upsert your per-session working-state slot (200) instead of appending a new post (201); omit it to append.",
|
|
2254
|
+
inputSchema: {
|
|
2255
|
+
type: "object",
|
|
2256
|
+
properties: {
|
|
2257
|
+
project_id: {
|
|
2258
|
+
type: "string",
|
|
2259
|
+
description: "UUID of the channel (a work project or kb scope) to post to.",
|
|
2260
|
+
},
|
|
2261
|
+
body: { type: "string", description: "The coordination message body." },
|
|
2262
|
+
key: {
|
|
2263
|
+
type: "string",
|
|
2264
|
+
description:
|
|
2265
|
+
"Optional per-session working-state slot key. When given, upserts the caller's slot for that key instead of appending a new post. Requires an active Claude Code session: the upsert is keyed on the auto-filled session_id (from CLAUDE_SESSION_ID), so a keyed post made outside a Claude Code session — where that env var is absent — is rejected with a 422 (session_id can't be blank). Omit key to append a plain post, which needs no session.",
|
|
2266
|
+
},
|
|
2267
|
+
refs: {
|
|
2268
|
+
type: "object",
|
|
2269
|
+
description:
|
|
2270
|
+
"Optional structured references map: { file, pr, branch, commit }.",
|
|
2271
|
+
properties: {
|
|
2272
|
+
file: { type: "string" },
|
|
2273
|
+
pr: { type: "string" },
|
|
2274
|
+
branch: { type: "string" },
|
|
2275
|
+
commit: { type: "string" },
|
|
2276
|
+
},
|
|
2277
|
+
},
|
|
2278
|
+
},
|
|
2279
|
+
required: ["project_id", "body"],
|
|
2280
|
+
},
|
|
2281
|
+
},
|
|
2282
|
+
{
|
|
2283
|
+
name: "channel_recent",
|
|
2284
|
+
description:
|
|
2285
|
+
"Read recent posts from a repo coordination channel (Epic 39 Repo Coordination Bus) on the agent key. A channel IS a project_id; RLS returns only your own tenant's channel, so this is an oracle-safe read. Use since (a full ISO8601 instant) to page forward from a known point and limit to cap results (default 25, max 100).",
|
|
2286
|
+
inputSchema: {
|
|
2287
|
+
type: "object",
|
|
2288
|
+
properties: {
|
|
2289
|
+
project_id: {
|
|
2290
|
+
type: "string",
|
|
2291
|
+
description: "UUID of the channel (a work project or kb scope) to read.",
|
|
2292
|
+
},
|
|
2293
|
+
since: {
|
|
2294
|
+
type: "string",
|
|
2295
|
+
description:
|
|
2296
|
+
"Optional ISO8601 instant; return posts newer than this (date-only is ignored server-side).",
|
|
2297
|
+
},
|
|
2298
|
+
limit: {
|
|
2299
|
+
type: "integer",
|
|
2300
|
+
description: "Optional max posts to return (default 25, max 100).",
|
|
2301
|
+
},
|
|
2302
|
+
},
|
|
2303
|
+
required: ["project_id"],
|
|
2304
|
+
},
|
|
2305
|
+
},
|
|
2210
2306
|
{
|
|
2211
2307
|
name: "delete_project",
|
|
2212
2308
|
description:
|
|
@@ -2783,10 +2879,12 @@ const TOOLS = [
|
|
|
2783
2879
|
{
|
|
2784
2880
|
name: "get_ingestion_anomalies",
|
|
2785
2881
|
description:
|
|
2786
|
-
"Get ingestion-health anomalies —
|
|
2787
|
-
"articles has gone silent)
|
|
2788
|
-
"
|
|
2789
|
-
"
|
|
2882
|
+
"Get ingestion-health anomalies — capture_silence (a source_type that was producing " +
|
|
2883
|
+
"articles has gone silent) and high_reject_rate (writes attempted but rejected at high " +
|
|
2884
|
+
"rate — 409 title_conflict / validation drops that persist no article row). Use to check " +
|
|
2885
|
+
"whether knowledge capture is still landing AND being accepted. Paginated (page/page_size); " +
|
|
2886
|
+
"advance `page` to enumerate all. Filter by source_type, anomaly_type, resolved status, or " +
|
|
2887
|
+
"include archived.",
|
|
2790
2888
|
inputSchema: {
|
|
2791
2889
|
type: "object",
|
|
2792
2890
|
properties: {
|
|
@@ -2796,8 +2894,12 @@ const TOOLS = [
|
|
|
2796
2894
|
},
|
|
2797
2895
|
anomaly_type: {
|
|
2798
2896
|
type: "string",
|
|
2799
|
-
|
|
2800
|
-
|
|
2897
|
+
// Keep in sync with Loopctl.Knowledge.IngestionAnomaly @anomaly_types (the
|
|
2898
|
+
// server-side Ecto.Enum + the ingestion_anomalies_anomaly_type_check DB CHECK).
|
|
2899
|
+
enum: ["capture_silence", "high_reject_rate"],
|
|
2900
|
+
description:
|
|
2901
|
+
'Optional: filter by anomaly type — "capture_silence" (writes stopped) or ' +
|
|
2902
|
+
'"high_reject_rate" (writes rejected at high rate).',
|
|
2801
2903
|
},
|
|
2802
2904
|
resolved: {
|
|
2803
2905
|
type: "string",
|
|
@@ -5011,6 +5113,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
5011
5113
|
case "restore_kb_scope":
|
|
5012
5114
|
return await restoreKbScope(args);
|
|
5013
5115
|
|
|
5116
|
+
case "channel_post":
|
|
5117
|
+
return await channelPost(args);
|
|
5118
|
+
|
|
5119
|
+
case "channel_recent":
|
|
5120
|
+
return await channelRecent(args);
|
|
5121
|
+
|
|
5014
5122
|
case "delete_project":
|
|
5015
5123
|
return await deleteProject(args);
|
|
5016
5124
|
|