One core, four faces: UI, CLI, HTTP, and MCP
This article explains why a desktop app should expose UI, CLI, HTTP, and MCP on top of one shared core, and how that design lowers maintenance cost while making AI-agent workflows more reliable.
Here is the short answer: if your product must serve humans directly, local scripts, existing systems, and AI agents, the durable design is usually not four separate products but one shared core with four entry surfaces. In OmniGoAI’s OmniPost, those surfaces are the desktop UI, CLI, HTTP API, and MCP. They look like different forms, but they should all call the same publishing capability, account state, and result history underneath.
Many teams hear “multiple interfaces” and immediately worry that the product will fragment. The real problem is not the number of interfaces. The real problem is whether those interfaces still share one core. If the UI has one logic path, the CLI duplicates another, the HTTP layer reimplements a third, and MCP wraps a fourth variation, the system quickly drifts: the desktop can publish something the CLI cannot, HTTP can see state that MCP cannot, and logs no longer match. The maintainable design is simple in principle: the outer layer decides how to call, and the inner layer decides how work actually happens.
If you are designing an agent-friendly desktop tool, this article answers four practical questions: why “one core, four faces” is more stable than four parallel implementations, what each interface is actually for, what the shared core must really share, and which mistakes teams make most often.
Why desktop tools increasingly need more than one interface
Modern desktop tools are no longer only software that waits for a human to click a button.
They often serve four kinds of callers at the same time:
- human users, who need a visible interface for inspection and manual intervention;
- local scripts and schedulers, which need a CLI for batch work and repeatable jobs;
- existing business systems, which need HTTP for CMS, queue, and backend integration;
- AI agents, which need MCP or another structured tool interface for stepwise decision-making.
If you only build one of those surfaces, the other three usually show up later anyway. That is why OmniGoAI’s OmniPost is not just a desktop app with buttons. It keeps the human-facing desktop experience, but it also exposes the same distribution capability over CLI, HTTP, and MCP so the same content workflow can scale from manual use to automation.
That is the same boundary we described in How to connect any AI agent to OmniPost: upstream systems can change, but the publishing layer should stay stable.
What “one core, four faces” actually means
This is not just a slogan. It maps to a concrete engineering structure:
- one core: the part that owns business rules, state, validation, execution, and records;
- four faces: UI, CLI, HTTP, and MCP as four ways of entering that same capability.
A simple test tells you whether you really have this structure: should the same action behave the same way no matter which interface called it?
Take a direct publish to Juejin in OmniPost. Whether the action starts from:
- a button in the desktop UI,
- a CLI
publish --mode publishcommand, - an HTTP
POST /api/publishcall, - or an MCP
publish_posttool call,
it should still flow through the same sequence:
- read account state,
- inspect platform capability,
- validate required fields such as category, tags, and summary,
- execute the publish action,
- record the result and return stage plus the public URL.
If those five steps are reimplemented separately per surface, that is not one core with four faces. It is four similar-looking systems.
What the shared core really needs to share
Many teams say they have a shared core when they really only share a data shape. The parts that matter most are deeper than that.
1. Business rules must be shared
The most important thing to centralize is not visual styling. It is business logic.
For a multi-platform publishing app, the core should own rules such as:
- which platforms support drafts and which support direct publish,
- which required fields should block direct publishing,
- why Juejin requires category, tags, and summary,
- how to interpret NEED_LOGIN, VALIDATION_FAILED, or MANUAL_PUBLISH,
- which outcomes count as
publishedand which are onlydraft.
Those rules should not live once in UI hints, again in CLI parsing, and again in HTTP handlers. If they do, they will drift.
2. The state model must be shared
The second requirement is shared state.
For a desktop tool, at least these facts should come from one place:
- whether the app is running,
- whether each platform account is logged in,
- the latest execution result,
- whether a post is draft, published, reviewing, or failed,
- logs, error codes, and public URLs.
If the UI keeps one local version of reality, the CLI checks another on demand, and HTTP reads a third source, humans and agents stop seeing the same world. In content operations, the most dangerous failure is often not the failure itself, but different interfaces telling different stories about the same event.
3. Execution paths must be shared
A shared core should not only centralize decisions. It should centralize the actual work too.
For example, “publish to Zhihu” should ultimately call one execution path no matter which outer interface triggered it. The outer layer should only adapt parameters in and results out.
That buys you several practical benefits:
- fix one bug and all four surfaces benefit,
- add one error code without rewriting four flows,
- keep result and logging formats consistent,
- prevent preview, draft, and publish behavior from drifting apart.
4. Result history must be shared
This layer is frequently overlooked. Teams may unify execution while leaving records fragmented.
A truly agent-friendly desktop tool should record results into one shared history no matter which surface initiated the action. At minimum, the record should include:
- time,
- platform,
- account,
- input summary,
- stage,
- error code,
- post URL or editor URL,
- a record ID that later runs can use for deduplication.
That is why OmniPost’s CLI, HTTP, and MCP should all end up in the same post record model. Otherwise, future runs cannot answer the simple question: “was this already published?”
What each interface is actually for
Sharing one core does not mean the four faces are interchangeable. Their roles should be distinct.
UI: a visible control surface for humans
The desktop UI matters not because it can also publish, but because it provides a visible, inspectable, manually overridable control surface.
It is especially good for:
- checking account and login state,
- filling category, tags, and summary by hand,
- previewing content and reading publish results,
- taking over when a captcha, risk check, or manual confirmation appears.
Many agent workflows still need a desktop surface eventually, because durable systems must allow human takeover at key moments.
CLI: a stable surface for files and scripts
The CLI is not mainly about replacing the UI. Its real strength is that it fits files, shells, and repeatable jobs.
It is best at:
- reading Markdown files directly,
- fitting into scheduled tasks and local scripts,
- handling readiness checks, publish actions, and follow-up checks,
- avoiding a long detour through shell escaping and JSON escaping.
If your content is written to disk first, then checked, built, and deployed before publishing, CLI is often the most natural face. If you want a deeper comparison of the machine-facing paths, see Choosing between OmniPost CLI, MCP, and HTTP.
HTTP: the integration surface for systems
HTTP is not “more advanced.” It is simply better suited to system integration.
It fits cases where:
- content originates from a CMS or backend service,
- several upstream systems share one distribution layer,
- centralized access control, auditing, or scheduling matters,
- upstream systems should not directly manipulate local files or desktop processes.
HTTP turns a desktop app from “something a person clicks” into “a service node that can be orchestrated.” That matters for content pipelines, admin systems, and operations tooling.
MCP: a structured tool surface for AI agents
If HTTP is designed for systems, MCP is designed for agents.
An agent rarely works as “run one command and stop.” It more often works like this:
- inspect status,
- check accounts,
- fill missing fields from the result,
- preview,
- decide between draft and direct publish,
- read the result and continue.
That stepwise rhythm is what structured tool interfaces are good at. MCP’s value is not a different capability set. It is a better reasoning surface for agents on top of the same core.
Why this is more stable than four separate implementations
The benefits are not just conceptual. They show up in long-term maintenance.
First, consistent behavior becomes much easier
When interfaces share one core, it becomes easier to keep these decisions aligned:
- which platforms support
publish:auto, - which fields are hard requirements,
- which failures mean “please log in again,”
- which outcomes should be stored as draft, published, or reviewing.
That consistency matters a lot for agents because agents depend on stable semantics in tool output.
Second, new capabilities only need one implementation
If you later add publish-status checks, account health, or scheduled publishing, the ideal flow is:
- add the capability to the core,
- expose it where needed through UI, CLI, HTTP, and MCP.
That keeps one source of truth for the hard logic while letting each interface decide how to present it.
Third, human fallback becomes much easier
Many real-world workflows will never be fully automatic.
Common examples in content operations include:
- expired logins,
- captchas or slider checks,
- platform risk controls,
- manual review of titles or categories.
If all four surfaces share state and history, an agent can hit an issue and a human can open the UI and see the same task, the same result, and the same error without reconciling several disconnected systems.
The most common mistakes in “one core, four faces” designs
The idea sounds straightforward. The implementation often is not.
Mistake 1: unifying transport but not business logic
A team may say that CLI and HTTP call the same service while the UI still uses a different path. That still creates drift between what humans see and what automation sees.
Mistake 2: inconsistent error semantics
If the UI shows free-form prose, the CLI prints a different free-form message, HTTP returns unrelated field names, and MCP invents another translation layer, agents cannot branch on failure reliably.
A better pattern is to define error semantics in the core first and let outer layers decide how to render them.
Mistake 3: fragmented result history
If the UI keeps its own history, the CLI only prints to stdout, HTTP logs elsewhere, and MCP only replies in-session, then even a simple rule like “skip if already published” becomes hard to trust.
Mistake 4: treating MCP as a separate business system
MCP should not be a second product designed only for agents. It should be a structured exposure of the same existing core. Otherwise you end up maintaining the human product and a shadow agent product in parallel.
Which products benefit most from this structure
This design is especially useful for three kinds of products.
1. Local-first desktop tools that also need automation
OmniPost is a good example: it keeps creator accounts local, but it still needs to integrate with automated publishing workflows. UI alone is not enough, and API alone is not enough.
2. Agent tools with explicit human fallback moments
If the workflow is guaranteed to include “let a human confirm this part,” the UI and the agent interfaces should share the same core from the start instead of being stitched together later.
3. Products whose capability boundary will expand over time
Today you may only need UI plus CLI. Tomorrow someone asks for HTTP, and then MCP. If you separate the core from the outer interfaces early, that expansion is much cheaper.
Frequently asked questions
Why not just build HTTP and make UI and MCP wrap it?
You can, as long as HTTP is still only a surface over a real shared core rather than the place where business logic starts to live. The important rule is that product logic should not grow inside the interface layer.
What is the real difference between MCP and HTTP?
HTTP is better for system-to-system integration. MCP is better for agents that need structured stepwise tool use. Underneath, they should still map to the same core capability.
Is the desktop UI still important in the age of agents?
Yes. As long as login, risk controls, human confirmation, and result review exist, a visible UI remains a necessary human control plane rather than a legacy burden.
Does “one core, four faces” make the product too heavy?
What makes products heavy is not the number of surfaces. It is reimplementing the same logic in each one. A shared core is usually how you reduce complexity across many surfaces.
If you are building a desktop tool that must serve humans, scripts, and agents at the same time, the most useful design question is usually not “which protocol should we launch first?” It is “which rules, states, and records must exist in exactly one place?” To see that idea in a real content-distribution product, start from the OmniPost download page: https://omnigoai.com/en/download/omnipost/