convergence-gambit-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@convergence-gambit-mcplist campaign resources"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
The Convergence Gambit MCP Server
Bespoke, single-tenant MCP server connecting Claude to the campaign's LegendKeeper wiki. Two clients, one project, design philosophy baked into source. No multi-tenancy, no database, no apologies.
Current state: read-side tools against the live LegendKeeper v3 API,
behind GitHub OAuth. Reads go through the official @legendkeeper/sdk
behind the LegendKeeperClient seam; without an LK_API_KEY an
in-memory fake answers instead. Every tool sits behind OAuth (see
"Authentication" below) — only GitHub account Kishotta is ever issued a
token. Write tools come next, designed from what the read tools teach us.
Read-side toolset
Tool | Purpose |
| Proves the transport; says whether the live project or the fake answers. |
| Serves the Architecture & Presentation Charter verbatim. |
| The project hierarchy as an outline, hidden resources marked. |
| The article skeletons (templates), with their tabs and nested resources. |
| One article as markdown: visibility, properties, pages, maps, timelines. |
| LegendKeeper full-text search with matching passages. |
| Tag-index queries — DM Dashboard / Field Notes previews. |
| Tag usage, with how many carriers players can see. |
| Raw block JSON for one tab (debugging, write planning). |
Every view except search takes audience: dm (default — everything,
with hidden tabs, secret blocks and hidden properties marked inline) or
player (only what players can see right now). read_article also
flags player-visible text that names a hidden article, the charter's "one
accepted leak".
Related MCP server: mcp-wikijs
Write-side toolset
Tool | Purpose |
| A new, hidden item article from the Magic Item templates, populated. |
| Insert, replace and delete blocks in one page tab of an existing article. |
The create tools are specific on purpose: each knows one kind of article,
its template, its tags and where its content goes. create_magic_item puts
mechanics in a secret block for anything above Common, since the party has
to identify those first.
edit_page is the catch-all for articles that already exist, for bringing
a stale or half-filled one in line with newer notes. Read the tab first
with read_article and blockIds: that prints a ⟦id⟧ line above each
block and the tab's document version, which the edit must quote so it
fails rather than overwrite a change made since. Secrecy is never read
from the text: content is secret when it is put inside a secret block or
sent with secret: true, and the result says which layer each piece
landed in. A batch is not atomic; it is checked as a whole before the
first write, then applies in order and stops at the first failure.
Setup (day one)
Prereqs: Node 18+, a free Cloudflare account.
npm install
npx wrangler login # opens browser, authorizes wrangler
npm run typecheck # sanity check
npm run dev # local server at http://localhost:8787/mcp
npx wrangler deploy # → https://convergence-gambit-mcp.<account>.workers.devOptional local verification before deploying:
npx @modelcontextprotocol/inspector
# connect to http://localhost:8787/mcp (Streamable HTTP)
# Inspector will walk you through the GitHub login popup — see Authentication belowThen on the phone: Customize → Connectors → Add custom connector, paste
https://convergence-gambit-mcp.<account>.workers.dev/mcp. Claude will
prompt a GitHub login before the connector activates. Once connected, ask
Claude to ping (it reports live vs. fake) and get_tree.
Deploys (CI/CD)
.github/workflows/ci.yml typechecks and bundles every pull request and
push, and deploys every push to main. Merging is shipping. Manual
npx wrangler deploy still works as a fallback.
One-time setup, as GitHub repository secrets (Settings → Secrets and variables → Actions):
CLOUDFLARE_API_TOKEN— Cloudflare dashboard → My Profile → API Tokens → Create Token → Edit Cloudflare Workers template, scoped to this account. If a deploy fails on the R2 or KV binding, add Workers R2 Storage: Edit / Workers KV Storage: Edit to the token.CLOUDFLARE_ACCOUNT_ID— fromnpx wrangler whoami.
The Worker's own secrets (LK_API_KEY, OAuth) stay in Cloudflare; deploys
leave them alone. Dependabot (.github/dependabot.yml) opens a PR for each
new @legendkeeper/sdk release so CI vets SDK upgrades before they ship.
Authentication
Every tool sits behind GitHub OAuth (@cloudflare/workers-oauth-provider +
a GitHub-OAuth defaultHandler, in src/auth/). No token is ever issued to
any GitHub account other than Kishotta — the callback in
src/auth/github-handler.ts rejects everyone else with a 403 before
completeAuthorization is ever called. This is stricter than Cloudflare's
own reference pattern (which authenticates any GitHub user and just hides
extra tools) because the campaign data behind these tools is
spoiler-sensitive, not just at mutation risk.
One-time setup, before wrangler dev or deploy will work:
Create two GitHub OAuth Apps at github.com/settings/developers → OAuth Apps → New OAuth App:
Dev: Authorization callback URL
http://localhost:8787/callbackProd: Authorization callback URL
https://convergence-gambit-mcp.<account>.workers.dev/callback(or the custom domain, once phase 3 lands — a new callback URL is needed on that cutover too)
Create the KV namespace OAuthProvider needs for state + client-approval storage (already done for this repo; re-run only if it's ever deleted):
npx wrangler kv namespace create OAUTH_KV # paste the resulting id into wrangler.jsonc's kv_namespaces bindingLocal dev: copy
.dev.vars.exampleto.dev.vars(gitignored) and fill in the dev app's client id/secret, plus a randomCOOKIE_ENCRYPTION_KEY(any random string — used to HMAC-sign the client-approval cookie):node -e "console.log(crypto.randomUUID())"Production secrets, from the prod app:
npx wrangler secret put GITHUB_CLIENT_ID npx wrangler secret put GITHUB_CLIENT_SECRET npx wrangler secret put COOKIE_ENCRYPTION_KEY # can reuse the dev value or generate a new one
Once all three secrets exist in both places, npm run dev / wrangler deploy work as before, just with GitHub login gating /mcp and /sse.
Two different "Client ID"s — don't cross them. This tripped us up on first deploy. There are two unrelated OAuth relationships stacked here:
Who authenticates to whom
Where the credential lives
GitHub OAuth App
our Worker → github.com
wrangler secret put GITHUB_CLIENT_ID/_SECRET— never touched by ClaudeThis server's own OAuth clients
Claude → our Worker
auto-registered by Claude itself via
/register(RFC 7591) — nobody ever types this inWhen adding the custom connector in Claude, enter only the server URL and leave any Client ID / Client Secret fields blank. If you paste the GitHub OAuth App's credentials into Claude's connector setup,
/authorizewill fail withInvalid client. The clientId provided does not match to this client.(thrown by@cloudflare/workers-oauth-providerwhen it can't find that client_id inOAUTH_KV— because it was never registered there in the first place). Also: Worker secrets and KV are different storage systems — don't addGITHUB_CLIENT_IDetc. as KV key-value pairs in theOAUTH_KVnamespace's dashboard browser;wrangler secret put(or the dashboard's Variables & Secrets tab) is the only thing that actually reachesenv.GITHUB_CLIENT_IDin the Worker.
Version drift note. The
agentspackage (Cloudflare'sMcpAgent) moves fast. Ifnpm installortypecheckcomplains, scaffold Cloudflare's current authless remote-MCP template into a scratch directory (npm create cloudflare@latest -- scratch --template=cloudflare/ai/demos/remote-mcp-authless), copy itspackage.jsondependency versions and any changedMcpAgent/serveidioms into this repo, and keep oursrc/content. The template is the authority on plumbing; this repo is the authority on everything above the seam.
The seam
Everything meaningful lives behind one interface:
src/lk/types.ts LegendKeeperClient + LK types (re-exported from the SDK)
src/lk/sdk.ts Live implementation over @legendkeeper/sdk — the only runtime SDK import
src/lk/fake.ts In-memory implementation for keyless local dev
src/lk/client.ts Picks one: LK_API_KEY set → SDK, else fake
src/wiki/ Above the seam: resource index, block→markdown renderer, views
src/index.ts MCP tools — talk only to the interface and src/wiki/The SDK is a beta regenerated on every API release, so it is pinned to an
exact version and confined to src/lk/. Upgrading it should mean editing
sdk.ts/types.ts, never the tools. Fields the live API returns
but the pinned SDK doesn't type yet are tagged SPEC-DRIFT in
src/lk/types.ts.
Nightly backups
A cron trigger (0 9 * * * in wrangler.jsonc) calls src/backup.ts
nightly. The v3 API has no whole-project export, so the backup walks the
project through the seam (every resource, and every page, map and timeline tab),
throttled to ~120 requests/minute to leave the key's 300/minute budget for
live use, and writes one snapshot to the BACKUPS R2 bucket
(convergence-gambit-backups), pruning anything older than 30 days. Board
content and assets aren't captured yet.
One-time setup before this can deploy: R2 must be enabled on the Cloudflare account (dashboard only, not scriptable), then create the bucket:
npx wrangler r2 bucket create convergence-gambit-backupsTo exercise the cron locally, with npm run dev already running:
curl "http://localhost:8787/cdn-cgi/handler/scheduled".
Phases
Walking skeleton (done) — authless, fake-backed, deployed, connected from the phone.
Auth (done) — GitHub OAuth in front of every tool (see "Authentication" above), allowlisted to
Kishottaonly.Custom domain — point
connoreaves.dev(orkishotta.com) DNS at Cloudflare, uncomment the route inwrangler.jsonc, redeploy tomcp.connoreaves.dev.Real client, read side (done) —
src/lk/sdk.tsover the official SDK, plus the read tools above. Needsnpx wrangler secret put LK_API_KEY(an ADMIN-role project key;LK_PROJECT_IDoptional).Charter-aware write tools (started) —
create_magic_item, the block-leveledit_pageand a small markdown→blocks converter (src/wiki/compose.ts) exist.edit_pagedoes not yet move blocks or write tables, images or infobox rows. Still to come:create_npc_article,create_location_article,stage_reveal. Designed from real usage of the tools so far.
What the real API answered
Visibility model: exposed.
isHiddenon resources, documents (tabs) and properties, andsecretblocks. Effective visibility (a hidden ancestor hides its subtree) is computed insrc/wiki/index.ts.Page format: a typed block tree, not ProseMirror and not markdown. The SDK ships block builders (
@legendkeeper/sdk/blocks) the write side can target.Whole-project export: none. The nightly backup synthesizes one.
This server cannot be deployed
Maintenance
Related MCP Connectors
- platform7nOAuthtech.p7n
Connect Claude to your Platform7n workspaces — chat, links, and tasks. One-click OAuth.
Connect your team's living knowledge base — docs, data, issues, CRM — to Claude and ChatGPT.
A second brain for Claude: remembers your work, plans your day, encrypted under a key only you hold.
Access your Kazkar TTRPG world — campaigns, chronicles, transcripts, lore — in any MCP client.
Related MCP Servers
- FlicenseNot gradedqualityAmaintenanceConnects Claude Desktop to Foundry VTT for AI-powered campaign management, enabling natural language interaction with game data including quest creation, character management, compendium searches, and dice rolling. Provides 20 MCP tools for seamless integration between Claude and your tabletop RPG sessions.73-
- AlicenseBqualityDmaintenanceConnects Claude to a Wiki.js instance, enabling page search, retrieval, listing, and creation.45 npmMIT
- AlicenseAqualityDmaintenanceConnects Claude to a Markdrop knowledge base, allowing it to search and retrieve markdown documentation for context-aware development assistance.4MIT
- AlicenseNot gradedqualityBmaintenanceConnect to your TTRPG campaign's repository and database. Instead of retrieving prose, its 48 tools (20 read, 28 write) return typed state: 14 entity schemas (NPCs, factions, locations, sessions, lore), relationship and knowledge-graph queries, wiki blocks, and a narrative-state bundle of open threads and canon facts.1MIT