sync: SHANGHAI-U9 2026-07-27 修复 MCP 双向同步向 Claude 注册失败
Add-ToClaude 改用 `claude mcp add ... --` 标志形式,不再走 add-json: Windows PowerShell 5.1 会吞掉传给原生 exe 的 JSON 参数里的双引号, claude.exe 收到残缺 JSON 后报 "Invalid configuration: : Invalid input"。 另外 add-json 即使在引号完好时也拒绝反斜杠路径,故新增 ConvertTo-PortablePath,把带盘符的路径统一成正斜杠;同一函数也用于 比较,避免 C:\x 与 C:/x 被误判成 drift。 实测:drawio(Codex 独有)现已能由 -Apply 自动补齐到 Claude 并连通。
This commit is contained in:
@@ -116,6 +116,16 @@ function Get-PortableMcps($Map, [string]$Side) {
|
||||
return $result
|
||||
}
|
||||
|
||||
function ConvertTo-PortablePath([string]$Value) {
|
||||
# `claude mcp add-json` rejects Windows backslash paths ("Invalid configuration:
|
||||
# : Invalid input"); forward slashes work on Windows and are what the CLI itself
|
||||
# writes. Only rewrite strings that actually carry a drive-letter path, so flags
|
||||
# and non-path args are left alone (`--extension-file=C:\..` is rewritten too).
|
||||
if ($null -eq $Value) { return $Value }
|
||||
if ($Value -match '[A-Za-z]:\\') { return ($Value -replace '\\', '/') }
|
||||
return $Value
|
||||
}
|
||||
|
||||
function Get-ComparableJson($Mcp) {
|
||||
$envMap = [ordered]@{}
|
||||
foreach ($key in @($Mcp.env.Keys) | Sort-Object) {
|
||||
@@ -126,10 +136,13 @@ function Get-ComparableJson($Mcp) {
|
||||
foreach ($key in @($Mcp.headers.Keys) | Sort-Object) {
|
||||
$headers[$key] = [string]$Mcp.headers[$key]
|
||||
}
|
||||
# `C:\x\y` and `C:/x/y` are the same target on Windows -- the two CLIs write
|
||||
# different separators, so compare on a single normalized form or every path
|
||||
# arg reads as drift.
|
||||
$value = [ordered]@{
|
||||
transport = $Mcp.transport
|
||||
command = $Mcp.command
|
||||
args = @($Mcp.args)
|
||||
command = (ConvertTo-PortablePath $Mcp.command)
|
||||
args = @(@($Mcp.args) | ForEach-Object { ConvertTo-PortablePath $_ })
|
||||
env = $envMap
|
||||
url = $Mcp.url
|
||||
headers = $headers
|
||||
@@ -175,21 +188,32 @@ function Add-ToCodex($Mcp) {
|
||||
}
|
||||
|
||||
function Add-ToClaude($Mcp) {
|
||||
# Deliberately NOT `claude mcp add-json`: Windows PowerShell 5.1 strips the
|
||||
# double quotes out of a JSON argument on its way to a native exe, so claude.exe
|
||||
# sees malformed JSON and dies with "Invalid configuration: : Invalid input".
|
||||
# The flag form takes plain tokens and is immune. (Separately, add-json rejects
|
||||
# backslash paths even when quoting is clean -- hence ConvertTo-PortablePath.)
|
||||
if ($Mcp.transport -eq "stdio") {
|
||||
$envMap = [ordered]@{}
|
||||
$arguments = @("mcp", "add", $Mcp.name, "--scope", "user")
|
||||
foreach ($key in $Mcp.env.Keys) {
|
||||
if ($CodexOnlyEnv -notcontains $key) { $envMap[$key] = $Mcp.env[$key] }
|
||||
if ($CodexOnlyEnv -contains $key) { continue }
|
||||
$arguments += @("--env", "$key=$($Mcp.env[$key])")
|
||||
}
|
||||
$cfg = [ordered]@{ type = "stdio"; command = $Mcp.command; args = @($Mcp.args); env = $envMap }
|
||||
} else {
|
||||
if ($Mcp.bearer_token_env_var) {
|
||||
throw "Codex bearer_token_env_var has no lossless Claude JSON translation: $($Mcp.name)"
|
||||
}
|
||||
$cfg = [ordered]@{ type = $Mcp.transport; url = $Mcp.url }
|
||||
if ($Mcp.headers.Count -gt 0) { $cfg.headers = $Mcp.headers }
|
||||
$arguments += "--"
|
||||
$arguments += (ConvertTo-PortablePath $Mcp.command)
|
||||
foreach ($arg in @($Mcp.args)) { $arguments += (ConvertTo-PortablePath $arg) }
|
||||
Invoke-External "claude add $($Mcp.name)" "claude" $arguments
|
||||
return
|
||||
}
|
||||
$json = $cfg | ConvertTo-Json -Depth 8 -Compress
|
||||
Invoke-External "claude add $($Mcp.name)" "claude" @("mcp", "add-json", "--scope", "user", $Mcp.name, $json)
|
||||
if ($Mcp.bearer_token_env_var) {
|
||||
throw "Codex bearer_token_env_var has no lossless Claude translation: $($Mcp.name)"
|
||||
}
|
||||
$arguments = @("mcp", "add", $Mcp.name, "--scope", "user",
|
||||
"--transport", $Mcp.transport, $Mcp.url)
|
||||
foreach ($key in $Mcp.headers.Keys) {
|
||||
$arguments += @("--header", "$key`: $($Mcp.headers[$key])")
|
||||
}
|
||||
Invoke-External "claude add $($Mcp.name)" "claude" $arguments
|
||||
}
|
||||
|
||||
function Remove-FromClaude([string]$Name) {
|
||||
|
||||
Reference in New Issue
Block a user