loopctl-mcp-server 2.22.0 → 2.24.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 (2) hide show
  1. package/index.js +82 -26
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -178,8 +178,12 @@ async function getTenant() {
178
178
  return toContent(result);
179
179
  }
180
180
 
181
- async function listProjects() {
182
- const result = await apiCall("GET", "/api/v1/projects");
181
+ async function listProjects({ page, page_size } = {}) {
182
+ const params = new URLSearchParams();
183
+ if (page != null) params.set("page", String(page));
184
+ if (page_size != null) params.set("page_size", String(page_size));
185
+ const query = params.toString() ? `?${params}` : "";
186
+ const result = await apiCall("GET", `/api/v1/projects${query}`);
183
187
  return toContent(result);
184
188
  }
185
189
 
@@ -381,12 +385,13 @@ async function listStories({ project_id, agent_status, verified_status, epic_id,
381
385
  return toContentCompact(result);
382
386
  }
383
387
 
384
- async function listReadyStories({ project_id, limit }) {
388
+ async function listReadyStories({ project_id, page, page_size }) {
389
+ // /stories/ready paginates by page/page_size — the old `limit` param was
390
+ // silently ignored by the server, capping callers at the first page.
385
391
  const params = new URLSearchParams({ project_id });
386
- params.set(
387
- "limit",
388
- String(Math.min(limit ?? DEFAULT_STORY_PAGE_SIZE, SERVER_MAX_STORY_PAGE_SIZE)),
389
- );
392
+ if (page != null) params.set("page", String(page));
393
+ if (page_size != null)
394
+ params.set("page_size", String(Math.min(page_size, SERVER_MAX_STORY_PAGE_SIZE)));
390
395
 
391
396
  const result = await apiCall("GET", `/api/v1/stories/ready?${params}`);
392
397
  return toContentCompact(result);
@@ -559,14 +564,20 @@ async function getCostSummary({ project_id, breakdown }) {
559
564
  return toContent(result);
560
565
  }
561
566
 
562
- async function getStoryTokenUsage({ story_id }) {
563
- const result = await apiCall("GET", `/api/v1/stories/${story_id}/token-usage`);
567
+ async function getStoryTokenUsage({ story_id, page, page_size }) {
568
+ const params = new URLSearchParams();
569
+ if (page != null) params.set("page", String(page));
570
+ if (page_size != null) params.set("page_size", String(page_size));
571
+ const query = params.toString() ? `?${params}` : "";
572
+ const result = await apiCall("GET", `/api/v1/stories/${story_id}/token-usage${query}`);
564
573
  return toContent(result);
565
574
  }
566
575
 
567
- async function getCostAnomalies({ project_id }) {
576
+ async function getCostAnomalies({ project_id, page, page_size }) {
568
577
  const params = new URLSearchParams();
569
578
  if (project_id) params.set("project_id", project_id);
579
+ if (page != null) params.set("page", String(page));
580
+ if (page_size != null) params.set("page_size", String(page_size));
570
581
 
571
582
  const query = params.toString() ? `?${params}` : "";
572
583
  const result = await apiCall("GET", `/api/v1/cost-anomalies${query}`);
@@ -1069,16 +1080,27 @@ async function knowledgeIngestBatch({ items, project_id, publish }) {
1069
1080
  return toContent(result);
1070
1081
  }
1071
1082
 
1072
- async function knowledgeIngestionJobs() {
1073
- const result = await apiCall("GET", "/api/v1/knowledge/ingestion-jobs", null, process.env.LOOPCTL_ORCH_KEY);
1083
+ async function knowledgeIngestionJobs({ limit, offset, since_days } = {}) {
1084
+ const params = new URLSearchParams();
1085
+ if (limit != null) params.set("limit", String(limit));
1086
+ if (offset != null) params.set("offset", String(offset));
1087
+ if (since_days != null) params.set("since_days", String(since_days));
1088
+ const query = params.toString() ? `?${params}` : "";
1089
+ const result = await apiCall(
1090
+ "GET",
1091
+ `/api/v1/knowledge/ingestion-jobs${query}`,
1092
+ null,
1093
+ process.env.LOOPCTL_ORCH_KEY,
1094
+ );
1074
1095
  return toContent(result);
1075
1096
  }
1076
1097
 
1077
1098
  // --- Knowledge Analytics Tools (orch key) ---
1078
1099
 
1079
- async function knowledgeAnalyticsTop({ limit, since_days, access_type } = {}) {
1100
+ async function knowledgeAnalyticsTop({ limit, offset, since_days, access_type } = {}) {
1080
1101
  const params = new URLSearchParams();
1081
1102
  if (limit != null) params.set("limit", String(limit));
1103
+ if (offset != null) params.set("offset", String(offset));
1082
1104
  if (since_days != null) params.set("since_days", String(since_days));
1083
1105
  if (access_type) params.set("access_type", access_type);
1084
1106
  const qs = params.toString();
@@ -1166,10 +1188,11 @@ async function knowledgeAgentUsage({ api_key_id, agent_id, limit, since_days } =
1166
1188
  return toContent(result);
1167
1189
  }
1168
1190
 
1169
- async function knowledgeUnusedArticles({ days_unused, limit } = {}) {
1191
+ async function knowledgeUnusedArticles({ days_unused, limit, offset } = {}) {
1170
1192
  const params = new URLSearchParams();
1171
1193
  if (days_unused != null) params.set("days_unused", String(days_unused));
1172
1194
  if (limit != null) params.set("limit", String(limit));
1195
+ if (offset != null) params.set("offset", String(offset));
1173
1196
  const qs = params.toString();
1174
1197
  const path = qs
1175
1198
  ? `/api/v1/knowledge/analytics/unused-articles?${qs}`
@@ -1401,10 +1424,15 @@ const TOOLS = [
1401
1424
  },
1402
1425
  {
1403
1426
  name: "list_projects",
1404
- description: "List all projects in the current tenant.",
1427
+ description:
1428
+ "List projects in the current tenant. Paginated (page/page_size); advance " +
1429
+ "`page` to enumerate all projects. Response includes pagination meta.",
1405
1430
  inputSchema: {
1406
1431
  type: "object",
1407
- properties: {},
1432
+ properties: {
1433
+ page: { type: "integer", description: "Page number (default 1)." },
1434
+ page_size: { type: "integer", description: "Items per page (default 20)." },
1435
+ },
1408
1436
  required: [],
1409
1437
  },
1410
1438
  },
@@ -1608,7 +1636,8 @@ const TOOLS = [
1608
1636
  description:
1609
1637
  "List stories that are ready to be worked on (contracted, dependencies met). " +
1610
1638
  "Returns compact results — use get_story for full details. " +
1611
- "Defaults to 20 per page; pass `limit` up to 500 to page larger. Response includes total_count.",
1639
+ "Paginated (page/page_size); advance `page` to enumerate all ready stories. " +
1640
+ "Response includes total_count.",
1612
1641
  inputSchema: {
1613
1642
  type: "object",
1614
1643
  properties: {
@@ -1616,9 +1645,10 @@ const TOOLS = [
1616
1645
  type: "string",
1617
1646
  description: "The UUID of the project.",
1618
1647
  },
1619
- limit: {
1648
+ page: { type: "integer", description: "Page number (default 1)." },
1649
+ page_size: {
1620
1650
  type: "integer",
1621
- description: "Maximum number of stories to return (default 20, max 500).",
1651
+ description: "Stories per page (default 100, max 500).",
1622
1652
  },
1623
1653
  },
1624
1654
  required: ["project_id"],
@@ -1963,7 +1993,9 @@ const TOOLS = [
1963
1993
  },
1964
1994
  {
1965
1995
  name: "get_story_token_usage",
1966
- description: "Get token usage records for a single story.",
1996
+ description:
1997
+ "Get token usage records for a single story. Paginated (page/page_size); " +
1998
+ "advance `page` to enumerate all records.",
1967
1999
  inputSchema: {
1968
2000
  type: "object",
1969
2001
  properties: {
@@ -1971,6 +2003,8 @@ const TOOLS = [
1971
2003
  type: "string",
1972
2004
  description: "The UUID of the story.",
1973
2005
  },
2006
+ page: { type: "integer", description: "Page number (default 1)." },
2007
+ page_size: { type: "integer", description: "Records per page (default 20)." },
1974
2008
  },
1975
2009
  required: ["story_id"],
1976
2010
  },
@@ -1979,7 +2013,8 @@ const TOOLS = [
1979
2013
  name: "get_cost_anomalies",
1980
2014
  description:
1981
2015
  "Get cost anomaly alerts — stories or agents that exceed expected token budgets. " +
1982
- "Optionally filter by project.",
2016
+ "Optionally filter by project. Paginated (page/page_size); advance `page` to " +
2017
+ "enumerate all anomalies.",
1983
2018
  inputSchema: {
1984
2019
  type: "object",
1985
2020
  properties: {
@@ -1987,6 +2022,8 @@ const TOOLS = [
1987
2022
  type: "string",
1988
2023
  description: "Optional: filter anomalies to a specific project UUID.",
1989
2024
  },
2025
+ page: { type: "integer", description: "Page number (default 1)." },
2026
+ page_size: { type: "integer", description: "Anomalies per page (default 20)." },
1990
2027
  },
1991
2028
  required: [],
1992
2029
  },
@@ -3038,11 +3075,20 @@ const TOOLS = [
3038
3075
  {
3039
3076
  name: "knowledge_ingestion_jobs",
3040
3077
  description:
3041
- "List recent content ingestion jobs for the current tenant. " +
3042
- "Returns jobs from the last 7 days, max 50 results. Requires orchestrator role.",
3078
+ "List content ingestion jobs for the current tenant, newest first. " +
3079
+ "Paginated (limit/offset over the full history; advance `offset` by `meta.limit` " +
3080
+ "to enumerate to completeness). Optional `since_days` narrows to a recent window. " +
3081
+ "Requires orchestrator role.",
3043
3082
  inputSchema: {
3044
3083
  type: "object",
3045
- properties: {},
3084
+ properties: {
3085
+ limit: { type: "integer", description: "Jobs per page (default 20)." },
3086
+ offset: { type: "integer", description: "Rows to skip (default 0)." },
3087
+ since_days: {
3088
+ type: "integer",
3089
+ description: "Optional: only jobs from the last N days (default: all history).",
3090
+ },
3091
+ },
3046
3092
  required: [],
3047
3093
  },
3048
3094
  },
@@ -3058,10 +3104,15 @@ const TOOLS = [
3058
3104
  properties: {
3059
3105
  limit: {
3060
3106
  type: "integer",
3061
- description: "Max rows to return. Default 20, max 100.",
3107
+ description: "Max rows per page. Default 20, max 100.",
3062
3108
  minimum: 1,
3063
3109
  maximum: 100,
3064
3110
  },
3111
+ offset: {
3112
+ type: "integer",
3113
+ description: "Rows to skip — page the ranking past the first page. Default 0.",
3114
+ minimum: 0,
3115
+ },
3065
3116
  since_days: {
3066
3117
  type: "integer",
3067
3118
  description: "Look back this many days. Default 7.",
@@ -3144,10 +3195,15 @@ const TOOLS = [
3144
3195
  },
3145
3196
  limit: {
3146
3197
  type: "integer",
3147
- description: "Max rows to return. Default 50, max 200.",
3198
+ description: "Max rows per page. Default 50, max 200.",
3148
3199
  minimum: 1,
3149
3200
  maximum: 200,
3150
3201
  },
3202
+ offset: {
3203
+ type: "integer",
3204
+ description: "Rows to skip — page the full unused set to completeness. Default 0.",
3205
+ minimum: 0,
3206
+ },
3151
3207
  },
3152
3208
  required: [],
3153
3209
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loopctl-mcp-server",
3
- "version": "2.22.0",
3
+ "version": "2.24.0",
4
4
  "description": "MCP server for loopctl — structural trust for AI development loops",
5
5
  "type": "module",
6
6
  "main": "index.js",