> ## Documentation Index
> Fetch the complete documentation index at: https://acachete.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Architecture

> The full call path from a natural-language request to an on-chain decision, and why each layer trusts the one below it exactly as much as it should.

## The layers

<Frame>
  <img src="https://mintcdn.com/acachete/KAJaZsJCqe34mxL9/images/diagrams/01-architecture.svg?fit=max&auto=format&n=KAJaZsJCqe34mxL9&q=85&s=08a4ecfd47bc0e74a5b1fbcf79e9288b" alt="Vertical architecture diagram: AI Agent to MCP Server (off-chain, untrusted) down through Stellar RPC to the Smart Wallet, which requires the Agent Policy Contract to co-sign before invoking the Target Contract (on-chain, the only place authority lives)." width="1508" height="1931" data-path="images/diagrams/01-architecture.svg" />
</Frame>

<Tip>
  Read this diagram as a trust gradient, not just a call sequence: everything above the "On-chain" boundary can be **fully compromised** -- the agent, the MCP server process, the machine running it -- without an attacker gaining any authority beyond what the policy contract already permits.
</Tip>

### AI Agent

Any MCP-compatible client. It never sees a raw Stellar address it has to get right by hand -- see [MCP Tools](/concepts/mcp-tools) for how tool schemas are generated and how missing addresses default sensibly.

### stellar-agentgate MCP Server

A thin, replaceable layer (`src/mcp-server.ts`) that:

1. Discovers the wallet's current on-chain allow-list (`agent-policy.get_config`) and turns it into MCP tool definitions.
2. Signs the resulting transaction with the agent's **own, independently powerless** Ed25519 key.
3. Surfaces on-chain rejections as ordinary MCP tool errors -- never crashes, never retries around a rejection.

This process holds real key material (the agent's signer), which is why it's still worth protecting in the ordinary ways -- but its compromise alone cannot move funds outside what the policy already allows.

### Stellar RPC

Standard `simulateTransaction` → sign → `sendTransaction` → `getTransaction` flow, with one non-obvious wrinkle specific to this project's custom-account wallet -- see [Signing Flow](/concepts/security-model#the-signing-flow).

### Smart Wallet (passkey-kit custom account)

The wallet's own `__check_auth` decides, for every requested context, which registered signer(s) may authorize it. The agent's signer is registered with `SignerLimits` that require the policy contract as a **required co-signer** for the target contract -- not `SignerLimits(None)` (unlimited), which would let the agent's signature alone satisfy auth with the policy never consulted at all. Getting this exactly backwards was a real bug caught during development; see the [Security Model](/concepts/security-model) page for the full story.

### Agent Policy Contract

The actual security boundary. See [Security Model](/concepts/security-model) for the full decision logic.

### Target Contract

Any Soroban contract the wallet owner chooses to expose -- `example-vault` in this repository's demo, but the pattern generalizes to any contract whose methods make sense to gate this way.

## Why this shape, not a simpler one

<AccordionGroup>
  <Accordion title="Why not just check permissions in the MCP server?">
    Because the MCP server is exactly the thing that can be compromised, misconfigured, or have a bug. An off-chain check is advisory; an on-chain check enforced by the wallet's own `__check_auth` cannot be skipped by anything that isn't a registered, sufficiently-authorized signer.
  </Accordion>

  <Accordion title="Why does the policy need to be a required CO-SIGNER, not just an independent signer?">
    A signer with `SignerLimits(None)` is unlimited -- its own signature is sufficient for anything, and the wallet's `__check_auth` never needs to consult any other signer. Scoping the agent's signer to require the policy as a co-signer for the target contract is what actually forces every call through the policy's allow-list and spend-cap logic.
  </Accordion>

  <Accordion title="Why generate MCP tools from the chain instead of hardcoding them?">
    Two wallets with different policies should expose different tools automatically. Reading `agent-policy.get_config` live means the MCP server's tool list is always exactly what the current on-chain configuration allows -- see [MCP Tools](/concepts/mcp-tools).
  </Accordion>
</AccordionGroup>
