mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-08-03 17:58:30 +08:00
parseAllowedTools (used to decide which GitHub MCP servers to install) hand-rolled a regex parse of claude_args, while the tools actually granted to Claude are parsed by parseClaudeArgsToExtraArgs in base-action/src/parse-sdk-options.ts using shell-quote. The two parsers diverged on two inputs (#1357): - Multiple values after a single flag: for `--allowedTools "Read" "Grep" "mcp__github__get_commit"` the regex captured only "Read", so the github MCP server was not installed even though mcp__github__get_commit was granted — tool calls then failed. - Commented-out lines: the regex counted tools on `#`-prefixed lines that the SDK parser strips, installing servers that were never used. Reimplement parseAllowedTools on the same shell-quote tokenizer and the same "accumulating flag consumes all consecutive non-flag values" semantics, stripping comment lines first, so the install decision agrees with the tools that are actually granted. Unquoted glob patterns (e.g. `mcp__github__*`), which shell-quote yields as glob objects, are recovered to their literal text to preserve existing behavior. Closes #1357 Co-authored-by: bymle <229636660+bymle@users.noreply.github.com>
82 lines
2.9 KiB
TypeScript
82 lines
2.9 KiB
TypeScript
import { parse as parseShellArgs } from "shell-quote";
|
|
|
|
// Flags whose values make up the allowed-tools list.
|
|
// Include both camelCase and hyphenated variants for CLI compatibility.
|
|
const ALLOWED_TOOLS_FLAGS = new Set(["allowedTools", "allowed-tools"]);
|
|
|
|
/**
|
|
* Strip comment lines from a shell argument string.
|
|
* Lines whose first non-whitespace character is `#` are removed entirely.
|
|
* Mirrors stripShellComments in base-action/src/parse-sdk-options.ts.
|
|
*/
|
|
function stripShellComments(input: string): string {
|
|
return input
|
|
.split("\n")
|
|
.filter((line) => !line.trim().startsWith("#"))
|
|
.join("\n");
|
|
}
|
|
|
|
/**
|
|
* Tokenize a claude_args string the same way base-action/src/parse-sdk-options.ts
|
|
* does: strip full comment lines, then run shell-quote. shell-quote returns
|
|
* unquoted glob patterns (e.g. `mcp__github__*`) as `{ op: "glob", pattern }`
|
|
* objects rather than strings, so recover their literal text; drop operator
|
|
* tokens (`|`, `>`, `;`, ...) which carry no value.
|
|
*/
|
|
function tokenize(claudeArgs: string): string[] {
|
|
return parseShellArgs(stripShellComments(claudeArgs))
|
|
.map((token) => {
|
|
if (typeof token === "string") return token;
|
|
if (token && typeof token === "object" && "pattern" in token) {
|
|
return (token as { pattern: string }).pattern;
|
|
}
|
|
return null;
|
|
})
|
|
.filter((token): token is string => token !== null);
|
|
}
|
|
|
|
/**
|
|
* Parse the list of allowed tool names from a user-provided claude_args string.
|
|
*
|
|
* This is used to decide which GitHub MCP servers to install. It MUST stay in
|
|
* agreement with how the actual tool list is built for the SDK in
|
|
* base-action/src/parse-sdk-options.ts (parseClaudeArgsToExtraArgs): otherwise a
|
|
* tool can be granted to Claude without its MCP server being installed, or a
|
|
* server can be installed for a tool that was never granted (#1357).
|
|
*
|
|
* To stay in agreement it uses the same shell-quote tokenizer and the same
|
|
* "an accumulating flag consumes all consecutive non-flag values" semantics,
|
|
* so `--allowedTools "Read" "Grep" "mcp__github__get_commit"` captures all
|
|
* three values, and commented-out lines are ignored.
|
|
*/
|
|
export function parseAllowedTools(claudeArgs: string): string[] {
|
|
if (!claudeArgs?.trim()) return [];
|
|
|
|
const args = tokenize(claudeArgs);
|
|
const tools: string[] = [];
|
|
const seen = new Set<string>();
|
|
|
|
for (let i = 0; i < args.length; i++) {
|
|
const arg = args[i];
|
|
if (!arg?.startsWith("--")) continue;
|
|
|
|
const flag = arg.slice(2);
|
|
if (!ALLOWED_TOOLS_FLAGS.has(flag)) continue;
|
|
|
|
// Consume all consecutive non-flag values, e.g.
|
|
// --allowedTools "Read" "Grep" "mcp__github__get_commit"
|
|
while (i + 1 < args.length && !args[i + 1]!.startsWith("--")) {
|
|
i++;
|
|
for (const tool of args[i]!.split(",")) {
|
|
const trimmed = tool.trim();
|
|
if (trimmed && !seen.has(trimmed)) {
|
|
seen.add(trimmed);
|
|
tools.push(trimmed);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return tools;
|
|
}
|