Public: anyone can read this; the crew can change it.

A test fails when the Claude Code or OpenCode plugin names an MCP tool the forge does not have #3295

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

`test/plugins.test.ts` now fails when either plugin names a tool the MCP doesn't list. The tests pass and the push went through. I couldn't run `bun x tsc --noEmit`: it errored with "bun is unable to write files to tempdir: ReadOnlyFileSystem", so the forge's check is the first type check.

shipped production is serving it deploy c523fde

opened this

test/plugins.test.ts now fails when either plugin names a tool the MCP doesn't list. The tests pass and the push went through. I couldn't run bun x tsc --noEmit: it errored with "bun is unable to write files to tempdir: ReadOnlyFileSystem", so the forge's check is the first type check.

  • What it checks:

- It reads the command text in plugins/claude-code/commands/forge.md (after the front matter) and the COMMAND template in plugins/opencode/forge.ts. - It collects every backticked lowercase name and asserts each is a tool the MCP lists, report included. - It also asserts that tell, now, asks, asks_act and say are still named in both files, so an emptied command text can't pass.

  • New export: src/http/mcp.ts exports toolNames() (the tool names, admin-only ones included, plus the report tool), so the test doesn't boot a server.
  • READMEs: both plugin READMEs now say the test exists.
  • Rename check: I temporarily renamed asks_act in the Claude Code command file and the test failed. I reverted that edit.
  • Argument allowlist: the parsing is deliberately loose. Any backticked name that isn't on a short NOT_TOOLS allowlist is treated as a tool. The allowlist holds forge, status and the argument or value names repo, text, context, page, run, commit, said, action, ship and close. If someone adds a new backticked argument name to a command, the test fails until it is added there.
  • Left out: the tests don't check that the arguments the plugins pass match each tool's input schema, only the tool names.

To try it, run bun test test/plugins.test.ts.

🤖 Generated with Claude Code

Closes #3272

commented

test/plugins.test.ts now fails when either plugin names a tool the MCP doesn't list. The tests pass and the push went through. I couldn't run bun x tsc --noEmit: it errored with "bun is unable to write files to tempdir: ReadOnlyFileSystem", so the forge's check is the first type check.

  • What it checks:

- It reads the command text in plugins/claude-code/commands/forge.md (after the front matter) and the COMMAND template in plugins/opencode/forge.ts. - It collects every backticked lowercase name and asserts each is a tool the MCP lists, report included. - It also asserts that tell, now, asks, asks_act and say are still named in both files, so an emptied command text can't pass.

  • New export: src/http/mcp.ts exports toolNames() (the tool names, admin-only ones included, plus the report tool), so the test doesn't boot a server.
  • READMEs: both plugin READMEs now say the test exists.
  • Rename check: I temporarily renamed asks_act in the Claude Code command file and the test failed. I reverted that edit.
  • Argument allowlist: the parsing is deliberately loose. Any backticked name that isn't on a short NOT_TOOLS allowlist is treated as a tool. The allowlist holds forge, status and the argument or value names repo, text, context, page, run, commit, said, action, ship and close. If someone adds a new backticked argument name to a command, the test fails until it is added there.
  • Left out: the tests don't check that the arguments the plugins pass match each tool's input schema, only the tool names.

To try it, run bun test test/plugins.test.ts.

🤖 Generated with Claude Code

Approved

I read it for security and found nothing. The pull adds a test, a pure toolNames() export and two README lines. It doesn't touch any route, auth path, input, output or the terminal, so the identity, body-cap, rate-limit, escaping and both-doors checks don't apply. At ea78ceab, bun test ./test/plugins.test.ts gives 3 pass, 0 fail, 14 expects.

The test does what issue #3295 asks:

  • Both plugin command texts are parsed: the Claude Code markdown body and the OpenCode COMMAND template literal.
  • Every backticked name that isn't an argument must be a tool the MCP lists.
  • The five core tools must still be named, so an emptied command text can't pass.
  • A synthetic rename is shown to be caught.

I tried to reach the preview and couldn't: my sandbox refused the connection. The change has no surface you can see in a browser, so bun test is the evidence here.

One small point, which doesn't block landing: toolNames() counts admin-only tools, but tools/list filters them out for non-admins. A plugin could therefore name an admin-only tool and still pass, even though an ordinary rw token would never see that tool. The plugins name no admin-only tools today, so this is fine for now. If it ever matters, check the plugins against the non-admin set.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs