From d009e391554d1c8c91de5a85f520624c9156982b Mon Sep 17 00:00:00 2001
From: Gk <gk@Gks-MacBook-Pro.local>
Date: Thu, 16 Apr 2026 14:43:32 -0700
Subject: [PATCH] fix(mcp): pause/resume tools use PATCH /v1/cues/{id}
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The `cueapi_pause_cue` and `cueapi_resume_cue` tools previously called
`POST /v1/cues/{id}/pause` and `/resume` respectively. Those endpoints
do not exist on the CueAPI server — calls returned runtime 404s.

Status is mutated via `PATCH /v1/cues/{id}` with body `{status: "..."}`,
matching the CLI's behavior (cueapi-cli/cueapi/cli.py:290-314). The
allowed status values come from the Cue model's CHECK constraint
(cueapi-core/app/models/cue.py:35):
    status IN ('active', 'paused', 'completed', 'failed')

Changes:
- src/tools.ts: pause handler now PATCHes with {status: "paused"};
  resume handler PATCHes with {status: "active"}.
- tests/tools.test.ts: added 3 behavioral tests that stub the client
  and pin the HTTP contract (method, path, body). The original test
  suite was surface-only (tool count, names, schemas) and did not
  exercise handlers, which is how the bug shipped. 9/9 tests pass.
- package.json: 0.1.2 → 0.1.3.
- README.md: changelog section with entries for 0.1.0, 0.1.2, 0.1.3.

No client.ts changes required — the existing generic `request(method,
path, body?, query?)` method supports PATCH without modification.
No changes to the Zod input schema.

Do NOT publish to npm until this PR is reviewed.
---
 README.md           |  6 +++++
 package.json        |  2 +-
 src/tools.ts        | 17 ++++++++++----
 tests/tools.test.ts | 56 ++++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 75 insertions(+), 6 deletions(-)

diff --git a/README.md b/README.md
index a7e338c..4681b5c 100644
--- a/README.md
+++ b/README.md
@@ -79,6 +79,12 @@ npm run dev     # run the server locally with tsx
 - **Core (open source):** https://github.com/cueapi/cueapi-core
 - **Model Context Protocol:** https://modelcontextprotocol.io
 
+## Changelog
+
+- **0.1.3** — Fix `cueapi_pause_cue` / `cueapi_resume_cue` to use `PATCH /v1/cues/{id}` with `{"status": "paused" | "active"}` (previously called non-existent `/pause` and `/resume` endpoints → runtime 404).
+- **0.1.2** — Register with the Official MCP Registry.
+- **0.1.0** — Initial release: 8 tools for create / list / get / pause / resume / delete cues, list executions, report outcome.
+
 ## License
 
 MIT © Vector Apps Inc.
diff --git a/package.json b/package.json
index eb17834..d33a851 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@cueapi/mcp",
-  "version": "0.1.2",
+  "version": "0.1.3",
   "mcpName": "io.github.govindkavaturi-art/cueapi-mcp",
   "description": "Official Model Context Protocol (MCP) server for CueAPI — give your AI agent a scheduler and verification gate. Open-source execution accountability primitive for AI agents.",
   "type": "module",
