10 min read By agentix-zero

Agent architecture is not application architecture: the agentix pattern

Application architecture structures code. Agent architecture decides who may do what at run time. The agentix pattern - one MCP server per system, one agent per step - is our answer.

This post is also available in Deutsch.

Recently we saw a setup in which one MCP server talked to about ten different applications. It was a Swiss army knife: one process, every credential, every tool, written for exactly one agent and reusable by nobody. It worked, and it is not what MCP is meant for. This post explains why, separates two kinds of architecture that are easy to mix up, and proposes a pattern for the second one. Where something is opinion rather than fact, the text says so.

Two questions, not one

Application architecture decides how code is structured: layers, services, data ownership, ports and adapters, who calls whom. Those calls are fixed at design time, reviewed in a pull request and deployed as units that fail independently.

Agent architecture decides who may do what at run time, when a model chooses the next step and that choice is not deterministic. Its subjects are capability boundaries, blast radius, the trust boundary between model output and real effects, handover contracts, approvals and cost.

The two are orthogonal. A well-layered application can host one agent that holds every credential, and a messy monolith can sit behind well-scoped agents. They meet at the MCP server: in application terms an adapter that translates a protocol into calls on a system, in agent terms a capability boundary where a model’s request becomes an effect.

Agent architecture and application architecture meet at the MCP serverUpper band: agent architecture, decided at run time; agents, one per step, call tools through a policy gate with audit. Lower band: application architecture, decided at design time; each system has its own layers. The MCP servers sit on the line between the bands: an adapter in application terms and a capability boundary in agent terms.agent architecture: who may do what, at run timecapabilities, blast radius, approvals, handovers, costagents, one per steppolicy gate + auditjira MCPcrm MCPMCP server: an adapter (app view)and a capability boundary (agent view)JiraAPI / portdomaindataCRMAPI / portdomaindataapplication architecture: how code is built, at design timelayers, services, data ownership, deploy units
Two orthogonal questions. They meet at the MCP server.

The agentix pattern

We call it the agentix pattern because open-agentix is built around it, not because its parts are new. Least privilege, bounded contexts, ports and adapters and workflow orchestration all predate agents. What we add is a specific combination, written as rules you can check.

Definition. In the agentix pattern every system an agent may touch sits behind exactly one MCP server, which owns its credentials and exposes its capabilities as typed tools, separated into read and write. Every business process is a versioned plan of steps; each step is executed by its own narrowly scoped agent that holds only the tools its step needs, hands over a typed artifact, and reaches any system only through a deterministic policy gate that records every decision under the run id.

Anti-pattern versus the agentix patternTop: one agent holding every tool talks to one do-everything MCP server that holds the credentials of ten applications. Bottom: the agentix pattern. Three agents, one per step (research reads, analysis has no tools, action writes one thing), reach systems only through a policy gate with audit, and each system sits behind its own MCP server; the Git server is not granted to any step.Anti-pattern: one god agent, one do-everything MCP serverone agent, every toolone MCP server, ten apps, every credentialapp 1app 2app 3app 4app 5app 6app 7app 8app 9app 10agentix pattern: one agent per step, one MCP server per systemresearchreads jira, crmanalysismodel onlyactionjira: issue.createpolicy gate (allow / deny / approval) + audit, run idjira MCPcrm MCPgit MCPnot granted
Top: one agent and one MCP server for everything. Bottom: the agentix pattern, one agent per step, one MCP server per system, a gate in between.

The rules and why

R1. One MCP server per system. One server per system or bounded context, and it is the only door to that system. Tools have typed schemas, reads and writes are separate tools, credentials live only there. Why: one door means one place to review, rotate a key and look in the audit trail, and a small server can be reused by every agent that needs that system. One server per tool, the other extreme, multiplies deployments and credentials without adding a boundary. If one system has very different risk classes, say reading invoices and issuing refunds, keep one server but publish separate read and write tool sets and attach policy per tool. Split the server only when the credentials themselves differ.

