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
|
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) {
|
function Get-ComparableJson($Mcp) {
|
||||||
$envMap = [ordered]@{}
|
$envMap = [ordered]@{}
|
||||||
foreach ($key in @($Mcp.env.Keys) | Sort-Object) {
|
foreach ($key in @($Mcp.env.Keys) | Sort-Object) {
|
||||||
@@ -126,10 +136,13 @@ function Get-ComparableJson($Mcp) {
|
|||||||
foreach ($key in @($Mcp.headers.Keys) | Sort-Object) {
|
foreach ($key in @($Mcp.headers.Keys) | Sort-Object) {
|
||||||
$headers[$key] = [string]$Mcp.headers[$key]
|
$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]@{
|
$value = [ordered]@{
|
||||||
transport = $Mcp.transport
|
transport = $Mcp.transport
|
||||||
command = $Mcp.command
|
command = (ConvertTo-PortablePath $Mcp.command)
|
||||||
args = @($Mcp.args)
|
args = @(@($Mcp.args) | ForEach-Object { ConvertTo-PortablePath $_ })
|
||||||
env = $envMap
|
env = $envMap
|
||||||
url = $Mcp.url
|
url = $Mcp.url
|
||||||
headers = $headers
|
headers = $headers
|
||||||
@@ -175,21 +188,32 @@ function Add-ToCodex($Mcp) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Add-ToClaude($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") {
|
if ($Mcp.transport -eq "stdio") {
|
||||||
$envMap = [ordered]@{}
|
$arguments = @("mcp", "add", $Mcp.name, "--scope", "user")
|
||||||
foreach ($key in $Mcp.env.Keys) {
|
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 }
|
$arguments += "--"
|
||||||
} else {
|
$arguments += (ConvertTo-PortablePath $Mcp.command)
|
||||||
if ($Mcp.bearer_token_env_var) {
|
foreach ($arg in @($Mcp.args)) { $arguments += (ConvertTo-PortablePath $arg) }
|
||||||
throw "Codex bearer_token_env_var has no lossless Claude JSON translation: $($Mcp.name)"
|
Invoke-External "claude add $($Mcp.name)" "claude" $arguments
|
||||||
}
|
return
|
||||||
$cfg = [ordered]@{ type = $Mcp.transport; url = $Mcp.url }
|
|
||||||
if ($Mcp.headers.Count -gt 0) { $cfg.headers = $Mcp.headers }
|
|
||||||
}
|
}
|
||||||
$json = $cfg | ConvertTo-Json -Depth 8 -Compress
|
if ($Mcp.bearer_token_env_var) {
|
||||||
Invoke-External "claude add $($Mcp.name)" "claude" @("mcp", "add-json", "--scope", "user", $Mcp.name, $json)
|
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) {
|
function Remove-FromClaude([string]$Name) {
|
||||||
|
|||||||
Reference in New Issue
Block a user