fix(extensions): retain servers for active clients
This commit is contained in:
1 parent
eeb437e234
commit
5c1b8fedba
2 files changed
+15
-4
No files matched your search
@@ -14,7 +14,7 @@
|
|||||||
## Menus and reconciliation
|
## Menus and reconciliation
|
||||||
|
|
||||||
- [x] Build update-menu checks per client when `Both` is selected. Both menus inspect Claude's installed plugins for every entry, so a Codex resource can start unchecked and the combined sync can remove it. (`scripts/lib/plugins.ps1:98`, `scripts/lib/plugins.sh:108`)
|
- [x] Build update-menu checks per client when `Both` is selected. Both menus inspect Claude's installed plugins for every entry, so a Codex resource can start unchecked and the combined sync can remove it. (`scripts/lib/plugins.ps1:98`, `scripts/lib/plugins.sh:108`)
|
||||||
- [ ] Keep shared MCPorter servers until neither client needs them. Reconciliation processes `shared` records even when only one client is selected; uninstalling that client with an empty selection removes servers still used by the other. (`scripts/lib/managed-extensions.py:463`, `scripts/commands/uninstall.ps1:59`, `scripts/commands/uninstall.sh:77`)
|
- [x] Keep shared MCPorter servers until neither client needs them. Reconciliation processes `shared` records even when only one client is selected; uninstalling that client with an empty selection removes servers still used by the other. (`scripts/lib/managed-extensions.py:463`, `scripts/commands/uninstall.ps1:59`, `scripts/commands/uninstall.sh:77`)
|
||||||
- [ ] Manage global packages once across clients. With `--client both`, MCPorter is installed and recorded twice; QMD's global package can be removed during a Codex-only deselection while Claude still uses its plugin. (`scripts/lib/managed-extensions.py:342`, `scripts/lib/managed-extensions.py:378`, `scripts/lib/managed-extensions.py:463`)
|
- [ ] Manage global packages once across clients. With `--client both`, MCPorter is installed and recorded twice; QMD's global package can be removed during a Codex-only deselection while Claude still uses its plugin. (`scripts/lib/managed-extensions.py:342`, `scripts/lib/managed-extensions.py:378`, `scripts/lib/managed-extensions.py:463`)
|
||||||
- [x] Fix Bash `auto` semantic retrieval selection. Both `test_semantic_retrieval_device()` and its caller read `$?` after a completed `if`, losing the benchmark's exit code; an unsuitable device produces an empty choice instead of `false`. (`scripts/lib/semantic-retrieval.sh:13`, `scripts/lib/install-options.sh:52`)
|
- [x] Fix Bash `auto` semantic retrieval selection. Both `test_semantic_retrieval_device()` and its caller read `$?` after a completed `if`, losing the benchmark's exit code; an unsuitable device produces an empty choice instead of `false`. (`scripts/lib/semantic-retrieval.sh:13`, `scripts/lib/install-options.sh:52`)
|
||||||
- [x] Make ordinary update dry-runs use the saved/current selection. Both menus bypass selection in dry-run and select every manifest entry, so the preview can diverge from the real update. (`scripts/lib/plugins.ps1:91`, `scripts/lib/plugins.sh:97`)
|
- [x] Make ordinary update dry-runs use the saved/current selection. Both menus bypass selection in dry-run and select every manifest entry, so the preview can diverge from the real update. (`scripts/lib/plugins.ps1:91`, `scripts/lib/plugins.sh:97`)
|
||||||
|
|||||||
@@ -202,6 +202,8 @@ class Manager:
|
|||||||
raise ValueError('Invalid owned CLI record.')
|
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', ''))):
|
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.')
|
raise ValueError('Invalid owned MCP record.')
|
||||||
|
if 'clients' in resource and (not isinstance(resource['clients'], list) or any(client not in ('codex', 'claude') for client in resource['clients'])):
|
||||||
|
raise ValueError('Invalid shared extension clients.')
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
if not self.dry_run:
|
if not self.dry_run:
|
||||||
@@ -414,7 +416,7 @@ class Manager:
|
|||||||
self.state['resources'].append(dict(client='codex', name=entry['name'], kind='qmd', package=package))
|
self.state['resources'].append(dict(client='codex', name=entry['name'], kind='qmd', package=package))
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
def ensure_mcporter(self, entry):
|
def ensure_mcporter(self, entry, clients):
|
||||||
server, url = entry['mcporter_name'], entry['mcporter_url']
|
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):
|
if not re.fullmatch(r'[a-z0-9-]+', server) or not re.fullmatch(r'https://[^\s]+', url):
|
||||||
raise ValueError('Invalid MCPorter server definition.')
|
raise ValueError('Invalid MCPorter server definition.')
|
||||||
@@ -434,11 +436,14 @@ class Manager:
|
|||||||
return
|
return
|
||||||
if existing is not None and record is not None and existing.get('baseUrl') != record['url']:
|
if existing is not None and record is not None and existing.get('baseUrl') != record['url']:
|
||||||
raise ValueError('Owned MCPorter server changed locally; refusing to overwrite it.')
|
raise ValueError('Owned MCPorter server changed locally; refusing to overwrite it.')
|
||||||
|
if record is not None:
|
||||||
|
record['clients'] = sorted(set(record.get('clients', ['claude', 'codex'])) | set(clients))
|
||||||
|
self.save()
|
||||||
if record is not None and existing is not None and not self.update:
|
if record is not None and existing is not None and not self.update:
|
||||||
return
|
return
|
||||||
self.command(['mcporter', '--config', str(self.home / '.mcporter/mcporter.json'), 'config', 'add', server, url])
|
self.command(['mcporter', '--config', str(self.home / '.mcporter/mcporter.json'), 'config', 'add', server, url])
|
||||||
if record is None:
|
if record is None:
|
||||||
self.state['resources'].append(dict(client='shared', name=entry['name'], kind='mcp', server=server, url=url))
|
self.state['resources'].append(dict(client='shared', name=entry['name'], kind='mcp', server=server, url=url, clients=sorted(clients)))
|
||||||
else:
|
else:
|
||||||
record.update(server=server, url=url)
|
record.update(server=server, url=url)
|
||||||
self.save()
|
self.save()
|
||||||
@@ -507,6 +512,12 @@ class Manager:
|
|||||||
continue
|
continue
|
||||||
selected_entries = mcporter_entries if record['client'] == 'shared' else direct_entries
|
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):
|
if (action == 'sync' and record['name'] not in selected_entries) or (action == 'remove' and record['name'] in selected):
|
||||||
|
if record['kind'] == 'mcp':
|
||||||
|
remaining = [client for client in record.get('clients', ['claude', 'codex']) if client not in clients]
|
||||||
|
if remaining:
|
||||||
|
record['clients'] = remaining
|
||||||
|
self.save()
|
||||||
|
continue
|
||||||
self.remove(record)
|
self.remove(record)
|
||||||
if action != 'remove':
|
if action != 'remove':
|
||||||
for client in clients:
|
for client in clients:
|
||||||
@@ -515,7 +526,7 @@ class Manager:
|
|||||||
if client == 'codex' and entry.get('codex_plugin') == 'caveman@thinkhome-caveman':
|
if client == 'codex' and entry.get('codex_plugin') == 'caveman@thinkhome-caveman':
|
||||||
self.repair_caveman_windows_hooks()
|
self.repair_caveman_windows_hooks()
|
||||||
for entry in mcporter_entries.values():
|
for entry in mcporter_entries.values():
|
||||||
self.ensure_mcporter(entry)
|
self.ensure_mcporter(entry, clients)
|
||||||
print('EXTENSIONS: reconciliation complete' + (' (dry run)' if self.dry_run else ''))
|
print('EXTENSIONS: reconciliation complete' + (' (dry run)' if self.dry_run else ''))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user