Why a Terraform-Inspired Workflow Fits MCP Configuration
Modern AI development has a practical local configuration problem.

Developers are no longer using one editor, one terminal, and one assistant. A practical AI-native workflow might include Claude Desktop for broad exploration, Claude Code for repository work, Cursor or VS Code for editor-native coding, Zed for fast local editing, Gemini CLI for terminal workflows, Codex CLI for agentic coding, and tools like Windsurf, OpenCode, Kiro, Roo Code, or Antigravity depending on the task.
That variety is useful, but it creates setup work.
The Model Context Protocol, or MCP, gives AI clients a common way to talk to external tools. But the human side of that setup is still fragmented. Each AI client stores MCP configuration differently. Some use JSON. Codex uses TOML. Some expect an mcpServers root key. Others use servers, context_servers, mcp, serverUrl, httpUrl, or client-specific command shapes. Some clients can run local stdio servers. Some can use remote HTTP servers. Some need bridges. Some should be skipped because the transport is not supported.
This article explains how Universal MCP Sync, or usync, approaches that problem. The focus is not the novelty of a CLI. The useful part is the workflow: collect intent, show a plan, apply native config changes, and verify the result.
Who this is for
This article is for developers, DevOps engineers, and platform engineers who use multiple AI clients locally and want a safer way to manage MCP configuration. You do not need to know the usync codebase, but the examples assume basic comfort with CLI tools, JSON/TOML config files, and Go project layout.
By the end, you should understand why a plan/apply workflow is useful for local AI configuration and how usync maps that idea onto native client files.
The real problem is not JSON
At first glance, usync can sound like a small utility:
A CLI/TUI that manages MCP config files.
That description is technically true, but it misses the operational problem.
The real problem is local developer platform consistency. When a developer wants Exa, Context7, GitHub, or another MCP server available across many AI tools, they have to answer the same set of questions repeatedly:
Where does this client store its config?
What root key does this client expect?
Does this client support
stdio, HTTP, streamable HTTP, or SSE?Should credentials go in a URL query string, HTTP header, or environment variable?
How do I preview the change before writing it?
How do I avoid leaking a token into terminal output?
How do I roll back if one file updates and the next one fails?
Those questions are familiar in platform work. They have the same shape as infrastructure automation problems: detect targets, generate desired state, show a plan, apply safely, verify the result, and keep recovery paths available.
That is why usync borrows from Terraform's mental model.
The Terraform idea: plan before apply
Terraform works because it separates intent from mutation. You describe what you want, inspect the plan, then apply changes when the plan matches your expectation.
usync applies that pattern to local AI toolchain configuration.
The workflow is:
usync
or, for the existing Exa non-interactive path:
usync sync --keys-file ./exa_keys.txt --dry-run
usync sync --keys-file ./exa_keys.txt --apply
The important detail is not the command. The important detail is the operating model:
Collect provider credentials.
Select target clients.
Generate a provider-neutral config.
Adapt that config to each client's native format.
Preview the exact files and actions.
Apply only after review.
Verify and remind the user to restart affected clients.
In the codebase, that shape lives mostly in pkg/app/app.go. The Manager creates an ExecutionPlan, where each Operation describes the app, file label, path, provider ID, credential label, backup path, file kind, and generated MCP config. The apply phase then turns those planned operations into actual JSON, TOML, or CLI changes.
That separation matters. Without it, the app becomes a pile of special cases. With it, the tool behaves like a small local platform engine.
Why Go was a good fit
Go fits this project because the problem is operational:
read and write files predictably
preserve permissions
parse and mutate structured config
build a cross-package CLI
ship a single binary
test edge cases across many file shapes
The repository structure reflects that:
cmd/usync/main.go # entrypoint
pkg/app/app.go # planning and apply orchestration
pkg/provider/ # Exa, Context7, GitHub, provider interface
pkg/client/ # client capability matrix and adaptation
pkg/config/ # path detection, JSON/TOML mutation, file writes
pkg/redact/ # secret redaction
pkg/verify/ # verification checks
pkg/tui/ # Bubble Tea user experience
The CLI can launch the Bubble Tea TUI, print a dry-run plan, or apply changes directly. That gives two useful workflows: a guided self-service path for humans, and a scriptable path for repeatable setup.
Configure once, adapt many times
The central product idea:
Configure an MCP server once, then let the tool translate it safely across native client configs.
Exa is a good example. The provider produces a URL-based HTTP configuration. Some clients can write that URL directly. Claude Desktop, however, speaks stdio natively, so remote HTTP gets bridged through mcp-remote. Gemini CLI uses httpUrl. Windsurf and Antigravity use serverUrl. VS Code uses a servers root and a type: http shape. Codex needs TOML.
The user should not need to memorize any of that. The tool should encode it.
That is the useful platform pattern here: reduce repeated manual work without hiding what will change.
Safety is the differentiator
The part that makes usync more useful than a direct file-editing helper is the safety model.
usync is local-first, but it treats local files as important state. It does not write by default. It redacts credentials in plans and terminal output. It creates same-directory backups for existing files. It writes through a temp file and rename flow. It uses private permissions. If a later write fails after earlier writes succeeded, the app attempts rollback. After apply, verification checks confirm that the provider entry exists in the updated config.
That is the difference between a quick automation script and a tool that can be reused with some confidence.
Automation makes work faster. Platform tooling makes the safe path the default path.
What learners can inspect
For learners, the useful part of usync is that the design is small enough to inspect. You can follow one provider from credential input to generated config, preview, write, backup, rollback, and verification.
Start with these files:
cmd/usync/main.gofor CLI and TUI entrypointspkg/app/app.gofor plan/apply orchestrationpkg/provider/types.gofor the provider contractpkg/client/capabilities.gofor client compatibility rulespkg/config/files.gofor backups, permissions, atomic writes, and rollbackpkg/app/qa_scenarios_test.gofor end-to-end behavior examples
For experienced readers, these same files show practical engineering habits: bounded abstractions, testable safety behavior, and attention to developer workflow. The value is in the implementation details.
What this series covers
This series breaks down the project as an open-source learning case study:
Why fragmented MCP config needs a plan/apply workflow.
How the provider architecture keeps the TUI and apply flow generic.
Why dry-run, redaction, backup, rollback, and verification matter.
How config drift appears across 12 AI clients.
How Context7 was added without hard-coding the TUI.
What building
usyncteaches about platform engineering.
The goal is to show how a local developer utility can use platform patterns without becoming heavy or centralized.
Next
Next: Designing a Provider Architecture for Local AI Tooling. It explains how Exa, Context7, GitHub, and future MCP servers can move through the same plan, adapt, write, and verify flow.





