moltbook-mcp 0.1.2 → 0.1.4

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 +14 -1
  2. package/dist/index.js +14 -4
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -177,7 +177,7 @@ The server enforces several safety mechanisms to protect the account:
177
177
 
178
178
  - **Rate limiting** -- captures `retry-after` values from API responses (headers and body fields) and blocks write attempts until the cooldown expires. Cooldowns are tracked per-category (post, comment, general write).
179
179
  - **Suspension detection** -- parses API responses for suspension or ban signals. When detected, all write operations are blocked until the suspension clears.
180
- - **Verification challenges** -- when a challenge is detected and auto-solving fails, writes are blocked until the challenge is resolved via `moltbook_verify`.
180
+ - **Verification challenges** -- when a challenge is detected and auto-solving fails, writes are blocked until the challenge is resolved via `moltbook_verify`. Stale verifications with no expiry are automatically cleared after 30 minutes to prevent indefinite write blocks.
181
181
  - **Safe mode** -- enabled by default, enforces a minimum 15-second interval between consecutive write operations to avoid triggering platform rate limits.
182
182
 
183
183
  All guard state is persisted to `~/.config/moltbook/mcp_state.json` and survives server restarts.
@@ -203,6 +203,19 @@ npm run test:coverage
203
203
 
204
204
  Requires Node.js >= 22.
205
205
 
206
+ ## Changelog
207
+
208
+ ### 0.1.4
209
+
210
+ - `moltbook_health` now clears expired verifications (calls `clearExpiredState()`) so stale zombies don't persist across health checks
211
+ - `moltbook_health` now returns `blocked_for_writes` boolean, matching `moltbook_challenge_status` behavior
212
+
213
+ ### 0.1.3
214
+
215
+ - Fix zombie pending verification blocking all writes indefinitely
216
+ - Add 30-minute max age for verifications with no expiry
217
+ - Add retry safety guards (extraction gate, verify-handler gate) to prevent zombie verification loops
218
+
206
219
  ## License
207
220
 
208
221
  [MIT](https://opensource.org/licenses/MIT)
package/dist/index.js CHANGED
@@ -98,6 +98,7 @@ function extractVerification(response) {
98
98
  if (!hasKeyword && !code) return null;
99
99
  if (response.status < 400 && !code) return null;
100
100
  const challengeText = challengeObj?.challenge ?? challengeObj?.prompt ?? challengeObj?.question ?? body.challenge_text ?? body.math_challenge ?? body.question ?? null;
101
+ if (!code && typeof challengeText !== "string") return null;
101
102
  return {
102
103
  verification_code: code ? String(code) : null,
103
104
  challenge: typeof challengeText === "string" ? challengeText : null,
@@ -107,6 +108,7 @@ function extractVerification(response) {
107
108
  }
108
109
 
109
110
  // src/state.ts
111
+ var MAX_VERIFICATION_AGE_MS = 30 * 60 * 1e3;
110
112
  var CREDENTIALS_PATH = join(homedir(), ".config", "moltbook", "credentials.json");
111
113
  var STATE_PATH = join(homedir(), ".config", "moltbook", "mcp_state.json");
112
114
  var DEFAULT_STATE = {
@@ -131,9 +133,14 @@ function saveState(state) {
131
133
  writeJson(STATE_PATH, state);
132
134
  }
133
135
  function clearExpiredState(state) {
134
- if (state.pending_verification?.expires_at) {
135
- const ts = Date.parse(state.pending_verification.expires_at);
136
- if (Number.isFinite(ts) && ts < Date.now()) state.pending_verification = null;
136
+ if (state.pending_verification) {
137
+ if (state.pending_verification.expires_at) {
138
+ const ts = Date.parse(state.pending_verification.expires_at);
139
+ if (Number.isFinite(ts) && ts < Date.now()) state.pending_verification = null;
140
+ } else if (state.pending_verification.detected_at) {
141
+ const age = Date.now() - Date.parse(state.pending_verification.detected_at);
142
+ if (Number.isFinite(age) && age > MAX_VERIFICATION_AGE_MS) state.pending_verification = null;
143
+ }
137
144
  }
138
145
  for (const key of ["post_until", "comment_until", "write_until"]) {
139
146
  if (!isFutureIso(state.cooldowns[key])) state.cooldowns[key] = null;
@@ -476,7 +483,8 @@ async function handleVerify(args) {
476
483
  if (suspension) {
477
484
  state.suspension = { active: true, reason: suspension.reason, until: suspension.until ? String(suspension.until) : null, seen_at: nowIso() };
478
485
  }
479
- const verification = extractVerification(response);
486
+ const rawVerification = extractVerification(response);
487
+ const verification = rawVerification?.verification_code ? rawVerification : null;
480
488
  if (verification) {
481
489
  const priorFailed = pending?.failed_answers ?? [];
482
490
  state.pending_verification = {
@@ -651,6 +659,7 @@ function registerHealth(server2) {
651
659
  const status = await apiRequest("GET", "/agents/status");
652
660
  const me = await apiRequest("GET", "/agents/me");
653
661
  const state = loadState();
662
+ clearExpiredState(state);
654
663
  const suspension = extractSuspension(status);
655
664
  if (suspension) {
656
665
  state.suspension = { active: true, reason: suspension.reason, until: suspension.until ? String(suspension.until) : null, seen_at: nowIso() };
@@ -661,6 +670,7 @@ function registerHealth(server2) {
661
670
  tool: "moltbook_health",
662
671
  account_status: status.body?.status ?? null,
663
672
  pending_verification: state.pending_verification,
673
+ blocked_for_writes: Boolean(state.pending_verification || state.suspension?.active),
664
674
  suspension: state.suspension,
665
675
  cooldowns: state.cooldowns,
666
676
  status_http: status.status,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "moltbook-mcp",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Challenge-aware MCP server for Moltbook with write safety guards",
5
5
  "type": "module",
6
6
  "bin": {