R2. Business logic becomes 1..n agents, one per step. Express the process as steps. By default one step is one agent; never build a god agent that holds every server. Split when the risk class, data class, privilege, model, failure handling, cost or approval need differs. Keep together when steps share context and privilege: every split costs latency, tokens and coordination, and loses context the next agent would have used.

R3. Agents never talk to systems directly. Every tool call passes a deterministic policy gate (allow, deny, approval) and lands in the audit trail. A model may ask; the decision comes from code you can read and test, outside the prompt.

R4. Handovers are explicit, typed and minimal. Agents pass a schema-typed artifact to the next step, not shared memory and not open chat. A typed handover can be validated, logged and replayed; free text from a step that read hostile input is a path for prompt injection into the next.

R5. Orchestration is data. The order of steps is a versioned plan, not another agent that knows everything. A plan can be diffed, reviewed and pinned; an orchestrator agent with all tools is a god agent with extra steps.

R6. Budgets and identity per step. Each agent has its own limits (steps, tool calls, tokens, cost, time) and its own identity in the audit trail, so a loop stops one step and “who did this?” has a better answer than “the pipeline”.

R7. Observability by run id. Every model call, tool call, policy decision, approval and cost line carries the run id and the agent id. Without that, nobody can show that R1 to R6 held.

A worked example: ticket triage

A ticket about a failed payment arrives. Someone should look up the ticket and the customer, decide whether this needs an engineering issue, and create one if so.

Worked example: ticket triageAn event starts the research agent, which may only call read tools of the Jira and CRM MCP servers. It hands typed findings to the analysis agent, which has no tools. The analysis agent hands a typed decision to the action agent, which may call one write tool, issue.create on the Jira MCP server, and only after a human approval. Every call passes the policy gate and is audited.event: ticket.createdresearchreads onlyanalysismodel only, no toolsactionone write, after approvalfindings.jsondecision.jsonpolicy gate + auditjira MCP serverreadissue.getreadissue.searchwriteissue.createcrm MCP serverreadcustomer.getwritecustomer.updatehuman approval before the callevery call into an MCP server passes the gate and is audited under the run id
Reads and writes are separate tools. Only the action step may write, once, after approval.

If text in the ticket manipulates the research agent, it can only read. The analysis agent holds nothing to misuse. The action agent can create one kind of issue, after a person approves, and cannot read the CRM. As a plan, sketched in the shape of the planned Agent Plan (not a shipped format):

kind: AgentPlan
name: payment-ticket-triage
version: 1.0.0
steps:
  - agent: research
    tools: [jira.issue.get, jira.issue.search, crm.customer.get]
    output: { schema: findings.json }
    budget: { maxToolCalls: 10, maxCostUsd: 0.10 }
  - agent: analysis
    tools: []
    input: findings.json
    output: { schema: decision.json }   # { action: "create" | "none", summary, priority }
  - agent: action
    when: decision.action == "create"
    tools:
      - name: jira.issue.create
        approval: required
        maxCallsPerRun: 1
    input: decision.json

Best practices for MCP server design

Each item says whether the MCP documentation states it (docs) or whether it is our recommendation (ours). Sources are listed at the end.

  • Single responsibility: one system or capability per server (ours, consistent with the docs, which describe servers that “expose specific capabilities” and give one-system examples).
  • Small, typed tool surface: every tool has an input schema with types and required fields (docs: tools are “schema-defined interfaces”; limits and enums are ours).
  • Read and write separated: separate tools, destructive ones named as such (ours; the docs separate read-only resources from model-controlled tools, but not read from write tools).
  • Idempotent and bounded: writes safe to retry, results limited in size (ours).
  • Explicit errors the agent and the audit trail can tell apart (ours).
  • No ambient credentials: secrets resolved inside the server, never in prompts; never pass a client’s token through to a downstream API (docs for the token rule, ours for the rest).
  • Least privilege on scopes: start with minimal scopes, elevate when needed (docs).
  • Per-tool policy and approval: approval or pre-approval per tool call (docs name approval dialogs and permission settings as options; deterministic policy per tool is ours).
  • Versioning of tools and scopes; no silent semantic changes (docs for scopes, ours for tools).
  • Audit: every call attributable to a run and an agent (docs mention activity logs and correlation ids; run and agent ids are ours).