diff --git a/src/tools.ts b/src/tools.ts
index 6859f96..0968384 100644
--- a/src/tools.ts
+++ b/src/tools.ts
@@ -135,9 +135,13 @@ export const tools: ToolDefinition[] = [
     description: "Pause a cue. Paused cues do not fire until resumed.",
     schema: cueIdSchema,
     handler: async (client, args) =>
+      // CueAPI does not expose a dedicated pause endpoint — status is
+      // mutated via PATCH, matching the CLI's behavior in
+      // cueapi-cli/cueapi/cli.py:290-294.
       client.request(
-        "POST",
-        `/v1/cues/${encodeURIComponent(args.cue_id)}/pause`
+        "PATCH",
+        `/v1/cues/${encodeURIComponent(args.cue_id)}`,
+        { status: "paused" }
       ),
   },
   {
@@ -145,9 +149,14 @@ export const tools: ToolDefinition[] = [
     description: "Resume a previously-paused cue.",
     schema: cueIdSchema,
     handler: async (client, args) =>
+      // "active" is the default status from the Cue model enum
+      // (cueapi-core/app/models/cue.py:35 CHECK IN ('active','paused',
+      // 'completed','failed')) — same value the CLI uses at
+      // cueapi-cli/cueapi/cli.py:313.
       client.request(
-        "POST",
-        `/v1/cues/${encodeURIComponent(args.cue_id)}/resume`
+        "PATCH",
+        `/v1/cues/${encodeURIComponent(args.cue_id)}`,
+        { status: "active" }
       ),
   },
   {
diff --git a/tests/tools.test.ts b/tests/tools.test.ts
index e5435c1..95adc1a 100644
--- a/tests/tools.test.ts
+++ b/tests/tools.test.ts
@@ -1,5 +1,6 @@
-import { describe, it, expect } from "vitest";
+import { describe, it, expect, vi } from "vitest";
 import { tools } from "../src/tools.js";
+import type { CueAPIClient } from "../src/client.js";
 
 describe("cueapi-mcp tool surface", () => {
   it("exposes at least 8 tools", () => {
@@ -43,3 +44,56 @@ describe("cueapi-mcp tool surface", () => {
     }
   });
 });
+
+describe("cueapi_pause_cue / cueapi_resume_cue — HTTP contract", () => {
+  // CueAPI does not have /pause or /resume endpoints. Status is mutated
+  // via PATCH /v1/cues/{id}. These tests pin the handler's HTTP behavior
+  // so a regression to the non-existent POST /pause / POST /resume routes
+  // (which would 404 at runtime) is caught at CI time, not in production.
+
+  function findTool(name: string) {
+    const t = tools.find((x) => x.name === name);
+    if (!t) throw new Error(`tool ${name} missing`);
+    return t;
+  }
+
+  function stubClient() {
+    const calls: Array<{ method: string; path: string; body?: unknown; query?: unknown }> = [];
+    const client = {
+      request: vi.fn(async (method: string, path: string, body?: unknown, query?: unknown) => {
+        calls.push({ method, path, body, query });
+        return { id: "cue_test", status: "ok" };
+      }),
+    } as unknown as CueAPIClient;
+    return { client, calls };
+  }
+
+  it("pause uses PATCH /v1/cues/{id} with {status: 'paused'}", async () => {
+    const tool = findTool("cueapi_pause_cue");
+    const { client, calls } = stubClient();
+    await tool.handler(client, { cue_id: "cue_abc123" });
+
+    expect(calls).toHaveLength(1);
+    expect(calls[0].method).toBe("PATCH");
+    expect(calls[0].path).toBe("/v1/cues/cue_abc123");
+    expect(calls[0].body).toEqual({ status: "paused" });
+  });
+
+  it("resume uses PATCH /v1/cues/{id} with {status: 'active'}", async () => {
+    const tool = findTool("cueapi_resume_cue");
+    const { client, calls } = stubClient();
+    await tool.handler(client, { cue_id: "cue_abc123" });
+
+    expect(calls).toHaveLength(1);
+    expect(calls[0].method).toBe("PATCH");
+    expect(calls[0].path).toBe("/v1/cues/cue_abc123");
+    expect(calls[0].body).toEqual({ status: "active" });
+  });
+
+  it("url-encodes the cue_id in the path", async () => {
+    const tool = findTool("cueapi_pause_cue");
+    const { client, calls } = stubClient();
+    await tool.handler(client, { cue_id: "cue/with/slashes" });
+    expect(calls[0].path).toBe("/v1/cues/cue%2Fwith%2Fslashes");
+  });
+});
