loopctl-mcp-server 2.0.0 → 2.0.1

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 +27 -6
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -12,9 +12,15 @@ import {
12
12
  } from "@modelcontextprotocol/sdk/types.js";
13
13
 
14
14
  // ---------------------------------------------------------------------------
15
- // HTTP helper
15
+ // HTTP helper — witness protocol state
16
16
  // ---------------------------------------------------------------------------
17
17
 
18
+ // The witness protocol requires clients to echo back the last-known Signed
19
+ // Tree Head (STH) on every authenticated request. On the very first request
20
+ // we send X-Loopctl-STH-Bootstrap: true to receive the current STH without
21
+ // needing one already. After that we cache and send X-Loopctl-Last-Known-STH.
22
+ let lastKnownSTH = null;
23
+
18
24
  function getBaseUrl() {
19
25
  return (process.env.LOOPCTL_SERVER || "https://loopctl.com").replace(/\/$/, "");
20
26
  }
@@ -43,13 +49,22 @@ async function apiCall(method, path, body, keyOverride) {
43
49
  return { error: true, status: 0, body: "No API key configured. Set LOOPCTL_API_KEY, LOOPCTL_ORCH_KEY, or LOOPCTL_AGENT_KEY." };
44
50
  }
45
51
 
52
+ const headers = {
53
+ Authorization: `Bearer ${key}`,
54
+ "Content-Type": "application/json",
55
+ Accept: "application/json",
56
+ };
57
+
58
+ // Witness protocol: send cached STH or request bootstrap
59
+ if (lastKnownSTH) {
60
+ headers["X-Loopctl-Last-Known-STH"] = lastKnownSTH;
61
+ } else {
62
+ headers["X-Loopctl-STH-Bootstrap"] = "true";
63
+ }
64
+
46
65
  const options = {
47
66
  method,
48
- headers: {
49
- Authorization: `Bearer ${key}`,
50
- "Content-Type": "application/json",
51
- Accept: "application/json",
52
- },
67
+ headers,
53
68
  signal: AbortSignal.timeout(30_000),
54
69
  };
55
70
 
@@ -68,6 +83,12 @@ async function apiCall(method, path, body, keyOverride) {
68
83
  return { error: true, status: 0, body: `Network error: ${err.message}${cause}` };
69
84
  }
70
85
 
86
+ // Witness protocol: cache the STH from response for subsequent requests
87
+ const sthHeader = response.headers.get("x-loopctl-current-sth");
88
+ if (sthHeader) {
89
+ lastKnownSTH = sthHeader;
90
+ }
91
+
71
92
  if (response.status === 204) {
72
93
  return { ok: true };
73
94
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loopctl-mcp-server",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "MCP server for loopctl — structural trust for AI development loops",
5
5
  "type": "module",
6
6
  "main": "index.js",