loopctl-mcp-server 1.1.0 → 1.1.2

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 (2) hide show
  1. package/index.js +25 -12
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -116,10 +116,12 @@ function toContent(result) {
116
116
  }
117
117
 
118
118
  /**
119
- * Compact variant for list endpoints — strips bulky fields (acceptance_criteria,
120
- * description) from each story to stay within token limits. Callers can use
121
- * get_story for full details on individual stories.
119
+ * Compact variant for list endpoints — strips acceptance_criteria and
120
+ * description (use get_story for full details). Keeps all other fields.
121
+ * Enforces a max page size to prevent MCP response token overflow.
122
122
  */
123
+ const MAX_PAGE_SIZE = 20;
124
+
123
125
  function toContentCompact(result) {
124
126
  if (result && result.error === true) return toContent(result);
125
127
 
@@ -191,7 +193,7 @@ async function listStories({ project_id, agent_status, verified_status, epic_id,
191
193
  if (agent_status) params.set("agent_status", agent_status);
192
194
  if (verified_status) params.set("verified_status", verified_status);
193
195
  if (epic_id) params.set("epic_id", epic_id);
194
- params.set("limit", String(limit ?? 20));
196
+ params.set("limit", String(Math.min(limit ?? MAX_PAGE_SIZE, MAX_PAGE_SIZE)));
195
197
  if (offset != null) params.set("offset", String(offset));
196
198
  if (include_token_totals) params.set("include_token_totals", "true");
197
199
 
@@ -201,7 +203,7 @@ async function listStories({ project_id, agent_status, verified_status, epic_id,
201
203
 
202
204
  async function listReadyStories({ project_id, limit }) {
203
205
  const params = new URLSearchParams({ project_id });
204
- params.set("limit", String(limit ?? 20));
206
+ params.set("limit", String(Math.min(limit ?? MAX_PAGE_SIZE, MAX_PAGE_SIZE)));
205
207
 
206
208
  const result = await apiCall("GET", `/api/v1/stories/ready?${params}`);
207
209
  return toContentCompact(result);
@@ -359,10 +361,18 @@ async function reportTokenUsage({ story_id, input_tokens, output_tokens, model_n
359
361
  }
360
362
 
361
363
  async function getCostSummary({ project_id, breakdown }) {
362
- const params = new URLSearchParams({ project_id });
363
- if (breakdown) params.set("breakdown", breakdown);
364
+ let path;
365
+ if (breakdown === "agent") {
366
+ path = `/api/v1/analytics/agents?project_id=${project_id}`;
367
+ } else if (breakdown === "epic") {
368
+ path = `/api/v1/analytics/epics?project_id=${project_id}`;
369
+ } else if (breakdown === "model") {
370
+ path = `/api/v1/analytics/models?project_id=${project_id}`;
371
+ } else {
372
+ path = `/api/v1/analytics/projects/${project_id}`;
373
+ }
364
374
 
365
- const result = await apiCall("GET", `/api/v1/projects/${project_id}/cost-summary?${params}`);
375
+ const result = await apiCall("GET", path);
366
376
  return toContent(result);
367
377
  }
368
378
 
@@ -482,7 +492,8 @@ const TOOLS = [
482
492
  description:
483
493
  "List stories for a project, optionally filtered by agent_status, verified_status, or epic_id. " +
484
494
  "Returns compact results (no acceptance_criteria/description) — use get_story for full details. " +
485
- "Defaults to 20 stories per page; use limit/offset to paginate.",
495
+ "Max 20 per page. Use offset to paginate (response includes total_count). " +
496
+ "Filter by epic_id or agent_status to reduce result size.",
486
497
  inputSchema: {
487
498
  type: "object",
488
499
  properties: {
@@ -523,7 +534,8 @@ const TOOLS = [
523
534
  name: "list_ready_stories",
524
535
  description:
525
536
  "List stories that are ready to be worked on (contracted, dependencies met). " +
526
- "Returns compact results — use get_story for full details. Defaults to 20 per page.",
537
+ "Returns compact results — use get_story for full details. " +
538
+ "Max 20 per page. Response includes total_count for pagination.",
527
539
  inputSchema: {
528
540
  type: "object",
529
541
  properties: {
@@ -840,7 +852,8 @@ const TOOLS = [
840
852
  },
841
853
  phase: {
842
854
  type: "string",
843
- description: "Optional: phase of work (e.g. implement, review, verify).",
855
+ enum: ["planning", "implementing", "reviewing", "other"],
856
+ description: "Optional: phase of work.",
844
857
  },
845
858
  skill_version_id: {
846
859
  type: "string",
@@ -956,7 +969,7 @@ const TOOLS = [
956
969
  const server = new Server(
957
970
  {
958
971
  name: "loopctl",
959
- version: "1.1.0",
972
+ version: "1.1.2",
960
973
  },
961
974
  {
962
975
  capabilities: { tools: {} },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loopctl-mcp-server",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "MCP server for loopctl — structural trust for AI development loops",
5
5
  "type": "module",
6
6
  "main": "index.js",