loopctl-mcp-server 2.46.0 → 2.48.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 +24 -1
- package/index.js +135 -0
- 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 |
|
|
@@ -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,61 @@ 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
|
+
|
|
480
|
+
async function channelDelete({ post_id }) {
|
|
481
|
+
// Repo Coordination Bus (Epic 39, US-39.7): HARD-delete a coordination post in
|
|
482
|
+
// the caller's tenant — the redact path for a leaked/regretted post, before its
|
|
483
|
+
// 30-day TTL. Agent-role, tenant-scoped: any agent in the tenant may delete any
|
|
484
|
+
// post in that tenant; a foreign or nonexistent id returns a 404 (no cross-tenant
|
|
485
|
+
// existence oracle).
|
|
486
|
+
const result = await apiCall(
|
|
487
|
+
"DELETE",
|
|
488
|
+
`/api/v1/channel/posts/${post_id}`,
|
|
489
|
+
null,
|
|
490
|
+
process.env.LOOPCTL_AGENT_KEY,
|
|
491
|
+
);
|
|
492
|
+
return toContent(result);
|
|
493
|
+
}
|
|
494
|
+
|
|
440
495
|
async function deleteProject({ project_id }) {
|
|
441
496
|
const result = await apiCall(
|
|
442
497
|
"DELETE",
|
|
@@ -2207,6 +2262,77 @@ const TOOLS = [
|
|
|
2207
2262
|
required: ["project_id"],
|
|
2208
2263
|
},
|
|
2209
2264
|
},
|
|
2265
|
+
{
|
|
2266
|
+
name: "channel_post",
|
|
2267
|
+
description:
|
|
2268
|
+
"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.",
|
|
2269
|
+
inputSchema: {
|
|
2270
|
+
type: "object",
|
|
2271
|
+
properties: {
|
|
2272
|
+
project_id: {
|
|
2273
|
+
type: "string",
|
|
2274
|
+
description: "UUID of the channel (a work project or kb scope) to post to.",
|
|
2275
|
+
},
|
|
2276
|
+
body: { type: "string", description: "The coordination message body." },
|
|
2277
|
+
key: {
|
|
2278
|
+
type: "string",
|
|
2279
|
+
description:
|
|
2280
|
+
"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.",
|
|
2281
|
+
},
|
|
2282
|
+
refs: {
|
|
2283
|
+
type: "object",
|
|
2284
|
+
description:
|
|
2285
|
+
"Optional structured references map: { file, pr, branch, commit }.",
|
|
2286
|
+
properties: {
|
|
2287
|
+
file: { type: "string" },
|
|
2288
|
+
pr: { type: "string" },
|
|
2289
|
+
branch: { type: "string" },
|
|
2290
|
+
commit: { type: "string" },
|
|
2291
|
+
},
|
|
2292
|
+
},
|
|
2293
|
+
},
|
|
2294
|
+
required: ["project_id", "body"],
|
|
2295
|
+
},
|
|
2296
|
+
},
|
|
2297
|
+
{
|
|
2298
|
+
name: "channel_recent",
|
|
2299
|
+
description:
|
|
2300
|
+
"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).",
|
|
2301
|
+
inputSchema: {
|
|
2302
|
+
type: "object",
|
|
2303
|
+
properties: {
|
|
2304
|
+
project_id: {
|
|
2305
|
+
type: "string",
|
|
2306
|
+
description: "UUID of the channel (a work project or kb scope) to read.",
|
|
2307
|
+
},
|
|
2308
|
+
since: {
|
|
2309
|
+
type: "string",
|
|
2310
|
+
description:
|
|
2311
|
+
"Optional ISO8601 instant; return posts newer than this (date-only is ignored server-side).",
|
|
2312
|
+
},
|
|
2313
|
+
limit: {
|
|
2314
|
+
type: "integer",
|
|
2315
|
+
description: "Optional max posts to return (default 25, max 100).",
|
|
2316
|
+
},
|
|
2317
|
+
},
|
|
2318
|
+
required: ["project_id"],
|
|
2319
|
+
},
|
|
2320
|
+
},
|
|
2321
|
+
{
|
|
2322
|
+
name: "channel_delete",
|
|
2323
|
+
description:
|
|
2324
|
+
"Delete a post from a repo coordination channel (Epic 39 Repo Coordination Bus) on the agent key — the redact path (US-39.7). Use this to immediately remove a leaked or regretted post (e.g. one that slipped a secret past the denylist) before its 30-day TTL. Agent-role, tenant-scoped: any agent in your tenant may delete any post in that tenant, enabling cleanup by whoever notices the leak. A post that does not exist in your tenant (including one in another tenant) returns a 404 — no cross-tenant existence oracle. The deletion is hard (the row is gone) but audited.",
|
|
2325
|
+
inputSchema: {
|
|
2326
|
+
type: "object",
|
|
2327
|
+
properties: {
|
|
2328
|
+
post_id: {
|
|
2329
|
+
type: "string",
|
|
2330
|
+
description: "UUID of the channel post to delete (must be in your tenant).",
|
|
2331
|
+
},
|
|
2332
|
+
},
|
|
2333
|
+
required: ["post_id"],
|
|
2334
|
+
},
|
|
2335
|
+
},
|
|
2210
2336
|
{
|
|
2211
2337
|
name: "delete_project",
|
|
2212
2338
|
description:
|
|
@@ -5017,6 +5143,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
5017
5143
|
case "restore_kb_scope":
|
|
5018
5144
|
return await restoreKbScope(args);
|
|
5019
5145
|
|
|
5146
|
+
case "channel_post":
|
|
5147
|
+
return await channelPost(args);
|
|
5148
|
+
|
|
5149
|
+
case "channel_recent":
|
|
5150
|
+
return await channelRecent(args);
|
|
5151
|
+
|
|
5152
|
+
case "channel_delete":
|
|
5153
|
+
return await channelDelete(args);
|
|
5154
|
+
|
|
5020
5155
|
case "delete_project":
|
|
5021
5156
|
return await deleteProject(args);
|
|
5022
5157
|
|