feat(rkd): RKD channel plugin for Claude Code (#917) #1
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/917-rkd-channel"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Phase 0 of nanoya-biz/rkd#917: the
rkdClaude Code channel plugin, so a paired RKD user can @mention or DM a bot account and drive a Claude Code session on the operator's laptop. Replies land in the same thread, and tool use is approved from chat. No RKD server or client change: the bot is an ordinary user account.Part of nanoya-biz/rkd#917 (phases 0.5 and 1 remain open there).
Type of change
Test plan
Automated:
pnpm testpasses locally (fromrkd/): 77 unit tests covering the trigger gate, pairing, access.json, the refresh rotation (single-flight, persist-before-use, device id), the bridge (threading, chunking, edit throttle and edit-window fallback, outbound allowlist, credential-upload refusal, permission relay routing) and the version floorpnpm typecheckandpnpm check:distpassrkd-server(main at 2ae81d2b, own database on :18090):RKD_TEST_SERVER=http://127.0.0.1:18090 pnpm test:integration, 8/8. It drivesdist/server.jsthrough the MCP SDK client the way Claude Code does: the owner's @mention arrives while plain chatter and a stranger's @mention don't; reply lands threaded (reply_to), then edit and react; a permission prompt is posted in the thread and only the owner'syes <id>approves (the stranger's is ignored); an unpaired DM gets a pairing code;known-channels.jsonlists the channelclaude plugin validate --strictpasses for the marketplace and the plugininitialize/tools/list/statuson Node 24 and Node 20 (scripts/smoke-handshake.mjs)Manual:
--plugin-dir rkd: the model sees all five tools (mcp__plugin_rkd_rkd__reply,edit_message,react,download_attachment,status) and both skills, andstatusreports the client version. This answers #917's open question aboutreplynot reaching the model--dangerously-load-development-channels: channel messages reaching the model and Claude Code's own permission prompts relaying. Not run: it needs the dev-channel consent dialog confirmed by a person. Steps are inrkd/README.mdunder "Manual smoke"Review checklist
console.log/dbg!/ commented-out code left behind (theloginCLI prints to the terminal on purpose)@modelcontextprotocol/sdk1.27.1 (the version the Telegram channel runs),ws(Node 20 has no global WebSocket),zod(the SDK's notification schemas). All are bundled intodist/server.js, so nothing is installed on a user's machineNotes for the reviewer
Deliberate differences from the issue text:
/rkd:configure <server> <email> <password>, but a skill argument ends up in the session transcript. Instead the operator runsnode <plugin>/dist/server.js login <server> <email>in their own terminal with a hidden prompt, and/rkd:configureprints that line. Only the refresh token and device id are stored.pairing, notallowlist, as in the Telegram plugin. Otherwise the operator has no way to find out their own user id./rkd:configurepushes toallowlistonce they're paired, and a stranger can hold at most 3 pending codes (1 h expiry), answered twice at most./rkd:access channel add dev). The bot subscribes to every text channel it can see, because the server only fansMessageCreateout to explicit subscribers, but it drops everything outside the configured ones before Claude reads it. It writesknown-channels.jsonso the skill can resolve names.Worth a hard look:
src/triggers.ts: the prompt-injection boundary. Approvals come only from the globalallowFrom, never from a channel-levelallowFrom.src/rkd-api.ts: refresh rotation. The server revokes the whole chain on a replayed token (and on a device-id mismatch), so the token is read only from.env, never from the environment, where a stale copy could be replayed on the next start.Open:
.forgejo/workflows/ci.yml) is the first workflow in therkdorg. I couldn't see whether that org's repos get a runner, so this PR may show no checks. The integration test needs a live server and is a local gate only for now.package.jsonsaysUNLICENSEDuntil someone decides.claude-code-boton dev/prod) isn't created. That's the operator's step in the README.https://claude.ai/code/session_01GdkP2Y3vcxcGz5XCFFWW16