Skip to content

Release v0.10.0: Cloudflare Workers deployment#3

Merged
klappy merged 4 commits intomainfrom
claude/oddkit-v0.10.0-jKaRv
Feb 2, 2026
Merged

Release v0.10.0: Cloudflare Workers deployment#3
klappy merged 4 commits intomainfrom
claude/oddkit-v0.10.0-jKaRv

Conversation

@klappy
Copy link
Copy Markdown
Owner

@klappy klappy commented Feb 2, 2026

Summary

  • Add Cloudflare Workers MCP server for Claude.ai on iOS/iPad/web
  • Three deployment methods: CLI, MCP (local), MCP (remote)
  • Full MCP capabilities: tools, resources, prompts
  • Updated all docs to cover deployment options

Test plan

  • npm test — All smoke tests pass
  • npm run typecheck — TypeScript compiles
  • wrangler deploy --dry-run — Build succeeds (44KB)
  • oddkit validate consulted

Note

Medium Risk
Introduces a new internet-exposed MCP endpoint with CORS and remote baseline fetching, which adds operational/security surface area despite being largely additive and isolated.

Overview
Adds a Cloudflare Worker deployment (workers/) that exposes oddkit as a remote MCP server over HTTP (/mcp JSON-RPC) with CORS, health endpoints, and support for tools, resources, and prompts (prompts fetched from the baseline registry).

Includes a Worker-specific runOrchestrate implementation that fetches the baseline index/docs from GitHub raw content (optional KV caching) and provides simplified librarian/catalog/preflight/validate behavior.

Bumps the package version to 0.10.0, adds a project-local .mcp.json example, and updates README + MCP/quickstart docs to describe CLI, local MCP, and remote MCP deployment paths.

Written by Cursor Bugbot for commit 9911109. This will update automatically on new commits. Configure here.

Remote MCP server for oddkit enabling Claude.ai on iOS/iPad/web.

Features:
- oddkit_orchestrate, oddkit_librarian, oddkit_validate tools
- Streamable HTTP transport for MCP
- GitHub raw content API for baseline fetching (no git clone)
- CORS enabled for cross-origin requests

Build output: 36.84 KiB / gzip: 9.41 KiB

To deploy:
  cd workers && npm install && npm run deploy

https://claude.ai/code/session_014FSYqtokXmcQADW4tTsgci
Now exposes same capabilities as CLI version:

Resources:
- oddkit://instructions — Decision gate
- oddkit://quickstart — Agent quick start
- oddkit://examples — Usage patterns

Prompts:
- Fetched from klappy.dev/canon/instructions/REGISTRY.json
- Returns agent prompts like odd-epistemic-guide, odd-scribe

Build: 44.18 KiB / gzip: 11.25 KiB

https://claude.ai/code/session_014FSYqtokXmcQADW4tTsgci
Major feature: Remote MCP server for Claude.ai on iOS/iPad/web.

Three deployment methods:
- CLI: npx oddkit <command>
- MCP (local): npx oddkit-mcp for Cursor/Claude Code
- MCP (remote): Cloudflare Worker for Claude.ai mobile/web

Updated docs:
- README.md: Added deployment methods table
- QUICKSTART.md: Added MCP (remote) layer
- MCP.md: Added deployment options section
- CLAUDE-CODE.md: Added Option 4 for mobile/web

https://claude.ai/code/session_014FSYqtokXmcQADW4tTsgci
@klappy klappy merged commit 64dc8c3 into main Feb 2, 2026
1 check passed
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 4 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

};
default:
return await runLibrarian(message, baselineUrl, cache);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Orient action advertised but silently falls back to librarian

Medium Severity

The tool schema declares orient as a valid action in the enum, and the examples resource documents it as supported. However, the runOrchestrate switch statement has no case for "orient", causing it to silently fall through to the default case which runs runLibrarian instead. Users who explicitly request the orient action will receive librarian behavior without any indication that orient wasn't actually executed.

