> For the complete documentation index, see [llms.txt](https://gotts.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://gotts.gitbook.io/docs/gotts-safe-mcp-server/mcp-server/15-install-wizard.md).

# Install Wizard

> **Document Type**: OPS (informative) | **Package**: `packages/cli/src/commands/setup/` | **Prerequisites**: [14-deployment.md](/docs/gotts-safe-mcp-server/mcp-server/14-deployment.md), [11-config.md](/docs/gotts-safe-mcp-server/mcp-server/11-config.md), [cli-architecture.md](/docs/prd-shared/cli-architecture.md) | **Last Updated**: 2026-02-24
>
> Interactive CLI that takes an operator from zero to a fully operational Uniswap agent in under 5 minutes. One command. Minimal decisions. No docs required. This is an operational document, not a product requirement -- see [shared/doc-standards.md](/docs/prd-shared/doc-standards.md) for document type definitions.

***

## Problem

Setting up a production Uniswap agent today requires reading 4+ PRD documents, visiting 2-3 external dashboards, manually editing `.env` files, choosing from 7 wallet providers, understanding transport modes, and running multiple deployment commands. The information exists but is scattered across:

* [14-deployment.md](/docs/gotts-safe-mcp-server/mcp-server/14-deployment.md) -- platform selection, deployment commands, security hardening
* [shared/onboarding-workflow.md](/docs/prd-shared/onboarding-workflow.md) -- wallet provisioning, identity registration, guardian setup
* [vault/00-quickstart.md](/docs/gotts-vaults/vault/00-quickstart.md) -- vault participation steps
* [11-config.md](/docs/gotts-safe-mcp-server/mcp-server/11-config.md) -- 25+ environment variables
* [10-wallets.md](/docs/gotts-safe-mcp-server/mcp-server/10-wallets.md) -- 7 wallet types with trade-offs
* [shared/credential-architecture.md](/docs/prd-shared/credential-architecture.md) -- key management model

An operator who just wants to run an agent should not need to read any of this. The wizard encodes all the knowledge from these documents into an interactive flow with opinionated defaults.

***

## Vision

```bash
npx @gotts.ai setup          # Terminal wizard — 5 questions, 5 minutes
npx @gotts.ai setup --ui     # Browser wizard — visual guided setup at localhost:3456
npx @gotts.ai setup --zero   # Zero-dependency — try Gotts in 30 seconds, no accounts needed
npx @gotts.ai setup --pair   # Pair with portal.gotts.ai (recommended — visual wallet setup)
npx @gotts.ai setup --self-hosted  # Direct Privy API setup without portal
npx @gotts.ai install-mcp    # Inject MCP config into Claude/Cursor/VS Code
```

One command. No pre-installation. Multiple entry modes. The operator answers a few questions (or none at all), and the wizard:

1. Creates a wallet (TEE-backed, no raw private keys)
2. Generates all configuration
3. Deploys Gotts Safe
4. Connects it to the operator's AI client (Claude, Cursor, etc.)
5. Verifies everything works

The agent is operational. The operator can start issuing commands immediately.

***

## Design Principles

| Principle                  | How It Applies                                                                                                                                                                 |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Progressive disclosure** | QuickStart asks 5 questions. Advanced exposes every option from [11-config.md](/docs/gotts-safe-mcp-server/mcp-server/11-config.md).                                           |
| **Opinionated defaults**   | Privy wallet, Alchemy RPC, Fly.io deploy, vault-participant policy. These are the right choices for 80% of operators.                                                          |
| **Zero-install**           | Distributed via npx. No global installation, no dependency management.                                                                                                         |
| **Idempotent**             | Re-running detects existing config. Offers to resume, reconfigure, or reset. Never silently overwrites.                                                                        |
| **Non-interactive**        | Every prompt has a CLI flag. CI/CD and agent-driven setup work without human interaction.                                                                                      |
| **Fail-forward**           | Each step validates before proceeding. Failures produce actionable error messages with fix commands. Partial progress is saved.                                                |
| **Agent-friendly**         | Claude and OpenClaw can invoke the wizard autonomously given pre-set API credentials.                                                                                          |
| **Multi-modal**            | Same setup flow accessible as terminal prompts, browser UI (`--ui`), agent-driven CLI, chat Q\&A, or zero-dependency instant start. All modes produce identical configuration. |

### UX References

* **OpenClaw `openclaw onboard`**: QuickStart vs Advanced modes, re-runnable without wiping config, health check at the end, non-interactive flag for scripting, daemon setup for background operation.
* **Vercel `npx create-next-app`**: Zero-install via npx, "Use recommended defaults?" as first question, CLI flags mirror every prompt, progressive disclosure of advanced options.
* **Privy SDK**: Email-based auth with OTP, zero config for simple use cases, auto-daemon management.

***

## Distribution

```bash
# Primary: zero-install via npx
npx @gotts.ai setup

# Browser wizard: visual guided setup
npx @gotts.ai setup --ui

# Zero-dependency: instant start, no accounts
npx @gotts.ai setup --zero

# Alternative: global install
pnpm add -g @gotts.ai/create
uniswap-create

# Alternative: from within the monorepo
pnpm create-agent
```

The package is published to npm as `@gotts.ai/create`. It is a standalone CLI with no dependency on the rest of the monorepo -- it downloads and configures Gotts Safe as needed.

***

## Setup Modes

Six modes of entry, all producing identical output: `.env` file, client config, running MCP server.

| Mode                | Invocation                              | UI                        | Human Required        | Time    | Best For                         |
| ------------------- | --------------------------------------- | ------------------------- | --------------------- | ------- | -------------------------------- |
| **Terminal wizard** | `npx @gotts.ai setup`                   | Terminal (@clack/prompts) | Yes (5 questions)     | \~5 min | Most operators                   |
| **Browser wizard**  | `npx @gotts.ai setup --ui`              | Browser (localhost:3456)  | Yes (visual guided)   | \~5 min | Operators who prefer GUI         |
| **Zero-dependency** | `npx @gotts.ai setup --zero`            | Terminal (zero questions) | No                    | <30 sec | Quick evaluation, testing        |
| **Agent headless**  | `npx @gotts.ai setup --non-interactive` | None                      | No (env vars pre-set) | \~2 min | Remote servers, OpenClaw via SSH |
| **OpenClaw skill**  | "Set up Gotts"                          | Auto-detected             | Depends on env        | \~3 min | OpenClaw agents (any surface)    |
| **Chat app**        | "@bot setup gotts"                      | Messaging app             | Yes (sequential Q\&A) | \~5 min | Telegram/Discord/Slack users     |

### Terminal QuickStart (Default)

Five questions. Opinionated defaults for everything else. Gets an operator to a working agent in under 5 minutes.

```
$ npx @gotts.ai setup

  Uniswap Agent Setup

  What do you want your agent to do?
  > Participate in vaults (earn yield)
    Trade tokens
    Manage liquidity
    Development / testing only

  Wallet provider:
  > Privy (recommended -- TEE-backed, 50K free sigs/month)
    Local key (development only -- NOT for production)

  Privy App ID: _______________
  Privy App Secret: _______________

  RPC provider:
  > Alchemy (recommended -- free tier, all chains)
    Infura
    Custom RPC URL

  Alchemy API Key: _______________

  Where should Gotts Safe run?
  > Local (this machine, stdio for Claude Code / Cursor)
    Fly.io (remote, ~$5/month, accessible from anywhere)
    Docker (self-hosted container)

  Setting up...

  [1/6] Checking prerequisites.............. ok
  [2/6] Creating Privy wallet............... 0x1a2B...3c4D
  [3/6] Register agent identity (recommended)
        Agent name: my-yield-agent (Enter to accept default)
        Registering on Base via Agent0 SDK...
        Agent ID: 42 · Tier: Unverified
  [4/6] Generating configuration............ .env written
  [5/6] Starting MCP server................. running (stdio)
  [6/6] Health check........................ all systems ready

  Your agent is ready.

  Wallet:    0x1a2B...3c4D (Privy, TEE-backed)
  Identity:  Agent #42 (Unverified tier)
  Server:    running locally (stdio)
  Transport: stdio (Claude Code / Cursor auto-discover)

  Next steps:
  1. Open Claude Code or Cursor -- Gotts Safe is already connected.
  2. Try: "What's the price of ETH?"
  3. Try: "Find a vault and deposit 100 USDC"

  Full docs: https://docs.uniswap.org/agents
```

### Terminal Advanced

Full control over every configuration option. Activated via flag or prompt:

```bash
npx @gotts.ai setup --advanced
```

Advanced mode exposes every option from [11-config.md](/docs/gotts-safe-mcp-server/mcp-server/11-config.md):

* Chain selection (which of the 11 chains to enable)
* Custom RPC URLs per chain
* Spending limits (per-tx, per-session, daily)
* Slippage limits
* Rate limits
* Token allowlist mode (strict/warn/off)
* Transport configuration (port, TLS, CORS)
* Logging level and redaction rules
* x402 micropayment configuration (inbound and outbound)
* Vault feature flag (`ENABLE_VAULT`)
* Policy template customization (contract allowlists, method restrictions)

### Browser Wizard (`--ui`)

Starts an Express server on `localhost:3456` and serves a pre-built React app reusing Portal onboarding components from `@gotts.ai/portal` (see [portal/01-overview.md](/docs/website/website/portal/01-overview.md) §4.4). A WebSocket bridge synchronizes state between the terminal process and the browser UI.

**Behavior:**

1. Terminal prints `Setup running at http://localhost:3456 — press Enter to open` and waits
2. Browser opens automatically (unless `--no-open` is passed)
3. Browser UI presents the same questions as Terminal QuickStart, with visual guidance, inline docs, and provider logos
4. When the browser flow completes, control returns to the terminal with the same summary output as terminal mode

**Flags:**

| Flag        | Default | Description                       |
| ----------- | ------- | --------------------------------- |
| `--ui`      | —       | Enable browser wizard mode        |
| `--ui-port` | `3456`  | Port for the local Express server |
| `--no-open` | `false` | Don't auto-open the browser       |

**Key insight:** The `--ui` flag reuses Portal onboarding components — it is not a separate codebase. `@gotts.ai/create` imports from `@gotts.ai/portal` for the browser UI.

### Zero-Dependency Quick Start (`--zero`)

The fastest possible path. Try Gotts in 30 seconds with zero questions, zero API keys, and zero external accounts.

```bash
npx @gotts.ai setup --zero
```

**What happens:**

1. Generates a local private key via `viem/accounts` (random keypair)
2. Starts the MCP server with `data` profile (28 read-only tools)
3. Connects to public RPCs (no Alchemy/Infura key needed)
4. Writes minimal `.env`
5. Outputs client config for auto-discovery

**What you can do:** All 28 read-only data tools — pool info, token prices, trade history, token search, portfolio views, etc.

**What you cannot do:** Write operations (trading, LP management, vault deposits) require a TEE-backed wallet with proper policy configuration.

**Security warning:** The local key is not production-safe. It exists solely for evaluation.

**Upgrade path:** Re-run `npx @gotts.ai setup` (without `--zero`) to upgrade to a Privy-backed wallet with full write capabilities. The wizard detects the existing zero-dep config and offers to upgrade in place.

See [vault/00-quickstart.md](/docs/gotts-vaults/vault/00-quickstart.md) for the zero-dependency quickstart user journey.

### Agent-Driven Setup (Headless)

Claude or OpenClaw can autonomously set up a new agent given pre-configured API credentials. Four patterns, from simplest to most adaptive:

#### Pattern A: Agent Runs the CLI

The agent executes the wizard in non-interactive mode:

```bash
npx @gotts.ai setup \
  --intent vault-participant \
  --wallet-provider privy \
  --deploy-target local \
  --non-interactive
```

Requires: `PRIVY_APP_ID`, `PRIVY_APP_SECRET`, and `ALCHEMY_API_KEY` in the environment.

#### Pattern B: Agent Uses MCP Tools Directly

If Gotts Safe is already running (e.g., with a local dev key), the agent can self-upgrade:

1. Call `provision_wallet` to create a Privy wallet
2. Call `check_setup_health` to verify readiness
3. Call `register_agent` to mint ERC-8004 identity
4. Call `vault_simulate_deposit` to preview
5. Call `vault_deposit` to enter a vault

This pattern requires no CLI at all — the agent uses MCP tools. The wizard is for the initial bootstrap; MCP tools are for ongoing operations.

#### Pattern C: OpenClaw Skill (`setup-gotts-agent`)

An OpenClaw skill wraps the wizard for discovery via ClawHub with adaptive environment detection:

```yaml
# packages/safe/skills/setup-gotts-agent/SKILL.md
---
description: Set up a Gotts agent with wallet, identity, and vault access
triggers:
  - "set up gotts"
  - "configure uniswap agent"
  - "deploy gotts safe"
parameters:
  intent:
    type: string
    default: vault-participant
    options:
      [
        vault-participant,
        vault-manager,
        vault-creator,
        trader,
        liquidity-manager,
        development,
      ]
  wallet_provider:
    type: string
    default: auto
    options: [auto, privy, local]
---
# Setup Gotts Agent

## Workflow

1. **Detect environment:**
- `hasDisplay()` — checks `DISPLAY` env (Linux), always `true` on macOS login session
- `hasConfigFile()` — checks for `.env` with `PRIVY_APP_ID` set
- `hasExistingSetup()` — checks for `config.json` or running MCP process

2. **Route to appropriate mode:**
- Config exists + no display (SSH) → run `npx @gotts.ai setup --non-interactive`
- No config + no display → run `npx @gotts.ai setup --zero`, prompt operator for credentials later
- No config + display (Mac Mini) → run `npx @gotts.ai setup --ui` for hybrid human-agent setup
- Existing setup detected → skip setup, report status via `check_setup_health`

3. **Post-setup verification:**
- Call `check_setup_health` to confirm MCP server is running
- If identity registration is available, offer to register via `register_agent`
- Report final status with wallet address, server transport, and available tool count
```

The skill invokes the wizard with the appropriate mode flag based on detected environment capabilities.

#### Pattern D: Chat App Interface

Protocol-agnostic conversational setup for Telegram, Discord, Slack, and other messaging platforms. See the [Chat App Setup Protocol](#chat-app-setup-protocol) section below for the full state machine definition.

### Chat App Interface

Operators interacting with a Gotts agent exclusively through a messaging app (Telegram, Discord, Slack) need a sequential, message-based setup flow. No forms, no browser — just numbered options and text replies.

**Flow overview:**

1. User sends setup intent (e.g., "@bot setup gotts")
2. Bot walks through 5-7 states, each producing one message and expecting one reply
3. Bot produces `gotts-setup.json` passable to `npx @gotts.ai setup --from-config`
4. Bot executes setup and reports results

**Security note:** Credentials should be set as environment variables on the bot server, not pasted into chat messages. The chat flow should detect pre-configured credentials and skip those prompts.

See [shared/user-journeys.md](/docs/prd-shared/user-journeys.md) §6c for the chat-based user journey.

***

## Wizard Flow

```mermaid
flowchart TD
    %% Entry points
    Terminal["npx @gotts.ai setup"] --> DetectExisting{Existing config?}
    BrowserEntry["npx @gotts.ai setup --ui"] --> StartBrowser["Start Express server<br/>on localhost:3456"]
    ZeroEntry["npx @gotts.ai setup --zero"] --> ZeroDep["Generate local key<br/>+ start data server"]
    HeadlessEntry["--non-interactive<br/>(env vars pre-set)"] --> PrereqCheck
    ChatEntry["@bot setup gotts"] --> ChatStateMachine["Chat state machine<br/>(5-7 messages)"]
    SkillEntry["OpenClaw: Set up Gotts"] --> DetectEnv{"Detect environment"}

    %% Existing config handling
    DetectExisting -->|Yes| ResumeChoice{"Found existing setup.<br/>What would you like to do?"}
    ResumeChoice -->|Resume| ResumeFromFailure["Resume from last<br/>successful step"]
    ResumeChoice -->|Reconfigure| AdvancedMode["Re-run with<br/>current values as defaults"]
    ResumeChoice -->|Reset| ConfirmReset["Confirm reset<br/>then start fresh"]

    DetectExisting -->|No| ModeSelect{"Use recommended<br/>defaults?"}
    ModeSelect -->|Yes| QuickStart
    ModeSelect -->|"No, customize"| AdvancedMode

    %% OpenClaw environment detection
    DetectEnv -->|"No display + no config"| ZeroDep
    DetectEnv -->|"No display + config"| HeadlessEntry
    DetectEnv -->|"Display + no config"| StartBrowser
    DetectEnv -->|"Existing setup"| HealthCheck

    %% Browser path converges to QuickStart answers
    StartBrowser --> BrowserUI["Browser UI collects<br/>same answers as terminal"]
    BrowserUI --> PrereqCheck

    %% Chat path produces config file
    ChatStateMachine --> FromConfig["--from-config<br/>gotts-setup.json"]
    FromConfig --> PrereqCheck

    subgraph quickstart [Terminal QuickStart Flow]
        QuickStart["Select intent"] --> WalletProvider["Select wallet provider"]
        WalletProvider --> WalletCreds["Enter provider credentials"]
        WalletCreds --> RpcProvider["Select RPC provider"]
        RpcProvider --> DeployTarget["Select deploy target"]
    end

    subgraph setup [Setup Execution]
        DeployTarget --> PrereqCheck["1. Check prerequisites"]
        PrereqCheck --> CreateWallet["2. Create wallet via SDK"]
        CreateWallet --> OfferIdentity{"3. Register ERC-8004<br/>identity? (recommended)"}
        OfferIdentity -->|Yes| GasResolve["Resolve gas<br/>(paymaster/faucet/manual)"]
        GasResolve --> RegisterId["Register identity"]
        RegisterId --> GenConfig["4. Generate .env + config"]
        OfferIdentity -->|"Later/Skip"| GenConfig
        GenConfig --> ApplyPolicy["4b. Apply wallet policy"]
        ApplyPolicy --> Deploy["5. Deploy / start server"]
        Deploy --> HealthCheck["6. Health check"]
    end

    %% Zero-dependency shortcut
    ZeroDep --> ZeroHealth["Health check<br/>(data tools only)"]
    ZeroHealth --> Done

    subgraph optional [Optional: Vault Deposit]
        HealthCheck --> OfferVault{"Deposit into<br/>a vault?"}
        OfferVault -->|Yes| VaultDeposit["Find vault + deposit"]
        OfferVault -->|No| Done
        VaultDeposit --> Done
    end

    Done["Done -- output client config<br/>+ next steps"]

    ConfirmReset --> QuickStart
    AdvancedMode --> PrereqCheck
```

### Step Details

#### Step 0: Detect Existing Configuration

Before any prompts, the wizard checks for existing state:

* `packages/safe/.env` exists
* `fly.toml` exists
* `gotts.config.json` exists
* MCP server process is running

If any exist, the wizard offers three options:

| Option          | Behavior                                                                      |
| --------------- | ----------------------------------------------------------------------------- |
| **Resume**      | Skip completed steps, continue from the first incomplete one                  |
| **Reconfigure** | Re-run all prompts with current values as defaults (easy to change one thing) |
| **Reset**       | Confirm, then delete config files and start fresh                             |

This makes the wizard safe to re-run at any time.

#### Step 1: Select Intent

```
What do you want your agent to do?
> Participate in vaults (earn yield)
  Trade tokens
  Manage liquidity
  Development / testing only
```

The intent determines the **tool profile** and downstream defaults. The tool profile controls which of the server's 147 tools are registered at startup -- agents only see the tools relevant to their use case. See [02-architecture.md](/docs/gotts-safe-mcp-server/mcp-server/02-architecture.md) for the full profile system.

| Intent                | Tool Profile | Policy Preset                                                                                              | Deploy Default | Chains                   |
| --------------------- | ------------ | ---------------------------------------------------------------------------------------------------------- | -------------- | ------------------------ |
| **Vault participant** | `vault`      | **Participant** (see [credential-architecture.md](/docs/prd-shared/credential-architecture.md) Section 7a) | Local          | Base                     |
| **Vault manager**     | `vault`      | **Manager**                                                                                                | Local          | Base                     |
| **Vault creator**     | `vault`      | **Creator**                                                                                                | Local          | Base                     |
| **Trader**            | `trader`     | `defi-trading`                                                                                             | Local          | Ethereum, Base, Arbitrum |
| **Liquidity manager** | `lp`         | `defi-trading`                                                                                             | Local          | Ethereum, Base, Arbitrum |
| **Development**       | `dev`        | None (all allowed)                                                                                         | Local (stdio)  | Ethereum fork            |

The vault intents map to the three named policy presets defined in [shared/credential-architecture.md](/docs/prd-shared/credential-architecture.md) Section 7a. Each preset outputs a tool profile list, wallet policy allowlist, daily aggregate limits, and emergency cancel authority configuration.

Advanced mode: full policy customization, chain selection, per-contract allowlists, profile composition (e.g., `trader,vault`), and per-tool enable/disable overrides.

#### Step 2: Select Wallet Provider

```
Wallet provider:
> Privy (recommended -- TEE-backed, 50K free sigs/month)
  Local key (development only -- NOT for production)
```

| Provider  | Prompts Required         | Auto-Created                                    | Policy Engine                                |
| --------- | ------------------------ | ----------------------------------------------- | -------------------------------------------- |
| **Privy** | App ID, App Secret       | Yes (via `@privy-io/node` or browser WebCrypto) | Yes (contract + method + amount + recipient) |
| **Local** | None (generates keypair) | Yes (random key via viem)                       | No                                           |

The wizard generates the wallet programmatically. No dashboard visit required for the wallet itself. The Privy App ID and App Secret still require a one-time visit to [console.privy.io](https://console.privy.io).

**Credential validation**: The wizard validates credentials immediately after entry by making a test API call. Invalid credentials produce an actionable error before proceeding.

#### Step 3: Enter Provider Credentials

Prompts depend on the wallet provider selected:

**Privy**:

```
Privy App ID: clx1234567890abcdef
Privy App Secret: ********************************
```

Get these from [console.privy.io](https://console.privy.io) → your app → Settings → API Keys.

**Local** (development):

```
No credentials needed. A random keypair will be generated.
Warning: local keys are NOT secure for production use.
```

The wizard validates Privy credentials by calling the provider's API immediately. If validation fails, the operator is prompted to re-enter or choose a different provider.

#### Step 4: Select RPC Provider

```
RPC provider:
> Alchemy (recommended -- free tier, 300M compute units/month)
  Infura
  Custom RPC URL
  Skip (use public RPCs -- slower, rate-limited)
```

If Alchemy or Infura: prompt for API key, validate with a test `eth_blockNumber` call.

If custom: prompt for URL, validate connectivity.

If skip: warn about rate limits and reliability. Public RPCs are acceptable for development but not production.

Advanced mode: per-chain RPC URL configuration.

#### Step 5: Select Deploy Target

```
Where should Gotts Safe run?
> Local (this machine -- for Claude Code, Cursor, Claude Desktop)
  Fly.io (remote server, ~$5/month, accessible from anywhere)
  Docker (self-hosted container, requires Docker installed)
  Railway (remote, usage-based pricing)
```

| Target              | Prerequisites                      | Cost                | Access From        |
| ------------------- | ---------------------------------- | ------------------- | ------------------ |
| **Local** (default) | None                               | Free                | This machine only  |
| **Fly.io**          | `flyctl` installed, Fly.io account | \~$5/month          | Anywhere (HTTPS)   |
| **Docker**          | Docker installed                   | Infrastructure cost | Network-accessible |
| **Railway**         | `railway` CLI, Railway account     | Usage-based         | Anywhere (HTTPS)   |

The wizard checks prerequisites for the selected target before proceeding. If `flyctl` is not installed, it offers to install it automatically (`curl -L https://fly.io/install.sh | sh`).

#### Step 6: Execute Setup

Non-interactive execution with progress indicators. Identity registration is now integrated as step \[3/6], between wallet creation and config generation, so that `agentId` can be written to the config file.

```
[1/6] Checking prerequisites.............. ok
      Node.js 22.1.0, pnpm 9.x
[2/6] Creating Privy wallet............... ok
      Address: 0x1a2B...3c4D
      Wallet ID: wlt_abc123xyz
[3/6] Register agent identity (recommended)
      Agent name: my-yield-agent
      Registering on Base via Agent0 SDK...
      Agent ID: 42 · Tier: Unverified
[4/6] Generating configuration............ ok
      Written: packages/safe/.env (15 variables)
      Written: gotts.config.json
[4b]  Applying vault-participant policy... ok
      Allowed contracts: 4
      Transfer limit: 1,000 USDC/tx, 5,000 USDC/day
[5/6] Starting MCP server................. ok
      Transport: stdio
      PID: 12345
[6/6] Health check........................ ok
      Wallet: connected (Privy)
      RPC: connected (11 chains)
      Identity: Agent #42 (Unverified)
      Balances: 0 ETH on Base (fund to execute transactions)
```

**Step \[3/6] — Identity Registration Prompt:**

```
Register agent identity on Base? (recommended — makes your agent discoverable)
  > Yes (requires ~$0.01 ETH on Base for gas)
    Later (I'll register via MCP tool after funding)
    Skip (no identity needed for my use case)
```

If "Yes": The wizard runs gas resolution (see below), then calls `registerAgent()` from `@gotts.ai/wallet`. Uses the three-tier registration fallback documented in [shared/erc8004-integration.md](/docs/prd-shared/erc8004-integration.md).

If "Later": Sets `GOTTS_AGENT_NAME` in `.env` so the MCP `register_agent` tool can use it later. Output: `"Fund 0x1a2B...3c4D with ETH on Base, then ask your agent: 'Register my identity'"`.

If "Skip": No identity registration. The agent can still use read-only tools and deposit into vaults (at Unverified tier caps).

**Gas Resolution Logic** (runs automatically before registration tx):

```
1. Is this a testnet (Sepolia, Base Sepolia)?
   → Check balance. If zero, call Gotts faucet API (api.gotts.ai/v1/faucet/request)
   → If faucet succeeds: proceed with registration
   → If faucet fails (already claimed / rate limited): show address, ask operator to fund

2. Is this Base mainnet?
   → Try paymaster-sponsored ERC-4337 UserOp (ERC-7677 paymaster)
   → If paymaster succeeds: zero ETH needed, proceed
   → If paymaster fails: fall back to direct tx, check balance

3. Is wallet funded?
   → Yes: proceed with registration
   → No: show address + "Send ~0.005 ETH to 0x1a2B...3c4D", wait up to 60s, offer "Later"
```

See [shared/erc8004-integration.md](/docs/prd-shared/erc8004-integration.md) Section 6 for the full gas strategy spec and [shared/testnet-faucet.md](/docs/prd-shared/testnet-faucet.md) for the faucet API.

If any step fails, the wizard:

1. Displays the error with context
2. Suggests a specific fix
3. Offers to retry or skip (if the step is optional — identity registration is always skippable)
4. Saves progress so `npx @gotts.ai setup` can resume

#### Step 7: Optional -- First Vault Deposit

```
Deposit into a vault to start earning yield?
> Yes
  No, I'll do it later

Finding best Simple Yield vault on Base...
  Vault: 0xdef456... (TVL: $1.2M, APY: 4.2%)

How much USDC to deposit? 100

Simulating deposit...
  Expected shares: 99.85
  Share price: $1.0015

Confirm deposit of 100 USDC?
> Yes

Depositing...
  Transaction: 0x789abc...
  Shares received: 99.85
  First Deposit milestone earned (70 reputation points)
```

#### Step 8: Output and Next Steps

The wizard concludes with a clear summary and client configuration:

```
Your agent is ready.

Wallet:    0x1a2B...3c4D (Privy, TEE-backed)
Identity:  Agent #42 (Unverified tier)
Server:    running locally (stdio, PID 12345)
Vault:     100 USDC deposited in 0xdef456...

API Keys:
  Read key:     gotts_read_xxx...     (monitoring dashboards)
  Feedback key: gotts_feedback_xxx... (Portal -- recommended)
  Write key:    gotts_write_xxx...   (agent -- keep secure)

Client configuration has been written to:
  .cursor/mcp.json     (Cursor -- auto-discovered)
  .claude/mcp.json     (Claude Code -- auto-discovered)

For Claude Desktop, add this to Settings > Connectors:
  {
    "uniswap": {
      "command": "node",
      "args": ["<path>/packages/safe/dist/index.js"],
      "env": { "NODE_ENV": "production" }
    }
  }

Next steps:
  1. Open Claude Code or Cursor -- the server is already connected.
  2. Try: "What's the price of ETH on Base?"
  3. Try: "Show my vault position"
  4. Run `npx @gotts.ai portal` to open your agent dashboard.
  5. Re-run this wizard anytime: npx @gotts.ai setup

Need help? https://docs.uniswap.org/agents
```

For remote deployments (Fly.io, Railway), the output includes the HTTPS URL and bearer token instead of local config.

***

## CLI Pairing Protocol (Workflow 2: Browser UI → CLI)

The pairing protocol lets a CLI instance securely claim a wallet created in the browser Portal. This enables the recommended operator flow: create and fund a Privy wallet using the Portal's full UI, then pair it with the local CLI in a single step.

### Why Pairing

Direct credential entry (Workflow 1: `--self-hosted`) requires the operator to locate, copy, and paste Privy API credentials from the dashboard. Pairing eliminates that friction: the Portal already holds a valid Privy session, so it can create the wallet and policy on behalf of the operator and pass a ready-to-use configuration to the CLI via a secure short-lived session.

### Pairing Flow

```
$ npx @gotts.ai setup --pair

  Connecting to Gotts Portal...

  Pairing code: RIVER EAGLE GLASS MOON

  Open this URL to complete setup:
  https://portal.gotts.ai/cli-setup?session=8f3a...c921

  Waiting for browser confirmation... (polls every 2s, times out in 5 min)
```

**Step-by-step:**

1. **CLI generates P-256 keypair** — using `@gotts.ai/crypto`'s `generateKeyPair()`. The private key stays in memory; the public key is sent to the Portal.
2. **CLI generates session ID** — 64-char hex (256-bit random), generated via `crypto.randomBytes(32).toString('hex')`.
3. **CLI generates pairing code** — 4 BIP-39 words sampled from the wordlist using `crypto.randomBytes(8)`. Provides \~32 bits of entropy. Used for out-of-band confirmation (operator sees the same words in browser and terminal).
4. **CLI registers session on Portal** — `POST /api/cli-session` with `{ sessionId, publicKey, pairingCode }`. Portal stores the session with 5-minute TTL.
5. **CLI displays pairing code + URL** — The URL embeds the session ID: `portal.gotts.ai/cli-setup?session=<sessionId>`. Auto-opens browser if `--no-open` is not set.
6. **Operator authenticates in Portal** — Signs in with Privy (email/social), sees the pairing code, confirms it matches terminal output.
7. **Portal creates wallet and policy** — Calls Privy API to create a server wallet with the selected policy preset (Participant/Manager/Creator). Stores wallet ID, wallet address, and policy ID in the session: `PATCH /api/cli-session/<sessionId>` with `{ walletId, walletAddress, policyId, privyAppId }`.
8. **CLI polls for completion** — `GET /api/cli-session/<sessionId>` every 2 seconds. Times out after 5 minutes with a clear message.
9. **CLI writes config** — On successful claim, writes `~/.gotts/config.json` (mode 0o600) with:

   ```json
   {
     "mode": "privy",
     "walletId": "wlt_abc123xyz",
     "walletAddress": "0x1a2B...3c4D",
     "authPrivateKey": "<base64-encoded P-256 private key>",
     "privyAppId": "clx1234567890abcdef",
     "webAppUrl": "https://portal.gotts.ai"
   }
   ```

   Then runs Steps 4-9 of the standard wizard flow (policy, deploy, health check, identity, vault).

### Security Properties

| Property                      | Implementation                                                                                                                 |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| **Pairing code verification** | Timing-safe comparison (`crypto.timingSafeEqual`) on Portal side. Aborts session after 10 failed attempts.                     |
| **Session scoping**           | Each session contains only one wallet's config. The Portal never returns credentials for a wallet the operator did not select. |
| **Key not transmitted**       | The CLI's P-256 private key never leaves the CLI process. Only the public key is sent to the Portal.                           |
| **Short TTL**                 | Sessions expire after 5 minutes. Expired sessions return 404; CLI prompts to restart.                                          |
| **Rate limiting**             | Portal enforces 10 pairing attempts per session ID and 20 per minute per IP.                                                   |
| **HTTPS only**                | All polling uses HTTPS. Self-signed certificates are rejected.                                                                 |

### Session API (Portal side)

Implemented in `packages/portal/src/app/api/cli-session/` — see [portal/01-overview.md](/docs/website/website/portal/01-overview.md) §7a for the full API route specification.

| Route                              | Method  | Purpose                                                         |
| ---------------------------------- | ------- | --------------------------------------------------------------- |
| `/api/cli-session`                 | `POST`  | Create new session (CLI → Portal)                               |
| `/api/cli-session/[id]`            | `GET`   | Poll session status (CLI → Portal)                              |
| `/api/cli-session/[id]`            | `PATCH` | Claim session with wallet config (Portal UI → Portal API)       |
| `/api/cli-session/[id]/public-key` | `GET`   | Retrieve CLI's public key (Portal UI → Portal API, for display) |

***

## Self-Hosted Mode (Workflow 1: Direct API Setup)

For operators who prefer not to use the Portal or who are deploying in environments without browser access (CI servers, remote machines, headless deployments).

```bash
npx @gotts.ai setup --self-hosted
```

**Flow:**

```
$ npx @gotts.ai setup --self-hosted

  Self-Hosted Privy Setup

  Get your credentials from console.privy.io → Settings → API Keys

  Privy App ID: clx1234567890abcdef
  Privy App Secret: ********************************

  Generating P-256 authorization keypair...
  Storing credentials in OS keychain...
  Creating Privy wallet...
  Applying vault-participant policy...

  Wallet: 0x1a2B...3c4D (Privy, wlt_abc123xyz)
  Credentials stored in macOS Keychain (service: gotts-privy)
```

**Steps:**

1. Prompt for `PRIVY_APP_ID` and `PRIVY_APP_SECRET` (or read from env)
2. Validate credentials via a test Privy API call
3. Generate a P-256 keypair using `@gotts.ai/crypto`'s `generateKeyPair()`
4. Store `privyAppId`, `privyAppSecret`, and `authPrivateKey` in the OS keychain via `@gotts.ai/crypto`'s `storePrivyCredentials()` (macOS Keychain / Linux Secret Service / Windows Credential Manager via `keytar`)
5. Call Privy API to create a server wallet with the generated P-256 public key as authorization key
6. Apply selected policy preset (Participant/Manager/Creator)
7. Write `~/.gotts/config.json` (mode 0o600)
8. Continue with standard Steps 4-9 (deploy, health check, identity, vault)

**Security notes:**

* The P-256 private key is stored in the OS keychain, not in `.env` or plain config files
* The Privy App Secret is also keychain-stored; it is not written to `~/.gotts/config.json`
* On Linux systems without a keychain daemon, the wizard falls back to writing to `~/.gotts/config.json` with a warning
* Self-hosted mode requires manual credential rotation; pairing mode (Workflow 2) can be re-run to refresh credentials via the Portal

***

## `install-mcp` Subcommand

A standalone subcommand for operators who have already completed wallet setup and just need to inject Gotts Safe into their AI client's MCP configuration.

```bash
npx @gotts.ai install-mcp
npx @gotts.ai install-mcp --global          # Claude Desktop (global config)
npx @gotts.ai install-mcp --local           # Project-level config (.cursor/mcp.json)
npx @gotts.ai install-mcp --client cursor   # Target a specific client
```

**What it does:**

1. **Detects installed IDE clients** by checking for config file presence and process names:
   * Claude Desktop: `~/Library/Application Support/Claude/claude_desktop_config.json`
   * Claude Code: `~/.claude.json` or `.mcp.json` (project)
   * Cursor: `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (project)
   * VS Code: `.vscode/mcp.json`
   * Windsurf: `~/.codeium/windsurf/mcp_config.json`
   * Continue.dev: `.continue/config.yaml`
2. **Reads `~/.gotts/config.json`** to get wallet address and server configuration (or `--wallet-address` / `--server-url` flags for override)
3. **Reads existing MCP config**, creates a timestamped backup (e.g., `mcp.json.bak.1740000000000`), then merges the Gotts Safe entry without overwriting existing servers
4. **Writes back** in the correct format for the target client (JSON / YAML / TOML — see [Per-Client Config Formats](#per-client-config-formats))

**Flags:**

| Flag                      | Description                                                                                            |
| ------------------------- | ------------------------------------------------------------------------------------------------------ |
| `--global`                | Write to global client config (default for Claude Desktop)                                             |
| `--local`                 | Write to project-level config (default for Cursor, Claude Code)                                        |
| `--client <name>`         | Target a specific client (`claude-desktop`, `claude-code`, `cursor`, `vscode`, `windsurf`, `continue`) |
| `--server-url <url>`      | Override server URL (for remote deployments)                                                           |
| `--wallet-address <addr>` | Override wallet address shown in config comments                                                       |
| `--dry-run`               | Show what would be written without modifying files                                                     |
| `--force`                 | Overwrite existing Gotts Safe entry if present                                                         |

**Example output:**

```
Detected clients: Claude Code, Cursor

Updating Claude Code config (~/.claude.json)...
  ✓ Backed up to ~/.claude.json.bak.1740000000000
  ✓ Added gotts-safe entry (stdio transport)

Updating Cursor config (~/.cursor/mcp.json)...
  ✓ Backed up to ~/.cursor/mcp.json.bak.1740000000000
  ✓ Added gotts-safe entry (stdio transport)

Restart your AI client to pick up the new MCP server.
```

***

## Non-Interactive Mode

Every prompt has a corresponding CLI flag. This enables CI/CD, scripting, and agent-driven setup:

```bash
npx @gotts.ai setup \
  --intent vault-participant \
  --wallet-provider privy \
  --privy-app-id $PRIVY_APP_ID \
  --privy-app-secret $PRIVY_APP_SECRET \
  --rpc-provider alchemy \
  --alchemy-api-key $ALCHEMY_API_KEY \
  --deploy-target local \
  --skip-identity \
  --skip-vault \
  --non-interactive
```

### Full Flag Reference

| Flag                      | Prompt Equivalent                                                                 | Values                                                            |
| ------------------------- | --------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
| `--intent`                | "What do you want your agent to do?"                                              | `vault-participant`, `trader`, `liquidity-manager`, `development` |
| `--wallet-provider`       | "Wallet provider"                                                                 | `privy`, `local`                                                  |
| `--privy-app-id`          | "Privy App ID"                                                                    | String                                                            |
| `--privy-app-secret`      | "Privy App Secret"                                                                | String                                                            |
| `--rpc-provider`          | "RPC provider"                                                                    | `alchemy`, `infura`, `custom`, `public`                           |
| `--alchemy-api-key`       | "Alchemy API Key"                                                                 | String                                                            |
| `--infura-api-key`        | "Infura API Key"                                                                  | String                                                            |
| `--rpc-url`               | "Custom RPC URL"                                                                  | URL                                                               |
| `--deploy-target`         | "Where should Gotts Safe run?"                                                    | `local`, `fly`, `docker`, `railway`                               |
| `--chains`                | (Advanced) Chain selection                                                        | Comma-separated chain IDs: `1,8453,42161`                         |
| `--spending-limit-per-tx` | (Advanced) Per-tx limit                                                           | USD amount                                                        |
| `--spending-limit-daily`  | (Advanced) Daily limit                                                            | USD amount                                                        |
| `--enable-vault`          | (Advanced) Vault tools                                                            | `true`, `false`                                                   |
| `--enable-x402`           | (Advanced) x402 payments                                                          | `true`, `false`                                                   |
| `--agent-name`            | Agent name for ERC-8004 registration                                              | String (e.g., `my-yield-agent`)                                   |
| `--skip-identity`         | Skip ERC-8004 registration                                                        | Boolean flag                                                      |
| `--skip-vault`            | Skip vault deposit                                                                | Boolean flag                                                      |
| `--skip-health-check`     | Skip health verification                                                          | Boolean flag                                                      |
| `--non-interactive`       | Suppress all prompts                                                              | Boolean flag                                                      |
| `--ui`                    | Enable browser wizard mode                                                        | Boolean flag                                                      |
| `--ui-port`               | Port for browser wizard server                                                    | Number (default: `3456`)                                          |
| `--no-open`               | Don't auto-open browser (with `--ui`)                                             | Boolean flag                                                      |
| `--zero`                  | Zero-dependency quick start (data profile, local key, public RPCs)                | Boolean flag                                                      |
| `--pair`                  | Browser pairing mode — open portal.gotts.ai to create wallet, CLI receives config | Boolean flag                                                      |
| `--self-hosted`           | Direct Privy API setup (no portal) — prompts for App ID + Secret                  | Boolean flag                                                      |
| `--from-config`           | Load answers from a JSON config file                                              | File path (e.g., `gotts-setup.json`)                              |
| `--advanced`              | Enable Advanced mode                                                              | Boolean flag                                                      |
| `--reset`                 | Delete existing config and start fresh                                            | Boolean flag                                                      |
| `--verbose`               | Show debug output                                                                 | Boolean flag                                                      |
| `--dry-run`               | Show what would be done without executing                                         | Boolean flag                                                      |

Environment variables are also read as fallbacks: `PRIVY_APP_ID`, `PRIVY_APP_SECRET`, `ALCHEMY_API_KEY`, etc. CLI flags take precedence over environment variables.

***

## Error Handling and Recovery

### Principle: Every Error is Actionable

No generic "something went wrong" messages. Every error includes:

1. **What happened** (specific, technical)
2. **Why it happened** (root cause)
3. **How to fix it** (specific command or action)

### Common Failures

| Step                  | Error                     | Message                                                                          | Recovery                                                                                                                             |
| --------------------- | ------------------------- | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| Prerequisites         | Node.js < 20              | "Node.js 20+ required. Current: 18.2.0. Run: `nvm install 20`"                   | Auto-suggest install command                                                                                                         |
| Wallet creation       | Invalid Privy credentials | "Privy API returned 401. Verify your App ID and Secret at console.privy.io"      | Re-prompt for credentials                                                                                                            |
| Wallet creation       | Privy rate limit          | "Privy rate limit reached. Wait 60 seconds and re-run."                          | Auto-retry with backoff                                                                                                              |
| RPC validation        | Invalid API key           | "Alchemy returned 401 for eth\_blockNumber. Verify key at alchemy.com/dashboard" | Re-prompt for key                                                                                                                    |
| Deploy (Fly.io)       | `flyctl` not installed    | "flyctl not found. Install: `curl -L https://fly.io/install.sh \| sh`"           | Offer to install automatically                                                                                                       |
| Deploy (Fly.io)       | Not authenticated         | "Not logged in to Fly.io. Run: `fly auth login`"                                 | Open browser for auth                                                                                                                |
| Health check          | Wallet cannot sign        | "Wallet signing test failed. Check PRIVY\_APP\_SECRET is correct."               | Re-prompt for credentials                                                                                                            |
| Health check          | RPC unreachable           | "Cannot reach Ethereum RPC. Check ALCHEMY\_API\_KEY or network."                 | Retry, suggest fallback                                                                                                              |
| Identity registration | Insufficient gas          | "Need \~0.005 ETH on Base for gas. Send ETH to 0x1a2B...3c4D"                    | Auto-resolve via paymaster (Base) or faucet (testnet); fall back to showing wallet address. See gas resolution logic in Step \[3/6]. |
| Vault deposit         | Insufficient USDC         | "Need USDC on Base to deposit. Send USDC to 0x1a2B...3c4D"                       | Display wallet address, wait for funding                                                                                             |

### Progress Persistence

The wizard saves progress to `~/.uniswap-agent/setup-state.json`:

```json
{
  "version": "1.0.0",
  "startedAt": "2026-02-14T10:00:00Z",
  "completedSteps": ["prerequisites", "wallet", "config", "policy"],
  "failedStep": "deploy",
  "failedReason": "flyctl not installed",
  "config": {
    "intent": "vault-participant",
    "walletProvider": "privy",
    "walletAddress": "0x1a2B...3c4D",
    "rpcProvider": "alchemy",
    "deployTarget": "fly"
  }
}
```

Re-running `npx @gotts.ai setup` detects this file and offers to resume from the failed step.

***

## Technical Implementation

### Package Structure

```
packages/create/
├── package.json
├── tsconfig.json
├── src/
│   ├── index.ts              # Entry point (#!/usr/bin/env node)
│   ├── cli.ts                # Argument parsing (commander or yargs)
│   ├── wizard/
│   │   ├── quickstart.ts     # Terminal QuickStart flow
│   │   ├── advanced.ts       # Terminal Advanced flow
│   │   ├── resume.ts         # Resume from saved state
│   │   └── prompts.ts        # All prompt definitions
│   ├── modes/
│   │   ├── zero-dep.ts       # Zero-dependency quick start (--zero)
│   │   ├── self-hosted.ts    # Self-hosted mode (--self-hosted)
│   │   ├── pairing.ts        # CLI pairing protocol (browser-based wallet setup)
│   │   ├── chat.ts           # Chat app state machine (--from-config producer)
│   │   └── from-config.ts    # Load answers from gotts-setup.json (--from-config)
│   ├── ui/
│   │   ├── server.ts         # Express server for browser wizard (--ui)
│   │   ├── ws.ts             # WebSocket bridge between terminal and browser
│   │   └── static/           # Pre-built React app (Portal onboarding components)
│   ├── steps/
│   │   ├── prerequisites.ts  # Node version, CLI tools
│   │   ├── wallet.ts         # Wallet creation (Privy, local)
│   │   ├── config.ts         # .env and config file generation
│   │   ├── policy.ts         # Wallet policy application
│   │   ├── deploy.ts         # Server deployment (local, Fly, Docker, Railway)
│   │   ├── health.ts         # Health check via MCP Inspector
│   │   ├── identity.ts       # ERC-8004 registration
│   │   └── vault.ts          # Vault discovery and deposit
│   ├── providers/
│   │   ├── privy.ts          # Privy wallet creation via @privy-io/node
│   │   └── local.ts          # Local keypair generation via viem
│   ├── install-mcp/
│   │   ├── index.ts          # `create install-mcp` subcommand entry
│   │   ├── detect.ts         # IDE detection (Claude Desktop, Claude Code, Cursor, etc.)
│   │   ├── inject.ts         # Config read → merge → write with backup
│   │   └── formats.ts        # Per-client config format handlers (JSON/YAML/TOML)
│   ├── output/
│   │   ├── env.ts            # .env file writer
│   │   ├── client-config.ts  # Claude/Cursor/Desktop config writer
│   │   └── summary.ts        # Final summary renderer
│   └── utils/
│       ├── state.ts          # Progress persistence (~/.gotts/setup-state.json)
│       ├── spinner.ts        # Spinner from @gotts.ai/tui (no ora dependency)
│       ├── env-detect.ts     # Environment detection (hasDisplay, hasConfig, etc.)
│       └── validate.ts       # Input validators
└── dist/                     # Build output
```

### Dependencies

| Package               | Purpose                                                                 | Size                         | Notes                                                                                                              |
| --------------------- | ----------------------------------------------------------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `@clack/prompts`      | Interactive prompts — the modern standard (\~2.5M weekly npm downloads) | \~25KB                       | Used by create-t3-app, create-astro, create-svelte. Re-exported via `@gotts.ai/tui/prompts` with branded wrappers. |
| `commander`           | CLI argument parsing                                                    | \~30KB                       |                                                                                                                    |
| `@privy-io/node`      | Privy wallet creation (dynamically imported)                            | \~200KB                      |                                                                                                                    |
| `@gotts.ai/crypto`    | P-256 key generation for CLI pairing protocol                           | \~50KB (via `@noble/curves`) |                                                                                                                    |
| `viem`                | Local key generation, RPC validation                                    | Already a peer dep           |                                                                                                                    |
| `dotenv`              | .env file parsing for existing config detection                         | \~5KB                        |                                                                                                                    |
| `strip-json-comments` | Parse JSONC config files (VS Code format)                               | \~2KB                        |                                                                                                                    |
| `js-yaml`             | Parse YAML config files (Continue.dev format)                           | \~50KB                       |                                                                                                                    |
| `smol-toml`           | Parse TOML config files (Codex CLI format)                              | \~15KB                       |                                                                                                                    |
| `express`             | Browser wizard HTTP server (`--ui` mode)                                | \~200KB                      | Dynamically imported                                                                                               |
| `ws`                  | WebSocket bridge between terminal and browser UI                        | \~50KB                       | Dynamically imported                                                                                               |
| `open`                | Auto-open browser for `--ui` mode                                       | \~10KB                       | Dynamically imported                                                                                               |

The Privy SDK is **dynamically imported** -- it is only loaded when the operator selects Privy as the wallet provider. This keeps the base package small and avoids installing unnecessary dependencies.

`@clack/prompts` is preferred over `@inquirer/prompts` because its `group()` API is designed for multi-step wizard flows with conditional branching. The `group()` call collects all answers at once and each step receives `{ results }` from prior steps:

```typescript
import * as p from "@clack/prompts";

p.intro("Gotts Safe Setup");

const answers = await p.group(
  {
    profile: () =>
      p.select({
        message: "What do you want your agent to do?",
        options: [
          {
            value: "data",
            label: "Read-only analytics",
            hint: "No wallet needed — try it immediately",
          },
          { value: "trader", label: "Trade tokens", hint: "Wallet required" },
          { value: "lp", label: "Manage liquidity", hint: "Wallet required" },
          {
            value: "vault",
            label: "Participate in vaults",
            hint: "Wallet required",
          },
        ],
      }),

    walletProvider: ({ results }) => {
      if (results.profile === "data") return; // Skip wallet for read-only
      return p.select({
        message: "Wallet provider:",
        options: [
          {
            value: "privy",
            label: "Privy",
            hint: "Recommended — TEE-backed, 50K free sigs/month",
          },
          {
            value: "local",
            label: "Local key",
            hint: "Development only — NOT for production",
          },
        ],
      });
    },

    privyAppId: ({ results }) => {
      if (results.walletProvider !== "privy") return;
      return p.text({
        message: "Privy App ID:",
        placeholder: "clx1234567890abcdef",
        validate: (v) => (!v ? "Required" : undefined),
      });
    },

    privyAppSecret: ({ results }) => {
      if (results.walletProvider !== "privy") return;
      return p.password({
        message: "Privy App Secret:",
        validate: (v) => (!v ? "Required" : undefined),
      });
    },

    client: () =>
      p.select({
        message: "Which MCP client?",
        initialValue: detectInstalledClient(), // Auto-detect and pre-select
        options: [
          {
            value: "claude-desktop",
            label: "Claude Desktop",
            hint: detectedHint("claude-desktop"),
          },
          {
            value: "claude-code",
            label: "Claude Code",
            hint: detectedHint("claude-code"),
          },
          { value: "cursor", label: "Cursor", hint: detectedHint("cursor") },
          { value: "vscode", label: "VS Code", hint: detectedHint("vscode") },
          {
            value: "windsurf",
            label: "Windsurf",
            hint: detectedHint("windsurf"),
          },
          {
            value: "other",
            label: "Other / manual",
            hint: "Show config snippet",
          },
        ],
      }),
  },
  {
    onCancel: () => {
      p.cancel("Setup cancelled.");
      process.exit(0);
    },
  },
);
```

### Entry Point

```typescript
#!/usr/bin/env node
import { program } from "commander";
import { runWizard } from "./wizard/quickstart.js";
import { runAdvanced } from "./wizard/advanced.js";
import { detectExistingConfig, resumeFromState } from "./wizard/resume.js";

program
  .name("@gotts.ai/create")
  .description("Set up a Uniswap agent in under 5 minutes")
  .option("--advanced", "Full control over every option")
  .option("--non-interactive", "Suppress prompts, use flags and env vars")
  .option("--reset", "Delete existing config and start fresh")
  .option("--dry-run", "Show what would be done without executing")
  // ... all flags from the Non-Interactive Mode table
  .action(async (options) => {
    const existing = await detectExistingConfig();

    if (existing && !options.reset) {
      const action = await promptResumeAction(existing);
      if (action === "resume") return resumeFromState(existing);
      if (action === "reconfigure")
        return runAdvanced({ defaults: existing.config });
    }

    if (options.advanced) {
      return runAdvanced(options);
    }

    return runWizard(options);
  });

program.parse();
```

### Config File Generation

The wizard writes two files:

**`packages/safe/.env`** (using the `GOTTS_` naming convention from [11-config.md](/docs/gotts-safe-mcp-server/mcp-server/11-config.md)):

```bash
# Generated by @gotts.ai/create on 2026-02-14T10:05:00Z
# Intent: vault-participant | Wallet: privy | RPC: alchemy

GOTTS_PROFILE=vault
GOTTS_TRANSPORT=stdio

GOTTS_PRIVY_APP_ID=clx1234567890abcdef
GOTTS_PRIVY_APP_SECRET=privy-secret-here
GOTTS_PRIVY_WALLET_ID=wlt_abc123xyz

GOTTS_RPC_ETHEREUM=https://eth-mainnet.g.alchemy.com/v2/alchemy-key-here
GOTTS_RPC_BASE=https://base-mainnet.g.alchemy.com/v2/alchemy-key-here
GOTTS_RPC_ARBITRUM=https://arb-mainnet.g.alchemy.com/v2/alchemy-key-here

GOTTS_SAFETY_MAX_TX_USD=1000
GOTTS_SAFETY_MAX_DAILY_USD=5000
GOTTS_SAFETY_TOKEN_ALLOWLIST=strict

# API Keys (three-tier model — see prd/website/portal/03-api-keys.md)
GOTTS_READ_KEY=gotts_read_<generated>
GOTTS_FEEDBACK_KEY=gotts_feedback_<generated>
GOTTS_WRITE_KEY=gotts_write_<generated>
```

**Client config injection**: The wizard detects the installed MCP client and injects config directly into the client's config file. See the [Per-Client Config Formats](#per-client-config-formats) section below for format details.

For remote deployments (Fly.io, Railway), the client config uses HTTP transport:

```json
{
  "mcpServers": {
    "uniswap": {
      "url": "https://uniswap-agent.fly.dev/mcp",
      "headers": {
        "Authorization": "Bearer <generated-token>"
      }
    }
  }
}
```

### `INSTALL.md` Generation

After a successful wizard run, generate an `INSTALL.md` in the working directory that records exactly how the server was configured. This becomes the operator's reference document:

````markdown
# Gotts Safe — Installation Record

Generated: 2026-02-14T10:05:00Z by @gotts.ai/create v1.0.0

## Configuration

- **Profile**: vault (vault operations + read-only data)
- **Chains**: ethereum (1), base (8453), arbitrum (42161)
- **Wallet**: Privy server wallet (0x1a2B...3c4D)
- **Safety**: max $1,000/tx, $5,000/day, strict token allowlist
- **Transport**: stdio (local)

## Client Config

### Cursor / Claude Code

Already written to `.cursor/mcp.json` and `.claude/mcp.json`.

### Claude Desktop

Add this to Settings > Connectors, or to `claude_desktop_config.json`:
\```json
{ "uniswap": { ... } }
\```

## Re-running Setup

\```bash
npx @gotts.ai setup # Resume or reconfigure
npx @gotts.ai setup --reset # Start fresh
\```

## Health Check

\```bash
node packages/safe/dist/index.js --health
\```
````

***

## Per-Client Config Formats

The MCP client ecosystem has **significant format incompatibilities**. The wizard handles these automatically during config injection. This section documents the differences for operators who need to configure clients manually.

### Format Differences Table

| Client                   | Config File (global)                                              | Root Key              | Format   | Notable Quirks                                   |
| ------------------------ | ----------------------------------------------------------------- | --------------------- | -------- | ------------------------------------------------ |
| Claude Desktop (macOS)   | `~/Library/Application Support/Claude/claude_desktop_config.json` | `mcpServers`          | JSON     | Supports `${env:VAR}` interpolation              |
| Claude Desktop (Windows) | `%APPDATA%\Claude\claude_desktop_config.json`                     | `mcpServers`          | JSON     | Same as macOS                                    |
| Claude Code              | `~/.claude.json` or `.mcp.json` (project)                         | `mcpServers`          | JSON     | `${VAR}` env interpolation, `claude mcp add` CLI |
| Cursor                   | `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (project)     | `mcpServers`          | JSON     | Deeplink install supported                       |
| Windsurf                 | `~/.codeium/windsurf/mcp_config.json`                             | `mcpServers`          | JSON     | Uses `serverUrl` not `url` for remote            |
| **VS Code**              | `.vscode/mcp.json` (workspace)                                    | **`servers`**         | JSON     | Different root key, uses `inputs` for secrets    |
| **Zed**                  | `~/.config/zed/settings.json`                                     | **`context_servers`** | JSON     | Nested structure differs                         |
| Continue.dev             | `.continue/config.yaml`                                           | `mcpServers` (list)   | **YAML** | `${{ secrets.VAR }}` syntax                      |
| Cline                    | `cline_mcp_settings.json`                                         | `mcpServers`          | JSON     | `alwaysAllow` array, `timeout` field             |
| OpenAI Codex             | `~/.codex/config.toml`                                            | `[mcp_servers]`       | **TOML** | `bearer_token_env_var` for auth                  |

**Key incompatibilities:**

* VS Code uses `"servers"` (not `"mcpServers"`) as the root key
* Zed uses `"context_servers"` as the root key
* Continue.dev is YAML, not JSON
* Codex CLI is TOML, not JSON
* VS Code uses an `"inputs"` array for user-provided secrets (prompts user at startup)

### Client-Specific Configs

**VS Code** (uses `servers` key + `inputs` for secrets):

```json
{
  "inputs": [
    {
      "type": "promptString",
      "id": "privy-secret",
      "description": "Privy App Secret",
      "password": true
    }
  ],
  "servers": {
    "uniswap": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@gotts.ai/safe@latest"],
      "env": {
        "GOTTS_PROFILE": "trader",
        "GOTTS_PRIVY_APP_SECRET": "${input:privy-secret}"
      }
    }
  }
}
```

**Codex CLI** (TOML format):

```toml
[mcp_servers.uniswap]
command = "npx"
args = ["-y", "@gotts.ai/safe@latest"]

[mcp_servers.uniswap.env]
GOTTS_PROFILE = "trader"
```

**Continue.dev** (YAML format):

```yaml
mcpServers:
  - name: uniswap
    command: npx
    args: ["-y", "@gotts.ai/safe@latest"]
    env:
      GOTTS_PROFILE: trader
```

### Config Injection Implementation

The wizard uses a safe backup → read → parse → merge → write pattern. It never overwrites existing server entries, and always creates a backup before modifying:

```typescript
import {
  readFileSync,
  writeFileSync,
  copyFileSync,
  existsSync,
  mkdirSync,
} from "fs";
import { dirname } from "path";

async function injectConfig(
  client: string,
  serverConfig: object,
): Promise<void> {
  const configPath = getConfigPath(client);

  // 1. Ensure parent directory exists
  mkdirSync(dirname(configPath), { recursive: true });

  // 2. Create backup if config file exists
  if (existsSync(configPath)) {
    copyFileSync(configPath, `${configPath}.bak.${Date.now()}`);
  }

  // 3. Read existing config (handles missing file gracefully)
  let config: Record<string, unknown> = {};
  if (existsSync(configPath)) {
    const raw = readFileSync(configPath, "utf-8");
    config = parseClientConfig(client, raw); // handles JSON/JSONC/YAML/TOML
  }

  // 4. Determine the correct root key for this client
  const rootKey =
    client === "vscode"
      ? "servers"
      : client === "zed"
        ? "context_servers"
        : "mcpServers";

  // 5. Merge without overwriting existing entries
  if (!config[rootKey]) config[rootKey] = {};
  (config[rootKey] as Record<string, unknown>)["uniswap"] = serverConfig;

  // 6. Write back in the correct format
  writeClientConfig(client, configPath, config);
}

function getConfigPath(client: string): string {
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "/";
  const paths: Record<string, string> = {
    "claude-desktop": `${home}/Library/Application Support/Claude/claude_desktop_config.json`,
    "claude-code": `${home}/.claude.json`,
    cursor: `${home}/.cursor/mcp.json`,
    vscode: `.vscode/mcp.json`,
    windsurf: `${home}/.codeium/windsurf/mcp_config.json`,
    cline: `cline_mcp_settings.json`,
    codex: `${home}/.codex/config.toml`,
  };
  return paths[client] ?? `.cursor/mcp.json`;
}
```

***

## Per-Client Install Snippets

After setup, the wizard displays copy-paste config snippets for every client. These are also maintained in a standalone `INSTALL.md` committed to the repository.

### Claude Desktop

```json
{
  "mcpServers": {
    "uniswap": {
      "command": "npx",
      "args": ["-y", "@gotts.ai/safe@latest"],
      "env": { "GOTTS_PROFILE": "data" }
    }
  }
}
```

### Claude Code (one-liner)

```bash
claude mcp add uniswap -- npx -y @gotts.ai/safe@latest
```

### Cursor

```json
{
  "mcpServers": {
    "uniswap": {
      "command": "npx",
      "args": ["-y", "@gotts.ai/safe@latest"],
      "env": { "GOTTS_PROFILE": "data" }
    }
  }
}
```

### One-Click Install (Cursor Deeplink)

```markdown
[![Add to Cursor](https://cursor.sh/deeplink/badge.svg)](cursor://anysphere.cursor-deeplink/mcp/install?name=uniswap&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBhZ2VudGljLXVuaXN3YXAvbWNwLXNlcnZlckBsYXRlc3QiXSwiZW52Ijp7IlVOSVNXQVBfTUNQX1BST0ZJTEUiOiJkYXRhIn19)
```

### One-Click Install (VS Code)

```markdown
[![Install in VS Code](https://img.shields.io/badge/Install%20in-VS%20Code-blue)](vscode:mcp/install?%7B%22name%22%3A%22gotts%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40gotts%2Fsafe%40latest%22%5D%7D)
```

See [16-registries.md](/docs/gotts-safe-mcp-server/mcp-server/16-registries.md) for the full registry submission and one-click install implementation details.

***

## Chat App Setup Protocol

A state machine for conversational setup over messaging platforms (Telegram, Discord, Slack). Each state produces exactly one message and expects one reply. Numbered options provide structured choices without requiring forms or buttons.

### State Machine

| State               | Bot Message                                                                                                           | Expected Reply | Next State                     |
| ------------------- | --------------------------------------------------------------------------------------------------------------------- | -------------- | ------------------------------ |
| **S0: Detect**      | *(internal)* Check env vars for pre-configured credentials                                                            | —              | S1 or S2 (skip if creds found) |
| **S1: Intent**      | "What should your agent do?\n1. Earn yield in vaults\n2. Trade tokens\n3. Manage liquidity\n4. Development / testing" | Number (1-4)   | S2                             |
| **S2: Wallet**      | "Wallet provider:\n1. Privy (recommended)\n2. Local key (dev only)\n3. Skip (zero-dep, read-only)"                    | Number (1-3)   | S3 or S5 (if 3)                |
| **S3: Credentials** | "Enter your Privy credentials.\nApp ID:"                                                                              | Text           | S3b                            |
| **S3b: Secret**     | "App Secret:"                                                                                                         | Text           | S4                             |
| **S4: RPC**         | "RPC provider:\n1. Alchemy (recommended)\n2. Infura\n3. Public RPCs (slower)"                                         | Number (1-3)   | S5                             |
| **S5: Confirm**     | Summary of selections + "Reply 'go' to execute or 'edit' to change"                                                   | "go" or "edit" | S6 or S1                       |
| **S6: Execute**     | "Setting up... \[progress updates]"                                                                                   | —              | S7                             |
| **S7: Done**        | Summary with wallet address, server status, next steps                                                                | —              | End                            |

### Security Notes

* **Never send credentials in chat.** If credentials are not pre-configured as env vars on the bot server, the chat flow should instruct the operator to set them via SSH/env and re-run.
* State S3/S3b should be skipped if credentials are detected in environment, with a message: "Using pre-configured Privy credentials."
* The chat flow produces a `gotts-setup.json` file that can be passed to `npx @gotts.ai setup --from-config gotts-setup.json`.

### Platform Integration Patterns

| Platform     | Entry Point                                  | Message Format                      | Structured Input       |
| ------------ | -------------------------------------------- | ----------------------------------- | ---------------------- |
| **Telegram** | Bot command: `/setup` or mention-based       | Markdown, inline keyboards optional | Reply markup buttons   |
| **Discord**  | Slash command: `/gotts setup`                | Embeds with fields                  | Select menus, buttons  |
| **Slack**    | Slash command: `/gotts-setup` or app mention | Block Kit                           | Interactive components |

All platforms converge to the same state machine. Platform-specific message formatting is handled by adapter modules.

***

## What the Wizard Does NOT Do

The wizard is for initial setup. It does not:

* **Manage ongoing operations** -- use MCP tools and agents for trading, LP, rebalancing
* **Handle credential rotation** -- see [shared/credential-architecture.md](/docs/prd-shared/credential-architecture.md) Section 6
* **Configure guardian protection** -- this is a post-setup security hardening step (see [shared/onboarding-workflow.md](/docs/prd-shared/onboarding-workflow.md) Step 3)
* **Deploy smart contracts** -- vault creation, hook deployment, and proxy contracts are separate workflows
* **Replace the full deployment guide** -- [14-deployment.md](/docs/gotts-safe-mcp-server/mcp-server/14-deployment.md) covers enterprise patterns (OAuth 2.1, Kubernetes, SIEM) that the wizard does not

The wizard gets operators to "first tool call" as fast as possible. Everything beyond that is documented in the referenced PRDs.

***

## Success Metrics

| Metric                                                             | Target       |
| ------------------------------------------------------------------ | ------------ |
| Time from `npx` to first successful tool call (QuickStart)         | < 5 minutes  |
| Time from `npx` to first vault deposit (QuickStart)                | < 10 minutes |
| Prompts in QuickStart mode                                         | <= 7         |
| Wizard completion rate (start to health check pass)                | > 80%        |
| Operators who complete identity registration when prompted         | > 50%        |
| Operators who complete vault deposit when prompted                 | > 30%        |
| Re-run recovery rate (resume succeeds after prior failure)         | > 90%        |
| Non-interactive mode works without modification in CI              | 100%         |
| Time from `npx --zero` to first successful tool call (zero-dep)    | < 30 seconds |
| Browser wizard completion rate (`--ui` start to health check pass) | > 85%        |
| Chat-based setup completion rate (S1 to S7)                        | > 60%        |
| Agent-driven setup success rate (headless / OpenClaw skill)        | > 95%        |
| Zero-dep to full setup conversion (re-run wizard within 7 days)    | > 40%        |

***

## Milestone Placement

The install wizard belongs in **Phase 2 (Trading and Deployment)** of [12-milestones.md](/docs/gotts-safe-mcp-server/mcp-server/12-milestones.md), alongside the existing deployment infrastructure deliverables:

* `scripts/setup-production.ts` (already listed) evolves into the `@gotts.ai/create` package
* The wizard is a Phase 2 deliverable because it depends on the wallet abstraction layer (also Phase 2)
* Identity registration and vault deposit steps depend on Phase 3-4 tools but are optional in the wizard -- the wizard degrades gracefully if those tools are not yet available

***

## Relationship to Existing PRDs

| PRD                                                                         | Relationship                                                                                                                                                                                   |
| --------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [14-deployment.md](/docs/gotts-safe-mcp-server/mcp-server/14-deployment.md) | The wizard automates the "Setup Automation CLI" section. `14-deployment.md` remains the reference for platform comparison, security hardening, and enterprise patterns.                        |
| [shared/onboarding-workflow.md](/docs/prd-shared/onboarding-workflow.md)    | The wizard implements "Step 0: Deploy MCP Server" and parts of "Path A: Automated Onboarding." The onboarding doc remains the reference for guardian setup, migration, and emergency runbooks. |
| [vault/00-quickstart.md](/docs/gotts-vaults/vault/00-quickstart.md)         | The wizard's optional vault deposit step implements Steps 1-4 of the quickstart. The quickstart doc remains the reference for advanced vault operations.                                       |
| [11-config.md](/docs/gotts-safe-mcp-server/mcp-server/11-config.md)         | The wizard generates config files that conform to the schema in `11-config.md`. Advanced mode exposes every option from that schema.                                                           |
| [10-wallets.md](/docs/gotts-safe-mcp-server/mcp-server/10-wallets.md)       | The wizard supports Privy (production) and local key (dev/testing). The wallets doc remains the reference for Lit Protocol, Safe, ZeroDev, and custom viem accounts.                           |

***
