Repository navigation
fix: make the bundled MCP servers and SQL safety hook work out of the box - #7
Merged
Merged
Conversation
The plugin's .mcp.json mapped each COCKROACHDB_* variable to ${VAR}.
Copilot CLI passes an unset ${VAR} through as the literal text ${VAR},
so Toolbox failed to build its connection URL, and the Cloud server
rejected the literal mcp-cluster-id header with HTTP 400. VS Code turns
an unset ${VAR} into an empty string, which Toolbox also uses as the
value instead of falling back to its tools.yaml default.
Both clients pass the parent environment to stdio servers, so the
Toolbox server no longer has an env block: set variables still reach
Toolbox and unset ones fall back to tools.yaml. No placeholder is safe
in both clients for the optional cluster ID header, so the plugin
config leaves it out; .vscode/mcp.json keeps it, because VS Code
resolves ${env:VAR} to an empty string, which the server treats as
absent.
Toolbox runs with readOnlyMode, so the execute-sql tool description,
the agent files, and the README no longer promise DDL and DML. The
README now covers installing Toolbox on Windows, the running cluster
the Toolbox server needs, turning off a server in Copilot CLI and VS
Code, limiting the Cloud server to one cluster, and the first-party
CockroachDB MCP Server as an alternative backend.
Related to cockroachdb/claude-plugin#27
Copilot CLI names a plugin's MCP tools <server>-<tool>, so the
validate-sql matcher, which listed only
mcp__cockroachdb-toolbox__cockroachdb-execute-sql, never matched
cockroachdb-toolbox-cockroachdb-execute-sql and the hook never ran. The
matcher now lists the Copilot CLI name, the Claude Code plugin name,
and the bare name.
Claude Code also stopped substituting ${CLAUDE_PLUGIN_ROOT} inside
shell-form commands; it exports the variable instead, and the bootstrap
read the placeholder from inside single quotes. The bootstrap now uses
the substituted path when a host provides one, as Copilot CLI does, and
otherwise reads CLAUDE_PLUGIN_ROOT, COPILOT_PLUGIN_ROOT, or PLUGIN_ROOT.
The Windows long-path prefix, which evaluated to \?\ instead of \\?\,
is now built with chr(92).
test-hooks.sh gains cases for an unsubstituted placeholder with each
exported variable, in both the sh and PowerShell forms; the old
bootstrap fails them. CONTRIBUTING and AGENTS document the bootstrap
and the per-host tool names.
GPT models in Copilot edit files with apply_patch, whose input is the patch text rather than an object with a file path, so check-sql-files.py silently skipped those edits. It now reads the edited paths from the patch headers and resolves them against the payload's cwd. The lint matcher also lists Copilot's runtime edit tool names next to the Claude names. The scripts are shared verbatim with the codex plugin, which also adds a --codex output mode. The copilot hooks don't pass it, so their output is unchanged. Both scripts now accept a tool input that arrives as a JSON string, as Copilot's camelCase hook format sends it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
.mcp.jsonmapped eachCOCKROACHDB_*variable to${VAR}. Copilot CLI passes an unset${VAR}through as the literal text${VAR}, so Toolbox couldn't build its connection URL. VS Code turns it into an empty string, which Toolbox also uses as the value instead of itstools.yamldefault. Both clients pass the parent environment to stdio servers, so the Toolbox server no longer has anenvblock: set variables still reach Toolbox, and unset ones fall back totools.yaml..vscode/mcp.jsongets the same change.mcp-cluster-idheader from the plugin config. WithCOCKROACHDB_CLUSTER_IDunset, Copilot CLI sent the literal placeholder and the Cloud server failed with HTTP 400. No placeholder is safe in both clients (${VAR:-}stays literal in VS Code), so the plugin config leaves the header out, and the connection reaches every cluster the user's role allows..vscode/mcp.jsonkeeps it, because VS Code resolves${env:VAR}to an empty string, which the server treats as absent.<server>-<tool>, so thevalidate-sqlmatcher (mcp__cockroachdb-toolbox__cockroachdb-execute-sql) never matchedcockroachdb-toolbox-cockroachdb-execute-sql. The matcher now lists the Copilot CLI name, the Claude Code plugin name, and the bare name. The hook bootstrap now also falls back to the exportedCLAUDE_PLUGIN_ROOT,COPILOT_PLUGIN_ROOT, orPLUGIN_ROOTwhen a host doesn't substitute the placeholder, as current Claude Code doesn't, and the Windows long-path prefix, which evaluated to\?\instead of\\?\, is fixed.apply_patch. GPT models in Copilot edit files withapply_patch, whose input is patch text with no file path key, socheck-sql-files.pyskipped those edits. It now reads the paths from the patch headers, and the lint matcher lists Copilot's runtime edit tool names. The scripts are shared verbatim with the codex plugin, which adds a--codexoutput mode that these hooks don't use.envblock, and document the bootstrap and the per-host tool names.Testing
Run on macOS with GitHub Copilot CLI 1.0.67, MCP Toolbox 1.12.0, and a local CockroachDB v25.4 cluster, using the installed plugin with its files temporarily swapped:
tool_nameiscockroachdb-toolbox-cockroachdb-execute-sqlfor the Toolbox SQL tool andWritefor file writes. The hook process getsCLAUDE_PLUGIN_ROOT,COPILOT_PLUGIN_ROOT, andPLUGIN_ROOT, runs from the plugin root, and receives${CLAUDE_PLUGIN_ROOT}already substituted.TRUNCATEthrough Toolbox is blocked by the hook ("TRUNCATE is blocked by CockroachDB plugin safety hook"). With the current hooks file, the hook never ran, and only Toolbox's read-only mode stopped the statement.toolboxonPATHshowed that the current.mcp.jsonsends unset variables to Toolbox as literal${COCKROACHDB_HOST}text, and that variables Copilot doesn't list are still inherited by the server.bash scripts/test-hooks.sh: all pass, including new cases for an unsubstituted placeholder with each exported variable in the sh and PowerShell forms (the current bootstrap fails those cases),apply_patchedits passed as a patch string or acommandobject (the currentcheck-sql-files.pyprints nothing for them), and a check that the SQL matcher lists the Copilot CLI tool name.Not tested: VS Code and Windows (the long-path prefix fix was checked by evaluating the string). The VS Code expansion behavior comes from its source code.
Related to cockroachdb/claude-plugin#27.