Additional Locations (1)

Fix in Cursor Fix in Web

Comment thread workers/package-lock.json
@@ -0,0 +1,1527 @@
{
"name": "oddkit-mcp-worker",
"version": "0.9.1",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worker package-lock.json version mismatches package.json

Low Severity

The workers/package-lock.json declares version 0.9.1 in two places (root and packages[""] entries), while workers/package.json declares version 0.10.0. The lock file wasn't regenerated after the version bump.

Additional Locations (1)

Fix in Cursor Fix in Web

Comment thread workers/src/index.ts
status: 204,
headers: corsHeaders(origin),
});
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requests with id zero silently return empty response

Medium Severity

The notification detection check !response.id incorrectly treats id: 0, id: null, and id: "" as falsy. If a client sends a valid JSON-RPC request with id: 0, the condition !response.id && !response.error evaluates to true, causing the server to return 204 No Content instead of the actual result. Clients using sequential numeric IDs starting from zero would have their first request silently fail.

Fix in Cursor Fix in Web

// Check for required evidence types
const hasScreenshot = artifacts.some((a) => /\.(png|jpg|jpeg|gif)$/i.test(a));
const hasVideo = artifacts.some((a) => /\.(mp4|mov)$/i.test(a));
const hasLog = artifacts.some((a) => /\.(log|txt)$/i.test(a));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable hasLog in validate function

Low Severity

The variable hasLog is declared but never used. Only hasScreenshot and hasVideo are used in the validation logic on line 216. This appears to be leftover code from planned functionality that was never implemented.

Fix in Cursor Fix in Web

klappy added a commit that referenced this pull request Apr 17, 2026
…and dead branch

Two issues from bugbot's 14:29 review:

1. Reframing 'none' check applies same defensive pattern as the tiersRaw
   fix in fetchStakesCalibration. The cell may be 'none' or
   'none (parenthetical reason)' — strict equality would silently surface
   all reframings via the 'all' fallback when authors include explanatory
   text. Same defect class as bug #3 in the evidence note; sweep applied.

2. Remove unreachable questionTiers.length === 0 branch in the question-
   surfacing condition. The SUPPRESSED early-return at line 1635 already
   handles that case, so the branch was dead code that misleadingly
   suggested 'surface all questions for empty tiers' semantics — the
   actual semantic is full suppression.

Verified: typecheck clean, parser test 97/97 against main, smoke 6/6.
Defect-class sweep on governance cell strict-equality checks: only two
sites (tiersRaw, surfacing), both now defensive.
klappy added a commit that referenced this pull request Apr 26, 2026
The three Cursor Agent fix commits on this PR addressed all three
Bugbot findings correctly, but only the count() collision (Bug #1)
got dedicated test coverage. Bugs #2 and #3 — both about literal
handling — landed without regression tests, so a future refactor
that reverts the literal-skip logic would not be caught by CI.

This commit closes the gap.

Test 1: rewriteSqlToRaw — semantic names inside single-quoted literals
  - 'klappy://sources/scientific-method' must not have method → blob2
  - SQL doubled-quote escape ''  must keep word inside literal preserved
  - Mixed case: column ref outside literal still rewrites; same word
    inside a literal stays untouched

Test 2: detectRawSlotNames — raw slot names inside literals do not
        trigger rejection
  - 'https://example.com/blob1/readme' must not be falsely rejected
  - 'klappy://reports/double5-summary' likewise
  - Sanity guard: bare blob1 outside any literal STILL gets rejected
  - Mixed case: raw slot outside a literal is rejected even when
    another raw slot appears inside a literal in the same query

All 17 tests pass locally:
  17 passed, 0 failed
  (was 15 — added 2)

This addresses the test-coverage gap I noted in the PR review, not
a code defect — the fixes themselves are correct and these tests
verify they hold.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants