loopctl-mcp-server 2.113.0 → 2.114.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 +1 -1
  2. package/index.js +13 -10
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -199,7 +199,7 @@ tool parameter.
199
199
  | `list_projects` | List all projects in the current tenant. |
200
200
  | `resolve_project` | Resolve a repo to its `project_id` in one cheap call — provide any of `slug`, `repo_url` (`git@github.com:owner/repo.git`, `https://github.com/owner/repo`, and bare `owner/repo` all match), or `name`. Precedence `slug > repo_url > name`, first match wins. Use the returned `id` to scope captures/recall (`memory_*`, `recall_context`). `404` `not_found` if nothing matches, `422` `no_identifier` if none supplied, `409` if a fuzzy identifier matches more than one active project. |
201
201
  | `create_project` | Create a new **work** project (epics/stories/custody). Requires orchestrator+ **and** a `human_anchored` tenant; an agent-rooted tenant gets `403 custody_tier_required` and should use `create_kb_scope`. |
202
- | `delete_project` | **Requires `LOOPCTL_USER_KEY`.** Delete a project and all of its dependent resources (epics, stories, audit entries). Irreversible — orchestrator role is not sufficient. |
202
+ | `delete_project` | **Needs a user-role key:** `LOOPCTL_USER_KEY` verbatim when set, else the default key (orchestrator role is 403 `insufficient_role`). Archives the project (status `archived`); no rows are removed and its slug stays taken. |
203
203
  | `get_progress` | Get progress summary for a project, including story counts by status. Pass `include_cost=true` for cost data. |
204
204
  | `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. With `merge: true`, `report_orphans: true` adds `stories_orphaned` (the project's stories the payload does not mention; nothing is detached) — a signal only on a FULL round-trip of an export; without it the key is absent (#880). |
205
205
 
package/index.js CHANGED
@@ -946,13 +946,16 @@ async function channelGraduate({ post_id, title, tags, category }) {
946
946
  return toContent(result);
947
947
  }
948
948
 
949
+ // Pinned like `userKeyApiCall` (LOOPCTL_USER_KEY verbatim when set, so a global
950
+ // LOOPCTL_API_KEY cannot displace it), spelled out so the route sweep sees the call.
949
951
  async function deleteProject({ project_id }) {
950
- const result = await apiCall(
951
- "DELETE",
952
- `/api/v1/projects/${project_id}`,
953
- null,
954
- process.env.LOOPCTL_USER_KEY
955
- );
952
+ const path = `/api/v1/projects/${project_id}`;
953
+ const key = process.env.LOOPCTL_USER_KEY;
954
+
955
+ const result = key
956
+ ? await apiCall("DELETE", path, null, key, { exactKey: true, keyHint: "LOOPCTL_USER_KEY" })
957
+ : await apiCall("DELETE", path, null);
958
+
956
959
  return toContent(result);
957
960
  }
958
961
 
@@ -1125,8 +1128,8 @@ async function getStory({ story_id }) {
1125
1128
  // Reads travel on the default key (any role passes). Create and update go through
1126
1129
  // `orchestratorPinnedApiCall`: the named orchestrator or user key VERBATIM, so a global
1127
1130
  // LOOPCTL_API_KEY of a lesser role cannot displace it, and the global key when neither is set. The deletes go through `userKeyApiCall`, pinned to LOOPCTL_USER_KEY so a
1128
- // global LOOPCTL_API_KEY cannot displace it: they destroy rows. (`delete_project` is not
1129
- // pinned this way.)
1131
+ // global LOOPCTL_API_KEY cannot displace it: they destroy rows. `delete_project` (an
1132
+ // archive) is pinned the same way, because its gate is `role: :user`.
1130
1133
 
1131
1134
  // ONE pinning rule for the epic and story writes: the env var `hint()` names is sent VERBATIM
1132
1135
  // when it is set, so a global LOOPCTL_API_KEY of a lesser role cannot displace it, and with none
@@ -4239,13 +4242,13 @@ const TOOLS = [
4239
4242
  {
4240
4243
  name: "delete_project",
4241
4244
  description:
4242
- "Delete a project and all of its dependent resources (epics, stories, audit entries scoped to it). REQUIRES LOOPCTL_USER_KEY to be set in the MCP server env (user role — orchestrator role is NOT sufficient for this destructive operation). The deletion is irreversible.",
4245
+ "ARCHIVE a project (DELETE /api/v1/projects/:id sets status to archived; it removes no rows). Its epics, stories and audit entries stay, and an already-archived project is a no-op. The SLUG stays taken: slugs are unique per tenant across archived projects too, resolve_project by that slug still returns the archived project, create_project with it is refused 422, and no API call changes or releases a slug, so a new project needs a different one. The way back: restore_kb_scope for a kb scope; a work project is restored by PATCH /api/v1/projects/:id with status active, which no MCP tool reaches yet. Sent with LOOPCTL_USER_KEY verbatim when it is set (a global LOOPCTL_API_KEY does not displace it), else with the default key. Refusals: 403 insufficient_role below user role, 403 custody_tier_required on an agent-rooted tenant (the human-anchor gate), 404 for an id not in your tenant.",
4243
4246
  inputSchema: {
4244
4247
  type: "object",
4245
4248
  properties: {
4246
4249
  project_id: {
4247
4250
  type: "string",
4248
- description: "The UUID of the project to delete.",
4251
+ description: "The UUID of the project to archive.",
4249
4252
  },
4250
4253
  },
4251
4254
  required: ["project_id"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loopctl-mcp-server",
3
- "version": "2.113.0",
3
+ "version": "2.114.0",
4
4
  "description": "MCP server for loopctl — structural trust for AI development loops",
5
5
  "type": "module",
6
6
  "main": "index.js",