fix(plugins): detect Context7 managed by MCPorter
CI / Capabilities and shell quality (push) Successful in 12s
CI / Python tests and compilation (push) Successful in 22s
CI / CI configuration security checks (push) Successful in 4s
CI / Build and inspect generated packages (push) Successful in 13s
CI / PowerShell tests on Ubuntu (push) Successful in 1m10s
CI / Bash integration and installation guards (push) Successful in 1m38s
CI / Installed client configuration smoke tests (push) Successful in 13s
CI / Qwen retrieval integration (push) Successful in 14m24s
CI / Capabilities and shell quality (push) Successful in 12s
CI / Python tests and compilation (push) Successful in 22s
CI / CI configuration security checks (push) Successful in 4s
CI / Build and inspect generated packages (push) Successful in 13s
CI / PowerShell tests on Ubuntu (push) Successful in 1m10s
CI / Bash integration and installation guards (push) Successful in 1m38s
CI / Installed client configuration smoke tests (push) Successful in 13s
CI / Qwen retrieval integration (push) Successful in 14m24s
This commit is contained in:
1 parent
728d72312f
commit
280292d72e
4 files changed
+85
-7
No files matched your search
+21
-1
@@ -65,6 +65,24 @@ function Get-InstalledPlugins {
|
||||
}
|
||||
}
|
||||
|
||||
function Test-InstalledMCPServer {
|
||||
param([string]$HomePath, [string]$Name, [string]$Url)
|
||||
$path = Join-Path $HomePath '.mcporter/mcporter.json'
|
||||
foreach ($candidate in @((Split-Path $path), $path)) {
|
||||
if ((Test-Path -LiteralPath $candidate) -and ((Get-Item -LiteralPath $candidate -Force).Attributes -band [System.IO.FileAttributes]::ReparsePoint)) { throw 'MCPorter configuration path contains a link.' }
|
||||
}
|
||||
if (-not (Test-Path -LiteralPath $path -PathType Leaf)) { return $false }
|
||||
$config = Get-Content -LiteralPath $path -Raw | ConvertFrom-Json
|
||||
if ($config -isnot [pscustomobject]) { throw 'Invalid MCPorter server configuration.' }
|
||||
$servers = $config.PSObject.Properties['mcpServers']
|
||||
if ($null -eq $servers) { return $false }
|
||||
if ($servers.Value -isnot [pscustomobject]) { throw 'Invalid MCPorter server configuration.' }
|
||||
$server = $servers.Value.PSObject.Properties[$Name]
|
||||
if ($null -eq $server) { return $false }
|
||||
if ($server.Value -isnot [pscustomobject]) { throw 'Invalid MCPorter server entry.' }
|
||||
return $server.Value.baseUrl -eq $Url
|
||||
}
|
||||
|
||||
function Read-PluginToggleSelection {
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][string[]]$Names,
|
||||
@@ -102,7 +120,9 @@ function Select-ConfiguredPlugins {
|
||||
if ($Mode -eq 'Install') { $true; return }
|
||||
if ($_.codex_method -eq 'cli') { return $null -ne (Get-Command $_.codex_source -ErrorAction SilentlyContinue) }
|
||||
$entry = $_
|
||||
$found = $false
|
||||
$found = if ($entry.mcporter_name -and $entry.mcporter_url) {
|
||||
Test-InstalledMCPServer -HomePath $HomePath -Name $entry.mcporter_name -Url $entry.mcporter_url
|
||||
} else { $false }
|
||||
foreach ($referenceClient in $referenceClients) {
|
||||
$selector = if ($referenceClient -eq 'Claude') { $entry.claude_plugin } else { $entry.codex_plugin }
|
||||
if ($referenceClient -eq 'Codex' -and $entry.codex_method -eq 'qmd') {
|
||||
|
||||
+34
-3
@@ -56,9 +56,9 @@ get_plugin_names() {
|
||||
plugin_field() {
|
||||
local root=$1 target=$2 field=$3
|
||||
# shellcheck disable=SC2034
|
||||
local name claude_marketplace claude_plugin codex_marketplace codex_plugin codex_method codex_source codex_skill
|
||||
local name claude_marketplace claude_plugin codex_marketplace codex_plugin codex_method codex_source codex_skill mcporter_name mcporter_url
|
||||
# shellcheck disable=SC2034
|
||||
while IFS=$'\t' read -r name claude_marketplace claude_plugin codex_marketplace codex_plugin codex_method codex_source codex_skill; do
|
||||
while IFS=$'\t' read -r name claude_marketplace claude_plugin codex_marketplace codex_plugin codex_method codex_source codex_skill mcporter_name mcporter_url; do
|
||||
[ "$name" = "$target" ] || continue
|
||||
printf '%s\n' "${!field}"
|
||||
return 0
|
||||
@@ -66,6 +66,30 @@ plugin_field() {
|
||||
return 1
|
||||
}
|
||||
|
||||
mcporter_server_installed() {
|
||||
local path="$1/.mcporter/mcporter.json" server=$2 url=$3
|
||||
if [ -L "$1/.mcporter" ] || [ -L "$path" ]; then printf 'MCPorter configuration path contains a link.\n' >&2; return 2; fi
|
||||
[ -f "$path" ] || return 1
|
||||
python3 -c '
|
||||
import json, sys
|
||||
try:
|
||||
with open(sys.argv[1], encoding="utf-8-sig") as stream:
|
||||
config = json.load(stream)
|
||||
if not isinstance(config, dict):
|
||||
raise ValueError("Invalid MCPorter server configuration")
|
||||
servers = config.get("mcpServers", {})
|
||||
if not isinstance(servers, dict):
|
||||
raise ValueError("Invalid MCPorter server configuration")
|
||||
entry = servers.get(sys.argv[2])
|
||||
if entry is not None and not isinstance(entry, dict):
|
||||
raise ValueError("Invalid MCPorter server entry")
|
||||
sys.exit(0 if entry is not None and entry.get("baseUrl") == sys.argv[3] else 1)
|
||||
except (OSError, ValueError, AttributeError) as error:
|
||||
print("Invalid MCPorter configuration: " + str(error), file=sys.stderr)
|
||||
sys.exit(2)
|
||||
' "$path" "$server" "$url"
|
||||
}
|
||||
|
||||
plugin_toggleable() {
|
||||
return 0
|
||||
}
|
||||
@@ -128,6 +152,13 @@ select_configured_plugins() {
|
||||
continue
|
||||
fi
|
||||
found=false
|
||||
local mcporter_name mcporter_url status
|
||||
mcporter_name=$(plugin_field "$root" "$name" mcporter_name)
|
||||
mcporter_url=$(plugin_field "$root" "$name" mcporter_url)
|
||||
if [ -n "$mcporter_name" ] && [ "$mcporter_name" != - ] && [ -n "$mcporter_url" ] && [ "$mcporter_url" != - ]; then
|
||||
if mcporter_server_installed "${home_path:-$HOME}" "$mcporter_name" "$mcporter_url"; then found=true
|
||||
else status=$?; [ "$status" -eq 1 ] || return "$status"; fi
|
||||
fi
|
||||
for reference_client in "${reference_clients[@]}"; do
|
||||
if [ "$reference_client" = claude ]; then selector=$(plugin_field "$root" "$name" claude_plugin); else selector=$(plugin_field "$root" "$name" codex_plugin); fi
|
||||
if [ "$reference_client" = codex ] && [ "$(plugin_field "$root" "$name" codex_method)" = qmd ]; then
|
||||
@@ -138,7 +169,7 @@ select_configured_plugins() {
|
||||
{ [ -d "$HOME/.agents/skills/$skill_name" ] || [ -d "$HOME/.codex/skills/$skill_name" ]; } && found=true
|
||||
elif plugin_installed "$reference_client" "$selector"; then found=true
|
||||
else
|
||||
local status=$?
|
||||
status=$?
|
||||
[ "$status" -eq 1 ] || return "$status"
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -37,7 +37,8 @@ catch { $failed = $true }
|
||||
$env:AI_CONFIG_VERBOSE = $null
|
||||
if (-not $failed -or $diagnostics -notcontains 'download progress' -or $diagnostics -notcontains 'WARN test warning') { throw 'Plugin failure must retain full diagnostics and fail.' }
|
||||
$global:LASTEXITCODE = 0
|
||||
function codex { $script:observedCodexHome = $env:CODEX_HOME; $script:observedProfile = $env:USERPROFILE; '{"installed":[{"pluginId":"ponytail@ponytail"},{"pluginId":"i-have-adhd@i-have-adhd"},{"pluginId":"superpowers@openai-curated-remote"},{"pluginId":"context7@context7-marketplace"},{"pluginId":"caveman@thinkhome-caveman"}],"available":[]}' }
|
||||
$script:codexInventory = '{"installed":[{"pluginId":"ponytail@ponytail"},{"pluginId":"i-have-adhd@i-have-adhd"},{"pluginId":"superpowers@openai-curated-remote"},{"pluginId":"context7@context7-marketplace"},{"pluginId":"caveman@thinkhome-caveman"}],"available":[]}'
|
||||
function codex { $script:observedCodexHome = $env:CODEX_HOME; $script:observedProfile = $env:USERPROFILE; $script:codexInventory }
|
||||
$testHome = Join-Path ([System.IO.Path]::GetTempPath()) 'ai-config-plugin-test-home'
|
||||
$previousCodexHome = [Environment]::GetEnvironmentVariable('CODEX_HOME', 'Process')
|
||||
$previousProfile = [Environment]::GetEnvironmentVariable('USERPROFILE', 'Process')
|
||||
@@ -51,6 +52,20 @@ $script:answers.Enqueue('4')
|
||||
$script:answers.Enqueue('done')
|
||||
$selection = Select-ConfiguredPlugins -RepositoryRoot (Split-Path (Split-Path $PSScriptRoot -Parent) -Parent) -Client Codex -Mode Update
|
||||
if (@($selection.Deselected | Where-Object { $_.name -eq 'Context7' }).Count -ne 1) { throw 'Only the explicitly unchecked installed plugin must be deselected.' }
|
||||
$mcporterHome = Join-Path (Split-Path (Split-Path $PSScriptRoot -Parent) -Parent) ('.context7-test-' + [Guid]::NewGuid())
|
||||
try {
|
||||
$configPath = Join-Path $mcporterHome '.mcporter/mcporter.json'
|
||||
New-Item (Split-Path $configPath) -ItemType Directory -Force | Out-Null
|
||||
[System.IO.File]::WriteAllText($configPath, '{"mcpServers":{"context7":{"baseUrl":"https://mcp.context7.com/mcp"}}}')
|
||||
$script:codexInventory = '{"installed":[],"available":[]}'
|
||||
$script:answers.Enqueue('done')
|
||||
$selection = Select-ConfiguredPlugins -RepositoryRoot $root -Client Codex -Mode Update -HomePath $mcporterHome
|
||||
if (@($selection.Selected | Where-Object { $_.name -eq 'Context7' }).Count -ne 1) { throw 'MCPorter Context7 must start checked when updating.' }
|
||||
[System.IO.File]::WriteAllText($configPath, '{"mcpServers":{"context7":{"baseUrl":"https://example.invalid/mcp"}}}')
|
||||
$script:answers.Enqueue('done')
|
||||
$selection = Select-ConfiguredPlugins -RepositoryRoot $root -Client Codex -Mode Update -HomePath $mcporterHome
|
||||
if (@($selection.Deselected | Where-Object { $_.name -eq 'Context7' }).Count -ne 1) { throw 'A different MCPorter server must not select Context7.' }
|
||||
} finally { Remove-Item -LiteralPath $mcporterHome -Recurse -Force -ErrorAction SilentlyContinue }
|
||||
$script:managedArguments = @()
|
||||
function python { $script:managedArguments = @($args); $global:LASTEXITCODE = 0 }
|
||||
$entry = [pscustomobject]@{ name = 'Test'; codex_method = 'plugin'; codex_marketplace = '-'; codex_plugin = 'test@market' }
|
||||
|
||||
@@ -14,8 +14,9 @@ root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)
|
||||
# shellcheck disable=SC2034
|
||||
names=(A B C)
|
||||
checked=(true false true)
|
||||
toggle_log=$(mktemp)
|
||||
trap 'rm -f -- "$toggle_log"' EXIT
|
||||
toggle_log=$(mktemp "$root/.plugin-toggle.XXXXXX")
|
||||
test_home=$(mktemp -d "$root/.context7-test.XXXXXX")
|
||||
trap 'rm -f -- "$toggle_log" "$test_home/.mcporter/mcporter.json"; rmdir -- "$test_home/.mcporter" "$test_home" 2>/dev/null || true' EXIT
|
||||
read_plugin_toggle_selection names checked <<< $'1\n2\ndone' > "$toggle_log"
|
||||
[ "$summary" = true ] || cat "$toggle_log"
|
||||
|
||||
@@ -36,6 +37,17 @@ if diagnostics=$(summary=true run_plugin_command false test_plugin_output fail 2
|
||||
exit 1
|
||||
fi
|
||||
[[ "$diagnostics" == *'download progress'* ]] && [[ "$diagnostics" == *'WARN test warning'* ]]
|
||||
mkdir -p "$test_home/.mcporter"
|
||||
printf '%s\n' '{"mcpServers":{"context7":{"baseUrl":"https://mcp.context7.com/mcp"}}}' > "$test_home/.mcporter/mcporter.json"
|
||||
home_path=$test_home
|
||||
plugin_installed() { return 1; }
|
||||
selected=()
|
||||
deselected=()
|
||||
select_configured_plugins "$root" codex Update true selected deselected
|
||||
[[ " ${selected[*]} " == *' Context7 '* ]] || { printf 'MCPorter Context7 must start checked when updating.\n' >&2; exit 1; }
|
||||
printf '%s\n' '{"mcpServers":{"context7":{"baseUrl":"https://example.invalid/mcp"}}}' > "$test_home/.mcporter/mcporter.json"
|
||||
select_configured_plugins "$root" codex Update true selected deselected
|
||||
[[ " ${deselected[*]} " == *' Context7 '* ]] || { printf 'A different MCPorter server must not select Context7.\n' >&2; exit 1; }
|
||||
# shellcheck disable=SC2034
|
||||
selected=()
|
||||
plugin_installed() { printf 'Empty selection must not query installed plugins.\n' >&2; exit 1; }
|
||||
|
||||
Reference in new issue
Block a user