Compare commits

..
6 Commits
Author SHA1 Message Date
fraujulian 0108257290 test(packages): enforce MCPorter policy
CI / Capabilities and shell quality (push) Successful in 12s
CI / Python tests and compilation (push) Successful in 11s
CI / PowerShell tests on Ubuntu (push) Failing after 17s
CI / CI configuration security checks (push) Successful in 3s
CI / Build and inspect generated packages (push) Successful in 6s
CI / Qwen retrieval integration (push) Failing after 9m44s
CI / Bash integration and installation guards (push) Successful in 15s
CI / Installed client configuration smoke tests (push) Failing after 12s
2026-09-18 12:40:11 +02:00
fraujulian e28a4171ea feat(instructions): require MCPorter for MCPs 2026-09-18 12:40:00 +02:00
fraujulian d7304e2b64 test(mcporter): cover Context7 routing 2026-09-18 12:39:51 +02:00
fraujulian 70414def48 feat(mcporter): route Context7 through CLI 2026-09-18 12:39:43 +02:00
fraujulian cb6cebb3d4 ci(retrieval): disable unstable Xet downloads 2026-09-18 12:23:10 +02:00
fraujulian 9e74b9be4a fix(capabilities): read Python exit code directly 2026-09-18 12:22:51 +02:00
11 changed files with 71 additions and 8 deletions

No files matched your search

