mcp-wire is a CLI tool written in Go that lets users install and configure MCP (Model Context Protocol) servers across multiple AI coding CLI tools (Claude Code, Codex, Gemini CLI, OpenCode, etc.) from a single interface.
The architecture has two dimensions:
- Services: what to install (e.g., Sentry, Jira, Stripe). Defined as YAML files — no Go code needed to add one.
- Targets: where to install (e.g., Claude Code, Codex). Each target is a Go implementation that knows how to read/write that tool's config file.
The CLI combines the two: the user picks a service, the tool resolves credentials, and writes the config into one or more targets.
Interaction modes:
- Guided mode (default): running
mcp-wirewith no subcommand starts an interactive menu/wizard for common workflows. - Explicit mode (advanced/CI): command-based syntax (for example
mcp-wire install <service> --target <slug> --no-prompt) remains fully supported for scripting.
UX principle:
- Optimize for users who do not remember command syntax.
- At the end of guided flows, print the equivalent explicit command for reproducibility.
mcp-wire/
├── cmd/
│ └── root.go # Cobra root command
│ └── install.go # install command
│ └── uninstall.go # uninstall command
├── internal/
│ ├── service/
│ │ ├── service.go # Service struct, YAML parsing
│ │ └── registry.go # Discovers and lists available service definitions
│ ├── target/
│ │ ├── target.go # Target interface definition
│ │ ├── registry.go # Target discovery (which are installed on this machine)
│ │ ├── claude.go # Claude Code target implementation
│ │ └── codex.go # Codex target implementation
│ ├── credential/
│ │ ├── resolver.go # Credential resolution chain
│ │ ├── env.go # Environment variable source
│ │ └── file.go # File-based credential store
│ └── config/
│ └── config.go # mcp-wire's own config (paths, defaults)
├── services/ # Community-contributed service definitions
│ ├── sentry.yaml
│ ├── jira.yaml
│ └── context7.yaml
├── main.go # Entrypoint, calls cmd/root.go
├── go.mod
├── go.sum
└── README.md
Create internal/service/service.go with the struct that maps to service YAML files.
type Service struct {
Name string `yaml:"name"`
Description string `yaml:"description"`
Transport string `yaml:"transport"` // "sse" or "stdio"
URL string `yaml:"url,omitempty"` // for SSE transport
Command string `yaml:"command,omitempty"` // for stdio transport
Args []string `yaml:"args,omitempty"` // for stdio transport
Env []EnvVar `yaml:"env,omitempty"`
}
type EnvVar struct {
Name string `yaml:"name"`
Description string `yaml:"description"`
Required bool `yaml:"required"`
SetupURL string `yaml:"setup_url,omitempty"`
SetupHint string `yaml:"setup_hint,omitempty"`
}A service YAML file looks like this:
name: sentry
description: "Sentry error tracking MCP"
transport: sse
url: "https://mcp.sentry.dev/sse"
env:
- name: SENTRY_AUTH_TOKEN
description: "Sentry authentication token"
required: true
setup_url: "https://sentry.io/settings/account/api/auth-tokens/"
setup_hint: "Create a token with project:read and event:read scopes"name: context7
description: "Context7 documentation lookup MCP"
transport: sse
url: "https://mcp.context7.com/mcp"
env: []name: filesystem
description: "Local filesystem access MCP"
transport: stdio
command: npx
args: ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/projects"]
env: []Create internal/service/registry.go.
This module discovers and loads all .yaml files from the services/ directory. It should:
- Accept a base path (default:
services/relative to the binary, but also support~/.config/mcp-wire/services/for user-added definitions). - Read each
.yamlfile and unmarshal into aServicestruct. - Validate required fields (name, transport, and either url or command depending on transport).
- Return a map of
name → Service. - If two files define the same
name, the user-local one takes precedence.
Key function signatures:
func LoadServices(paths ...string) (map[string]Service, error)
func ValidateService(s Service) errorCreate internal/target/target.go.
type Target interface {
// Name returns the human-readable name (e.g., "Claude Code")
Name() string
// Slug returns the CLI-friendly identifier (e.g., "claude")
Slug() string
// IsInstalled checks if this CLI tool is present on the system
IsInstalled() bool
// Install writes the service config into this target's config file.
// resolvedEnv contains env var names mapped to their resolved values.
Install(svc service.Service, resolvedEnv map[string]string) error
// Uninstall removes the service config from this target's config file.
Uninstall(serviceName string) error
// List returns the names of currently configured MCP services in this target.
List() ([]string, error)
}Create internal/target/registry.go.
A simple slice of all known targets. On startup, iterate and call IsInstalled() to determine which are available.
func AllTargets() []Target
func InstalledTargets() []Target
func FindTarget(slug string) (Target, bool)Each target follows the same pattern: locate the config file, read it as JSON (preserving unknown keys), add/remove the MCP entry, write it back.
Critical rule: always preserve unknown keys. Use map[string]any or json.RawMessage when reading config files. Never deserialize into a strict struct that would drop fields the user set manually.
Create internal/target/claude.go.
Config file location: ~/.claude/settings.json (global) or .claude/settings.json (project-local). Start with global only.
Config structure (relevant portion):
{
"mcpServers": {
"sentry": {
"type": "sse",
"url": "https://mcp.sentry.dev/sse",
"env": {
"SENTRY_AUTH_TOKEN": "the-token-value"
}
}
}
}For stdio-based services:
{
"mcpServers": {
"filesystem": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/projects"]
}
}
}Implementation:
IsInstalled(): check ifclaudebinary is in PATH (useexec.LookPath).Install(): read~/.claude/settings.json→ unmarshal tomap[string]any→ add entry undermcpServers→ write back withjson.MarshalIndent. Create the file/directory if it doesn't exist.Uninstall(): same flow, delete the key frommcpServers.List(): readmcpServerskeys.
Create internal/target/codex.go.
Before implementing, research the current Codex CLI config file location and format. As of early 2025, Codex uses ~/.codex/config.json or similar. The MCP server config structure needs to be verified.
Action: search for the Codex CLI documentation or config format before implementing. The structure is likely similar to Claude Code but may differ in key names or nesting.
Implementation follows the same pattern as Claude Code.
Each of these would be a new file in internal/target/:
geminicli.go— Gemini CLIopencode.go— OpenCode
Research their config formats when adding support. Each should take roughly 50-100 lines of Go following the established pattern.
Create internal/credential/resolver.go.
type Source interface {
Name() string
Get(envName string) (string, bool)
Store(envName string, value string) error // may return ErrNotSupported
}
type Resolver struct {
sources []Source
}
// Resolve tries each source in order. Returns the value and which source it came from.
func (r *Resolver) Resolve(envName string) (value string, source string, found bool)Resolution order:
- Environment variable (highest priority — user explicitly set it)
- File store (
~/.config/mcp-wire/credentials) - Not found → trigger interactive prompt
Create internal/credential/env.go.
Simple wrapper around os.Getenv. Store() returns ErrNotSupported.
Create internal/credential/file.go.
Reads/writes ~/.config/mcp-wire/credentials in a simple KEY=VALUE format (one per line). This file should be created with 0600 permissions.
Format:
SENTRY_AUTH_TOKEN=snt_abc123...
JIRA_API_TOKEN=jira_xyz789...
On Get(): read the file, parse lines, return matching value.
On Store(): read the file, update or append the key, write back.
This lives in the install command logic (not in the credential package). When a required env var is not found by the resolver:
- Print the env var name, description, and why it is needed.
- If
setup_urlis provided, print it and offer to open in browser (useopen/xdg-open/startdepending on OS). - If
setup_hintis provided, print it. - Show progress when multiple credentials are required (example:
1/2,2/2). - Prompt the user to paste the value (mask input for security).
- Ask where to store: file store or skip (manage manually).
- If file store chosen, call
source.Store().
Example output:
🔧 Configuring: Sentry error tracking MCP
SENTRY_AUTH_TOKEN is required.
→ Create one here: https://sentry.io/settings/account/api/auth-tokens/
Tip: Create a token with project:read and event:read scopes
Open URL in browser? [Y/n]: y
Paste your token: ****************************
Save to mcp-wire credential store? [Y/n]: y
✓ Saved
Installing to: Claude Code, Codex
✓ Claude Code — configured
✓ Codex — configured
Use github.com/spf13/cobra for command structure.
When the user runs mcp-wire with no subcommand, open an interactive menu:
- Install a service
- Uninstall a service
- Exit
- Service selection
- Show a searchable/filterable list (name + description).
- Support quick search by substring.
- Target selection
- Show detected installed targets first.
- Allow multi-select.
- Credential step (for each required env var)
- Show description.
- Show setup URL and ask whether to open it.
- Show setup hint/scopes.
- Prompt with masked input.
- Ask whether to save in credential store.
- Confirmation step
- Show selected service, targets, and credential source summary.
- Apply + results
- Print per-target success/failure.
- Print equivalent explicit command.
- Service selection (searchable)
- Target selection (multi-select)
- Confirmation
- Optional credential cleanup prompt
mcp-wire installwith no service argument should enter the service picker.mcp-wire uninstallwith no service argument should enter the service picker.- Explicit args/flags continue to work unchanged.
Removed — the list services command was a low-value informational view. Service discovery now happens through the install wizard's service picker.
Removed — the list targets command was a low-value informational view. Target selection now happens through the install/uninstall wizard.
Flags:
--target <slug>— install to a specific target only (can be repeated). Default: all installed targets.--no-prompt— fail if credentials are not already resolved (for CI/scripting).
Flow:
- Load the service definition by name.
- If
<service>is omitted, enter interactive service selection. - For each required env var, run the credential resolver.
- If not found and
--no-promptis not set, run the interactive prompt (Phase 3.4). - If not found and
--no-promptis set, exit with error. - For each target (filtered by
--targetor all installed), calltarget.Install(). - Print results.
Flags:
--target <slug>— same as install.
Flow:
- For each target, call
target.Uninstall(serviceName). - If
<service>is omitted, enter interactive service selection. - Optionally ask if the user wants to remove stored credentials for this service.
Removed — the status command only showed curated YAML services, not all servers installed in a target, making it misleading.
Create YAML files in the services/ directory for at least these services:
name: sentry
description: "Sentry error tracking MCP"
transport: sse
url: "https://mcp.sentry.dev/sse"
env:
- name: SENTRY_AUTH_TOKEN
description: "Sentry authentication token"
required: true
setup_url: "https://sentry.io/settings/account/api/auth-tokens/"
setup_hint: "Create a token with project:read and event:read scopes"name: jira
description: "Jira project management MCP (Atlassian)"
transport: sse
url: "https://mcp.atlassian.com/v1/sse"
env:
- name: JIRA_API_TOKEN
description: "Atlassian API token"
required: true
setup_url: "https://id.atlassian.com/manage-profile/security/api-tokens"
setup_hint: "Create an API token from your Atlassian account settings"name: context7
description: "Context7 documentation lookup MCP"
transport: sse
url: "https://mcp.context7.com/mcp"
env: []Before writing more service definitions, verify the current MCP configuration format for each service. The URLs and env var names above are based on known configurations as of early 2025 and should be verified against current documentation.
go mod init github.com/<your-username>/mcp-wire
go get github.com/spf13/cobra
go get gopkg.in/yaml.v3
No other external dependencies should be needed for v0.1.
- Service loading: test YAML parsing with valid and invalid files, test validation logic, test precedence when multiple paths are provided.
- Target implementations: test Install/Uninstall/List against temporary config files. Create a temp dir, write a sample config, run the operation, assert the result.
- Credential resolver: test the chain order (env takes precedence over file), test the file store read/write.
- Integration test: write a test that loads a service YAML, creates a temp config file, runs Install, reads back the config, and verifies the MCP entry is correct.
- Guided interactive flow tests: test menu navigation, service search/filter behavior, target multi-select, and credential prompt flow using mocked input/output streams.
- Sandboxed CLI integration tests: run end-to-end install/status/uninstall flows in temporary HOME/PATH to avoid touching user config.
- Manual QA checklist: test on a machine with existing MCP configs and a clean machine; verify no unrelated config keys are removed.
Standard Go cross-compilation:
# Local
go build -o mcp-wire .
# Cross-compile
GOOS=darwin GOARCH=arm64 go build -o mcp-wire-darwin-arm64 .
GOOS=linux GOARCH=amd64 go build -o mcp-wire-linux-amd64 .
GOOS=windows GOARCH=amd64 go build -o mcp-wire-windows-amd64.exe .Use GoReleaser or GitHub Actions for automated releases. Provide binaries for macOS (arm64 + amd64), Linux (amd64), and Windows (amd64). Consider a Homebrew tap for macOS users.
Implement in this exact order to have something working as early as possible:
- ✅ Go module + main.go + Cobra skeleton — just
mcp-wire --helpworks. - ✅ Service struct + YAML loader + validation — can parse service files.
- ✅
list servicescommand — first working command, proves the YAML loading. (later removed — low-value informational view) - ✅ Target interface + Claude Code implementation — can read/write Claude Code config.
- ✅
list targetscommand — detects installed tools. (later removed — low-value informational view) - ✅ Credential resolver (env + file sources) — can resolve and store tokens.
- ✅
installcommand with interactive prompt — the core feature, end-to-end flow. - ✅
uninstallcommand — straightforward once install works. - ✅
statuscommand — reads from all targets, displays matrix. (later removed — only showed curated services, misleading) - ✅ Codex target implementation — second target, validates the abstraction.
- ✅ Initial service YAML files — ship with 3-5 verified services.
- ✅ README, contributing guide — explain how to add services and targets.
- ✅ Guided main menu —
mcp-wirewith no args opens interactive navigation. - ✅ Install wizard UX — searchable service picker, target selection, credential guidance.
- ✅ Uninstall wizard parity — same guided UX model as install.
- ✅ Interactive UX tests + sandbox integration tests — prevent regressions in guided flows.
- ✅ Equivalent-command summary in guided mode — print scriptable command at the end of each workflow.
When reading a target's config file, always use map[string]any (not a strict struct). This ensures that any keys the user added manually are preserved when mcp-wire writes back to the file. This is the single most important implementation detail — getting it wrong means the tool destroys user config.
The primary way community members contribute is by adding YAML files to services/. This must require zero Go knowledge. The YAML schema should be well-documented in CONTRIBUTING.md with examples for both SSE and stdio transports.
The user is always asked before storing credentials to the file store. The --no-prompt flag makes credentials required from environment or file store (no interactive prompting), which is useful for CI/automation.
mcp-wire is a pure CLI tool. It reads files, writes files, and exits. No running processes, no state between invocations beyond the credential file and the target config files.
For v0.1, service YAML files are shipped alongside the binary (in the repo). The tool looks for them relative to the binary location and also in ~/.config/mcp-wire/services/. This avoids any network dependency. A future version could fetch definitions from a remote registry.
This phase adds optional integration with the Official MCP Registry (https://registry.modelcontextprotocol.io) without changing the current safe defaults.
- Curated stays default: mcp-wire bundled/user YAML services remain the default discovery/install source.
- Registry is opt-in: registry support is disabled by default behind a one-time local setting until the feature is stable.
- No hidden references when disabled: if registry is disabled, do not show registry flags/options/help/menu entries anywhere.
- Latest-only selection: when a user selects a registry MCP, resolve details from the
latestversion only. - Runtime checks are warnings by default: missing package managers should warn, not block installation.
Add mcp-wire local settings file (for example ~/.config/mcp-wire/config.json) with:
{
"features": {
"registry": false
}
}Suggested commands:
mcp-wire feature enable registrymcp-wire feature disable registrymcp-wire feature list(optional but useful)
Acceptance criteria:
- Fresh install behavior is unchanged.
- With
registry=false, all commands and guided flows behave exactly as today.
Create internal/registry/ for typed API client logic.
Use these endpoints:
- List latest servers:
GET /v0.1/servers?version=latest&limit=...&cursor=... - Search latest servers:
GET /v0.1/servers?version=latest&search=... - Get selected server details (latest only):
GET /v0.1/servers/{serverName}/versions/latest
Important details:
- URL-encode
serverNamepath values (/must become%2F). - Parse
application/problem+jsonerrors and return friendly messages. - Do not expose historical version selection in this phase.
Add a local cache/index (for example under ~/.cache/mcp-wire/) to avoid network-per-keystroke UI.
Requirements:
- Cold sync with pagination from
version=latestlist endpoint. - Incremental sync using
updated_since. - Local in-memory filtering/search for interactive pickers.
- Graceful fallback to stale cache when network fails.
- Background sync at CLI startup when registry is enabled, with non-blocking UX and visible sync status in registry flows.
- Provide a cache reset command for local recovery (
mcp-wire cache clear).
Acceptance criteria:
- Guided and non-guided search feel immediate.
- Registry API latency does not block every filter action.
Introduce an internal catalog type that supports both curated and registry entries.
Each entry should carry source metadata:
source: curated(maintained/tested by mcp-wire)source: registry(from Official MCP Registry, not vetted by mcp-wire)
Keep existing service.Service and YAML format intact for curated entries.
When registry is enabled, add source filtering:
--source curated|registry|all(default:curated)
Apply this to install flows that pick services interactively.
When registry is disabled:
- Do not expose
--source. - Do not mention registry in help/usage text.
Note: mcp-wire list services was later removed as a low-value informational view.
Add a source step before service selection:
- Curated services (recommended)
- Registry services (community)
- Both
If user selects "Both", show one merged searchable list with clear markers:
*= curated by mcp-wire- unmarked (or explicit label) = registry
Always print the legend near the list when markers are shown.
Before confirming installation of a registry entry, show a short summary:
- Source (
registry) - Install type (
remoteorpackage) - Transport (
streamable-http,sse,stdio) - Required secrets/credentials
- Repository URL (if available)
Interactive mode should require explicit confirmation for registry entries.
First support registry entries that map cleanly to remote installs:
remoteswithstreamable-httporsse- URL/header variable prompting and substitution
Then support package-backed installs:
npm,pypi,oci,nuget,mcpb- map registry metadata into target-compatible stdio/local config
For selected registry MCPs, always fetch/install using latest details only (versions/latest).
For package-backed installs, preflight-check required runtime commands (for example npx, uvx, docker, bun, dnx, pipx, python3).
Behavior:
- Default: warn and continue.
- Optional strict mode (for CI): fail fast when runtime is missing (for example
--strict-runtime-check).
Ensure uninstall handles registry-installed services as first-class entries, not only curated YAML service names.
Requirements:
- Uninstall should work by installed service key regardless of source.
Note: the status command was removed because it only showed curated YAML services, not all servers installed in a target.
Suggested order for shipping gradually:
- ✅ Feature gate + settings file + no-registry-references enforcement.
- ✅ Registry read client + latest-only detail lookup + basic tests.
- ✅ Cache/index + local search.
- ✅
list servicessource filter (curateddefault). (list services later removed) - ✅ Guided UI source step and merged list markers.
- ✅ Registry remote-install support.
- Runtime preflight warnings.
- ✅ Registry package-install support.
- Uninstall parity for mixed sources.
- Hardening, integration tests, docs.
Replace the survey/v2-based interactive UI with a full-screen Bubble Tea TUI featuring breadcrumb navigation, fixed-height content area, live-filtered search, multi-select checkboxes, and progress indicators. The plain-text fallback (guided.go) and explicit CLI mode (mcp-wire install sentry --target claude) remain unchanged.
Wizard state machine with composable screens. A single root Bubble Tea model owns the layout shell (title bar, breadcrumb, status bar) and delegates 13-15 content lines to the active screen sub-model. Each screen implements a Screen interface. Navigation via message passing (NavigateMsg, BackMsg). A screen stack enables Esc-back.
New package structure:
internal/tui/
app.go -- Root model (state machine, layout shell)
theme.go -- Lip Gloss styles, color palette, layout constants
screen.go -- Screen interface, message types, ScreenID enum
breadcrumb.go -- Breadcrumb bar renderer
statusbar.go -- Bottom status bar renderer
menu.go -- Screen 1: Main Menu
source.go -- Screen 2: Source Selection
service.go -- Screen 3: Service Selection (with search)
trust.go -- Screen 4: Registry Trust Warning
target.go -- Screen 5: Target Multi-select
scope.go -- Screen 5b: Scope Selection
review.go -- Screen 6: Review
apply.go -- Screen 7: Apply (progress/result)
credential.go -- Credential prompting within TUI
Each file gets a corresponding _test.go.
Key types:
type Screen interface {
Init() tea.Cmd
Update(msg tea.Msg) (Screen, tea.Cmd)
View() string
StatusHints() []KeyHint
}
type WizardState struct {
Action string // "install" or "uninstall"
Source string // "curated", "registry", "all"
Service service.Service
Entry *catalog.Entry
Targets []target.Target
Scope target.ConfigScope
Results []TargetResult
}New dependencies:
github.com/charmbracelet/bubbletea v1.x
github.com/charmbracelet/lipgloss v1.x
github.com/charmbracelet/bubbles v0.x
After completion, AlecAivazis/survey/v2 and its transitive deps are removed.
Add Bubble Tea, Lip Gloss, and Bubbles to go.mod. Create the package skeleton with the root model, theme, and a placeholder main-menu screen. Wire into root.go alongside the existing dispatch logic, guarded by a feature flag so the old survey path remains reachable during development.
Create:
internal/tui/app.go— RootWizardModel(tea.Model). Owns terminal dimensions, renders layout frame (title row, breadcrumb, content, status bar). Delegates to activeScreen.internal/tui/screen.go—Screeninterface,ScreenIDenum, navigation message types.internal/tui/theme.go—Themestruct with Lip Gloss styles matching mockup palette (cyan active, green completed, dim future, yellow warning, red error). Layout constant:ContentHeight = 13.internal/tui/breadcrumb.go— Renders fromWizardState. Completed = green ✓ + value. Active = bold cyan. Future = dim.internal/tui/statusbar.go— Renders[]KeyHinton bottom row.internal/tui/menu.go— Placeholder main menu.
Modify:
go.mod/go.sum— Add bubbletea, lipgloss, bubbles.internal/cli/root.go— Add TUI branch inrunGuidedMainMenu(), guarded by feature flag.
Acceptance: make test passes. bin/mcp-wire shows the Bubble Tea menu.
Full menu with ↑↓ navigation, Enter select, q quit. Three items: Install service, Uninstall service, Exit.
Acceptance: Menu navigable. Exit quits cleanly.
Conditional on registry feature flag. Three options with inline descriptions: Curated (recommended), Registry, Both.
Modify internal/tui/app.go to skip source screen when registry feature is off, defaulting to "curated".
Acceptance: With registry enabled → source screen shown. Without → skipped directly to service selection.
Live-filtered search over curated and/or registry catalog.
- Uses Bubbles
textinput.Modelfor search bar. allEntries,filtered,cursor,offsetfor scroll state.- Right-aligned count ("786 services" / "2 matches").
- Two-line entries: name + description.
- Scroll indicator ("▼ N more").
Init()loads catalog via existingloadCatalog()fromcatalog_helpers.go.- Registry sync: poll
registrySyncStatusLine()viatea.Tickevery 500ms (replaces the blockingwaitForRegistrySyncInSurvey).
Acceptance: Search filters live. Scroll works. Enter selects.
Shown after selecting a non-curated service. Displays service metadata (source, transport, URL, repo) and horizontal Yes/No choice.
- On "Yes": fetch latest via
refreshRegistryEntry(), convert viacatalogEntryToService(), proceed to targets. - On "No": back to service list.
Polished view: ⚠ warning header, service name (cyan/bold) + description, remote URL in cyan, caution text, descriptive choice labels ("No, go back" / "Yes, proceed").
Acceptance: Registry service → trust shown. Curated service → skipped.
Target screen:
[x]/[ ]checkboxes. Space toggles,aall installed,nnone.- Not-installed targets dimmed and non-selectable.
- Enter confirms (≥1 required).
Scope screen:
- User/Project choice. Only shown if any selected target supports project scope.
Acceptance: Full flow from source → service → target → scope works.
Summary of all selections: Source, Service, Targets, Scope, Credentials mode. Shows equivalent CLI command (reuse buildEquivalentInstallCommand logic from guided.go). Horizontal Apply/Cancel choice.
Acceptance: Review shows correct summary. Apply proceeds to execution.
Credential prompting in TUI:
CredentialScreensteps through unresolvedEnvVarentries sequentially.- Uses
textinput.ModelwithEchoMode = EchoNonefor secrets. - Shows setup URL/hint before each prompt.
- On completion, stores resolved values and proceeds to apply.
Apply screen:
- Per-target status rows:
◌pending,◌configuring,✓done,✗failed. - Runs each
target.Install()viatea.Cmd(goroutine), receives progress messages. - Success: green display + equivalent command + "Install another / Back to menu / Exit".
- Partial failure: warning + error details + "Retry failed / Back to menu / Exit".
- OAuth: runs
Authenticate()in atea.Cmd, shows "authenticating..." status.
Modify internal/cli/install.go — extract installSingleTarget() from executeInstall() loop so both CLI and TUI paths can call it.
Acceptance: Full end-to-end install with credential prompting and per-target progress.
Reuses most install screens. WizardState.Action = "uninstall" controls rendering and business logic differences in Review and Apply screens. Add post-uninstall credential removal prompt (mirrors maybeRemoveStoredCredentials from uninstall.go).
Acceptance: Full uninstall flow works end-to-end in the TUI.
Visual polish across all TUI screens to match design mockups: source screen question header and inline descriptions, service search prompt and scroll indicator formatting, scope heading with target names and footer, review em-dash labels with Apply-first choice order and inline command display, apply service-name headers with per-state icons (✓/△/✗) and retry choice on failure.
Remove survey dependency and transition code.
Delete:
internal/cli/survey_ui.gointernal/cli/survey_escape_input.gointernal/cli/survey_escape_input_unix.gointernal/cli/survey_escape_input_other.gointernal/cli/survey_escape_input_test.go
Modify:
internal/cli/root.go— Remove survey branch and feature-flag guard. TTY → TUI, non-TTY → plain text.go.mod— RemoveAlecAivazis/survey/v2and transitive deps.
Acceptance: make test passes. survey gone from go.mod. TUI, plain-text, and CLI flag modes all work.
| Risk | Mitigation |
|---|---|
| Credential prompting in Bubble Tea | Use Bubbles textinput with EchoNone — hidden input native to the event loop. term.ReadPassword stays in install_credentials.go for plain-text fallback only. |
| Registry sync blocking | Replace waitForRegistrySyncInSurvey() with tea.Tick polling of registrySyncStatusLine(). Sync already runs in a background goroutine. |
| OAuth subprocess | Run Authenticate() inside a tea.Cmd (goroutine). Browser opens outside terminal control — no conflict. |
| Terminal resize | Handle tea.WindowSizeMsg in root model. Degrade gracefully if height is too small. |
| Coexistence during development | Feature flag toggles between survey and TUI. Default stays survey until step 8.9. |
These are features to consider after the core works. Not to be implemented now.
- Manifest file (
mcp-wire.yaml) — declare desired services in a file, runmcp-wire applyto sync all targets at once. Enables version-controlling your MCP setup. - Target auto-detection improvements — check config file existence in addition to binary presence, handle edge cases like Homebrew vs manual installs.
- OS keychain integration — use
zalando/go-keyringto store credentials in macOS Keychain / Linux Secret Service / Windows Credential Manager instead of a plaintext file. mcp-wire credentials list— show stored credentials (masked).mcp-wire credentials rotate <service>— re-open setup URL, re-prompt, update all targets.- Additional registry providers — support third-party/private registries that implement the MCP Registry API, with per-registry trust controls.
- Gemini CLI target — add when config format is stable and documented.
- Target capability parity — expand and test Claude/Codex/OpenCode parity for advanced MCP config shapes (for example richer header mapping, variable substitution, and transport-specific options).
mcp-wire diff— show what's configured in targets vs what a manifest says, dry-run mode.- Profiles — "work" vs "personal" service sets, each with their own credentials and target selection.
- Service health check —
mcp-wire check <service>verifies the MCP endpoint responds. - Shell completions — Cobra has built-in support for bash/zsh/fish completions.