How to Integrate Go CLIs in 2026

Most Go CLIs that need to integrate with other tools face the same temptation: invent a framework, then spend the next year maintaining it.

Usually the right starting point is simpler. Use Unix process composition first. Add richer protocol layers only when they solve a real problem.

This matters more in 2026 because the integration surface has grown. A modern CLI may need to work with:

Those are real needs, but they do not justify collapsing everything into one runtime.

The baseline: executables, pipes, JSON

The most robust integration model is still the old one:

For a Go CLI named kg, a plugin named kg-nlm is a better integration point than a shared object ABI, embedded scripting runtime, or custom daemon.

That gives you:

It also keeps upgrade blast radius small. If the downstream tool changes, the adapter changes. The host CLI does not need to absorb that churn.

A concrete rule

If a user can reasonably type:

tool foo ...

then your first design should probably be:

tool-foo ...

with the host CLI discovering and dispatching to it.

That design is boring, which is a point in its favor.

Where richer systems fit

There are three common reasons teams move beyond plain executables.

1. Typed discovery

You want to know what a plugin is, what it does, and how to render it in help or UIs.

That does not require abandoning the Unix process model. A small metadata probe is enough:

tool-foo --tool-plugin-metadata

returning JSON like:

{
  "name": "foo",
  "summary": "Short description"
}

This is cheap and good enough for plugin list and plugin info.

2. Structured tool invocation

This is where MCP helps. MCP is useful when the integration is no longer just “run this subcommand” but “expose a tool surface to an agent or host runtime.”

Use MCP when:

Do not use MCP by default when a plain CLI adapter is enough. A one-shot shell command is easier to install, reason about, audit, and support.

The useful rule is:

These can coexist as separate binaries.

Inference changes the architecture

Inference is the main reason older plugin advice breaks down.

A CLI that talks to models is no longer just performing deterministic local computation. It crosses privacy, cost, latency, and provenance boundaries.

That means you should separate:

Do not send an entire working set to a model because it is convenient. Reduce first.

The host CLI or adapter should:

  1. resolve the local target
  2. gather only relevant context
  3. trim and normalize that context
  4. invoke inference

This is good systems design, not just cost control. It reduces privacy exposure and makes results easier to explain.

Privacy defaults matter

Most CLI integrations have terrible privacy defaults because they start from os.Environ(), broad filesystem access, and convenience logging.

That is lazy design.

If your CLI handles notes, source code, prompts, transcripts, credentials, or production state, default to least disclosure.

Reasonable defaults:

A plugin on PATH is local code execution. Treat it that way.

Security lessons from a real plugin system

The it2 CLI has already explored a more ambitious plugin model: executable discovery, typed plugin classes, manifests, trust levels, caching, and sandbox design.

That work is useful because it exposes the real failure modes.

The most important ones are ordinary:

Environment leaks

Passing the full parent environment to plugins leaks secrets. This is one of the easiest mistakes to make and one of the highest value fixes.

Mixed stdout and stderr

If you expect machine-readable output, do not merge stderr into stdout. Warnings should not corrupt the data channel.

Missing process-tree cleanup

Timeouts that only kill the top-level plugin process are incomplete. Shell wrappers often spawn children. Clean up the process group.

Discovery confused with trust

Finding an executable on PATH tells you nothing about whether you should trust it.

Unbounded inputs

Do not shovel whole buffers, large repos, or arbitrary terminal transcripts into every plugin invocation. Bound the input contract.

These are not exotic plugin problems. They are systems hygiene problems.

Provenance without turning your CLI into a package manager

Supply chain security is real, but many CLI designs overreact by inventing an entire plugin marketplace before they have a stable interface.

A better progression is:

Layer 0: plain executable discovery

Support tool-foo on PATH. This is the compatibility layer and the local development path.

Layer 1: optional metadata

Add a small metadata probe for help text and UI rendering.

Layer 2: optional manifest

If a plugin wants higher trust, let it ship a manifest that declares:

This is not a sandbox. It is a statement that can be checked and shown to users.

Layer 3: optional verification

For distributed plugins, add:

Layer 4: local policy

Let users or orgs enforce policy:

This separates compatibility from trust. That separation is important.

For Go-native plugin ecosystems, govulncheck -mode binary is a useful inline
verification step for released executables. It is a good fit for:

It is not a substitute for signatures, checksums, or provenance. It answers a
different question: whether the built Go executable appears to contain known
reachable vulnerabilities. It does not prove who built it, whether it is the
expected file, or whether a shell wrapper around it is safe.

macOS sandboxing and XPC

macOS adds another temptation: once a CLI starts dealing with inference,
credentials, or enterprise policy, it becomes attractive to move everything
behind a service boundary.

That instinct is sometimes right, but not as a default.

The App Sandbox is usually the wrong primitive for a CLI

The macOS App Sandbox works best for GUI apps with well-defined entitlement
needs and brokered user file selection.

It is awkward for developer CLIs because:

For that reason, most Go CLIs should not try to make the main executable an
App Sandbox target.

XPC is better when you need a broker

If you need stronger boundaries on macOS, XPC is more useful than trying to
contort the whole CLI into a sandboxed app.

An XPC service can own:

The CLI remains the interactive surface. The service becomes the trusted
broker.

A good split

The clean macOS shape is often:

That preserves Unix ergonomics without giving up a higher-assurance path.

When XPC is justified

Use an XPC service when:

When it is not

Do not require XPC when:

This is the same general lesson as with MCP: do not force the richer system
into the default path unless it solves a concrete problem the simpler system
cannot.

Security consequence

A useful rule is:

If a plugin needs secrets, attested network access, or strong local policy,
that functionality probably belongs in an XPC broker or helper service, not in
an arbitrary executable discovered from PATH.

The design mistake to avoid

The common failure mode is trying to solve all trust and orchestration concerns inside the host CLI itself.

That usually leads to:

Sometimes those are justified. Often they are not.

Before you add them, ask:

If the answer is not clear, defer it.

A practical recommendation for 2026

For most Go CLIs, the right default architecture is:

  1. External command plugins by executable name
  2. Text and JSON over stdio
  3. Explicit metadata probe
  4. Strict environment allowlist
  5. Deadlines and process-group cleanup
  6. Optional manifests for provenance and policy
  7. Separate MCP servers for agent-facing integrations

That stack handles most real-world integration needs without baking in irreversible complexity.

The point is not to avoid sophistication forever. The point is to earn it.

Unix got the first part right decades ago: processes, pipes, and composability are a very strong substrate.

In 2026, the best CLI designs still start there.

goclipluginsmcpsecuritysupply-chain