+1
View File
@@ -172,6 +172,7 @@ jobs:
- name: Run retrieval tests with real models
env:
QWEN_TEST_MODEL_CACHE: ${{ github.workspace }}/.cache/huggingface
HF_HUB_DISABLE_XET: '1'
run: python scripts/tests/test-retrieval-server.py
security:
+1
View File
@@ -172,6 +172,7 @@ jobs:
- name: Run retrieval tests with real models
env:
QWEN_TEST_MODEL_CACHE: ${{ github.workspace }}/.cache/huggingface
HF_HUB_DISABLE_XET: '1'
run: python scripts/tests/test-retrieval-server.py
security:
+2 -2
View File
@@ -1,8 +1,8 @@
name claude_marketplace claude_plugin codex_marketplace codex_plugin codex_method codex_source codex_skill
name claude_marketplace claude_plugin codex_marketplace codex_plugin codex_method codex_source codex_skill mcporter_name mcporter_url
Ponytail DietrichGebert/ponytail ponytail@ponytail DietrichGebert/ponytail ponytail@ponytail plugin - -
i-have-adhd ayghri/i-have-adhd i-have-adhd@i-have-adhd ayghri/i-have-adhd i-have-adhd@i-have-adhd plugin - -
Superpowers - superpowers@claude-plugins-official openai-curated-remote superpowers@openai-curated-remote plugin - -
Context7 upstash/context7 context7@context7-marketplace upstash/context7 context7@context7-marketplace plugin - -
Context7 upstash/context7 context7@context7-marketplace upstash/context7 context7@context7-marketplace plugin - - context7 https://mcp.context7.com/mcp
Caveman JuliusBrussee/caveman caveman@caveman thinkhome-org/caveman-codex caveman@thinkhome-caveman plugin - -
QMD tobi/qmd qmd@qmd - - qmd @tobilu/qmd qmd
MCPorter - mcporter - mcporter cli mcporter -
1 name claude_marketplace claude_plugin codex_marketplace codex_plugin codex_method codex_source codex_skill mcporter_name mcporter_url
2 Ponytail DietrichGebert/ponytail ponytail@ponytail DietrichGebert/ponytail ponytail@ponytail plugin - -
3 i-have-adhd ayghri/i-have-adhd i-have-adhd@i-have-adhd ayghri/i-have-adhd i-have-adhd@i-have-adhd plugin - -
4 Superpowers - superpowers@claude-plugins-official openai-curated-remote superpowers@openai-curated-remote plugin - -
5 Context7 upstash/context7 context7@context7-marketplace upstash/context7 context7@context7-marketplace plugin - - context7 https://mcp.context7.com/mcp
6 Caveman JuliusBrussee/caveman caveman@caveman thinkhome-org/caveman-codex caveman@thinkhome-caveman plugin - -
7 QMD tobi/qmd qmd@qmd - - qmd @tobilu/qmd qmd
8 MCPorter - mcporter - mcporter cli mcporter -
+3 -2
View File
@@ -13,8 +13,9 @@ Require-Command jq
$pythonVersion = $null
foreach ($python in @('python', 'python3')) {
if (-not (Get-Command $python -ErrorAction SilentlyContinue)) { continue }
$candidate = (& $python --version 2>$null | Select-Object -First 1) -replace '^Python\s+', ''
if ($LASTEXITCODE -eq 0) { $pythonVersion = $candidate; break }
$candidate = & $python --version 2>$null
$pythonExitCode = $LASTEXITCODE
if ($pythonExitCode -eq 0) { $pythonVersion = $candidate -replace '^Python\s+', ''; break }
}
if ($null -ne $pythonVersion) { Check-Version 'Python' $pythonVersion ([version]'3.11') } else { Fail 'Python 3.11 or newer is required' }
+28 -4
View File
@@ -166,7 +166,7 @@ class Manager:
raise ValueError('Invalid extension ownership ledger.')
identities = set()
for resource in self.state['resources']:
if not isinstance(resource, dict) or resource.get('client') not in ('codex', 'claude') or resource.get('kind') not in ('plugin', 'skill', 'qmd', 'cli'):
if not isinstance(resource, dict) or resource.get('client') not in ('codex', 'claude', 'shared') or resource.get('kind') not in ('plugin', 'skill', 'qmd', 'cli', 'mcp'):
raise ValueError('Invalid extension ownership record.')
if not isinstance(resource.get('name'), str) or not resource['name']:
raise ValueError('Invalid extension ownership name.')
@@ -196,6 +196,8 @@ class Manager:
raise ValueError('Invalid owned QMD record.')
elif resource['kind'] == 'cli' and not re.fullmatch(r'[a-z0-9@/_.-]+', resource.get('package', '')):
raise ValueError('Invalid owned CLI record.')
elif resource['kind'] == 'mcp' and (resource['client'] != 'shared' or not re.fullmatch(r'[a-z0-9-]+', resource.get('server', '')) or not re.fullmatch(r'https://[^\s]+', resource.get('url', ''))):
raise ValueError('Invalid owned MCP record.')
def save(self):
if not self.dry_run:
@@ -258,6 +260,8 @@ class Manager:
pass
if record['files']:
return
elif record['kind'] == 'mcp':
self.command(['mcporter', '--config', str(self.home / '.mcporter/mcporter.json'), 'config', 'remove', record['server']])
else:
package = record['package']
self.command(['npm', 'uninstall', '--global', package])
@@ -361,6 +365,20 @@ class Manager:
self.state['resources'].append(dict(client='codex', name=entry['name'], kind='qmd', package=package))
self.save()
def ensure_mcporter(self, entry):
server, url = entry['mcporter_name'], entry['mcporter_url']
if not re.fullmatch(r'[a-z0-9-]+', server) or not re.fullmatch(r'https://[^\s]+', url):
raise ValueError('Invalid MCPorter server definition.')
record = next((r for r in self.state['resources'] if r['client'] == 'shared' and r['name'] == entry['name']), None)
if record is not None and not self.update:
return
self.command(['mcporter', '--config', str(self.home / '.mcporter/mcporter.json'), 'config', 'add', server, url])
if record is None:
self.state['resources'].append(dict(client='shared', name=entry['name'], kind='mcp', server=server, url=url))
else:
record.update(server=server, url=url)
self.save()
def ensure_skill(self, entry, record):
skill = entry['codex_skill']
if not re.fullmatch(r'[A-Za-z0-9_-]+', skill):
@@ -417,15 +435,21 @@ class Manager:
def sync(self, entries, clients, selected, action='sync'):
desired = {entry['name']: entry for entry in entries if entry['name'] in selected}
use_mcporter = 'MCPorter' in desired
mcporter_entries = {name: entry for name, entry in desired.items() if use_mcporter and entry.get('mcporter_name') and entry.get('mcporter_url') not in ('', '-')}
direct_entries = {name: entry for name, entry in desired.items() if name not in mcporter_entries}
for record in list(self.state['resources']):
if record['client'] not in clients:
if record['client'] != 'shared' and record['client'] not in clients:
continue
if (action == 'sync' and record['name'] not in desired) or (action == 'remove' and record['name'] in selected):
selected_entries = mcporter_entries if record['client'] == 'shared' else direct_entries
if (action == 'sync' and record['name'] not in selected_entries) or (action == 'remove' and record['name'] in selected):
self.remove(record)
if action != 'remove':
for client in clients:
for entry in desired.values():
for entry in direct_entries.values():
self.ensure(client, entry)
for entry in mcporter_entries.values():
self.ensure_mcporter(entry)
print('EXTENSIONS: reconciliation complete' + (' (dry run)' if self.dry_run else ''))
+11
View File
@@ -0,0 +1,11 @@
[CmdletBinding()]
param([switch]$Summary)
$ErrorActionPreference = 'Stop'
$root = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent
$content = Get-Content (Join-Path $root 'scripts/commands/check-capabilities.ps1') -Raw
if ($content -match '(?s)\(& \$python --version.*?Select-Object.*?\).*?\$LASTEXITCODE') {
throw 'Python capability detection must not read $LASTEXITCODE after a pipeline.'
}
if ($Summary) { Write-Output 'Tests: PASS | capabilities' } else { Write-Output 'PASS capabilities: Python exit code is read without a pipeline' }
+16
View File
@@ -23,6 +23,16 @@ def skill():
return dict(name='Humanizer', codex_method='skill', codex_source='blader/humanizer', codex_skill='humanizer', codex_plugin='-')
def mcporter():
return dict(name='MCPorter', codex_method='cli', codex_source='mcporter', codex_plugin='-', codex_marketplace='-', claude_plugin='-', claude_marketplace='-')
def mcp():
return dict(name='Context7', codex_method='plugin', codex_plugin='context7@test', codex_marketplace='-',
claude_plugin='context7@test', claude_marketplace='-', mcporter_name='context7',
mcporter_url='https://mcp.context7.com/mcp')
class ManagedExtensionTests(unittest.TestCase):
def setUp(self):
self.temporary = tempfile.TemporaryDirectory()
@@ -186,6 +196,12 @@ class ManagedExtensionTests(unittest.TestCase):
self.assertIn(['claude', 'plugin', 'install', 'qmd@qmd', '--scope', 'user'], self.commands)
self.assertNotIn(['codex', 'mcp', 'add', 'qmd', '--', 'qmd', 'mcp'], self.commands)
def test_mcporter_replaces_direct_context7_plugin(self):
self.manager().sync([mcp(), mcporter()], ['codex'], {'Context7', 'MCPorter'})
config = str(self.home / '.mcporter/mcporter.json')
self.assertIn(['mcporter', '--config', config, 'config', 'add', 'context7', 'https://mcp.context7.com/mcp'], self.commands)
self.assertNotIn(['codex', 'plugin', 'add', 'context7@test'], self.commands)
def test_foreign_empty_directory_survives_skill_removal(self):
self.manager().sync([skill()], ['codex'], {'Humanizer'})
foreign = self.home / '.agents/skills/humanizer/foreign-empty'
+5
View File
@@ -27,6 +27,11 @@ def embedding():
class RetrievalServerTests(unittest.TestCase):
def test_ci_disables_xet_for_model_downloads(self):
for workflow in ('.github/workflows/ci.yml', '.gitea/workflows/ci.yml'):
content = (ROOT / workflow).read_text(encoding='utf-8')
self.assertIn("HF_HUB_DISABLE_XET: '1'", content)
def test_runtime_python_uses_adjacent_virtualenv(self):
with TemporaryDirectory() as temporary:
script = Path(temporary) / 'semantic-retrieval' / 'server.py'
+1
View File
@@ -55,6 +55,7 @@ foreach ($shell in @('powershell','bash')) {
}
$docFile = if ($client -eq 'codex') { 'AGENTS.md' } else { 'CLAUDE.md' }
$docContent = Get-Content -LiteralPath (Join-Path $package $docFile) -Raw
if ($docContent -notmatch [regex]::Escape('Use MCP services only through the `mcporter` CLI.')) { throw "$package must require MCPorter for MCP services" }
if (([regex]::Matches($docContent, [regex]::Escape('Apply instructions in this order'))).Count -ne 1) { throw "$client-$shell must embed the priority rule exactly once" }
if ($client -eq 'codex' -and $docContent -match 'Always load and apply `rules/general\.md`') { throw "$package must not still instruct loading general.md by path" }
if ($client -eq 'claude' -and (Test-Path (Join-Path $package 'rules/general.md'))) { throw 'Claude must not automatically load a second copy of general rules.' }
+1
View File
@@ -75,6 +75,7 @@ for shell in powershell bash; do
doc_file=AGENTS.md
[ "$client" != claude ] || doc_file=CLAUDE.md
doc_content=$(cat "$package/$doc_file")
printf '%s' "$doc_content" | grep -Fq 'Use MCP services only through the `mcporter` CLI.'
occurrences=$(printf '%s' "$doc_content" | grep -o 'Apply instructions in this order' | wc -l)
[ "$occurrences" -eq 1 ] || { printf '%s-%s must embed the priority rule exactly once\n' "$client" "$shell" >&2; exit 1; }
if [ "$client" = codex ] && printf '%s' "$doc_content" | grep -q 'Always load and apply `rules/general\.md`'; then
+2
View File
@@ -4,4 +4,6 @@ This file is generated into client-specific instruction files. Do not edit gener
__RULE_LOADING__
Use MCP services only through the `mcporter` CLI. Do not invoke direct MCP tools or create, edit, or use native MCP connections, including `/mcp`. If MCPorter or its required server is unavailable, report that limitation instead of bypassing it.
Main agent owns state and decisions. Delegate only for independent work where benefit exceeds context/tool cost. Use one implementer for contained work; subagents never spawn agents. Pass minimal context and verify handoffs.