Anti-patterns

  • The god agent: one agent with every server, “because it is simpler”, until the first injection.
  • The Swiss army knife server: one MCP server for many systems. Many credentials, no reuse.
  • MCP-per-endpoint sprawl: a server for every API call. Many doors, no boundary.
  • Shared credentials: the audit trail can no longer say who acted.
  • Agent-to-agent free chat: nothing to validate, nothing to replay.
  • The orchestrator agent that holds everything: R5 broken in the most common way.
  • Policy in the prompt: “never delete anything” is a wish, not a control.

Decision checklist

  1. Can I name each step of this process in one sentence?
  2. Which tools of which servers does each step need, and which only read?
  3. Do neighbouring steps differ in risk, data class, privilege, model, failure handling, cost or approval? Then split; otherwise keep them together.
  4. What does each step hand over, and can I write a schema for it?
  5. Which calls need a person, and who may decide?
  6. Can I reconstruct a run from its run id alone?

What the documentation says, and what is ours

The MCP documentation describes a host that creates one client per server, servers that “expose specific capabilities” with examples per system (file system, database, GitHub, Slack, calendar), tools where each “performs a single operation”, optional user consent before tool execution, and security guidance that forbids token passthrough and recommends minimal scopes. The Claude Code documentation describes subagents that run in their own context window with “specific tool access”, a tools allowlist and an optional per-subagent model and MCP server list. R1, R2 and R3 are consistent with that. Neither source prescribes our rules or endorses this pattern.

Where we go further, or differ: the MCP docs do not say one server per system; one of their own examples is a combined “Calendar/Email Server”. Claude Code subagents return a summary to the main conversation, which itself delegates. Our R4 (typed handovers) and R5 (a plan instead of an orchestrating agent) are stricter choices for unattended, audited runs, not a correction of those docs.

Trade-offs and opinion

More agents mean more handovers, more model calls, more latency and more to maintain. A task with one tool and one reader does not need three agents. Mitigations: keep steps that share privilege together, use small models for narrow steps and plain code where no model is needed, and keep handovers short. The rules borrow from least privilege, microservices, bounded contexts and ports and adapters without being the same thing: microservices split deploy units, R2 splits authority. Fact: tool calls can be intercepted, and a smaller grant reaches less. Opinion: that one step per agent is the right default, one server per system the right granularity, and the extra cost usually worth paying.

Where open-agentix stands

Implemented in 0.1.0: agents.md pipelines of 1..n agents with immutable versions, tool allowlists with argument constraints and approval: required per agent, model and budget per agent, the deterministic policy gate, the MCP gateway, the hash-chained audit trail, and steps and costs per run and agent. On main, not yet released: tenants, connections scoped to tenant, team or agent with secret references only, and role bindings for a single agent.

Not there yet:

  • Typed handovers (R4): the previous output is passed on as text; format: json is parsed but not validated against a schema. Input and output schemas are planned.
  • Conditional plans (R5): a pipeline is an ordered list today; when is planned.
  • Named read/write profiles per server (R1) come with the planned catalog governance; until then, per-agent grants and policy patterns give the same effect.
  • Per-step credentials and isolated workers (R6) are on the roadmap for v0.2.
  • Agent Check and Agent Plan generation, where a model proposes the split for human review, are planned and advisory by design.

References

All accessed 2026-10-04. We paraphrase; quotes are short.

The short version of the pattern lives in the docs. If you think a rule is wrong, or know a case where a god agent is the better design, tell us in GitHub Discussions. We would rather correct the pattern than defend it.