# The GHAN MCP server

> GHAN ships every write capability as an MCP tool before it ships anywhere else. A founder's coding agent can register an app, read a machine-readable score, integrate the SDK, fund a wallet, run a campaign and pull the raw signed ledger without a human opening a browser.

Source: https://ghan.io/docs/mcp/  
Published: 2026-09-22 · Updated: 2026-09-22  
Publisher: GHAN — the audited cross-promotion network for desktop apps (https://ghan.io)

---

## Auth

`register_app` returns an API key. Send it as `Authorization: Bearer <key>`. Read tools that describe the network rather than an account need no key at all.

## Tools

### `register_app`

```
register_app {
  name: string,
  platform: "mac" | "win",
  bundle_id: string,
  domain: string,
  download_url: string,
  contact_email: string
} -> { app_id, api_key, status: "pending", scoring_job_id }
```

Queues the [vetting gate](/vetting/). Status is `pending` until the gate completes.

### `get_score`

```
get_score { app_id } -> {
  status: "pending" | "approved" | "throttled" | "live" | "suspended" | "rejected",
  gate: { signed: bool, malware_clean: bool, domain_age_ok: bool,
          contact_verified: bool, bundle_match: bool },
  reputation: { total, ai_visibility, cadence, reviews, coherence },
  behavior: { retention_48h, uninstall_rate, canary, complaints } | null,
  next_review_at,
  reasons: [ code ]
}
```

Rejection reasons are stable codes. An agent can act on them without parsing English.

### `get_integration_guide`

```
get_integration_guide {} -> markdown
```

Returns the [SDK quickstart](/docs/sdk/) verbatim. Public. This exists so an agent reads the guide from the source of truth rather than scraping a website.

### `top_up`

```
top_up { app_id, amount_cents } -> { checkout_url } | HTTP 402
```

If the caller can pay machine-to-machine, the endpoint answers **HTTP 402** with an x402 challenge settled in USDC on Base. Otherwise it returns a Stripe Checkout URL that bundles payment, the terms click-wrap, and identity verification where required.

### `create_campaign`

```
create_campaign { app_id, categories: [string], daily_budget_cents, max_bid_cents? }
  -> { campaign_id, floor_cents, max_bid_cents, status: "throttled" }
```

`floor_cents` comes from the [rate card](/pricing/) and is always available to you. `max_bid_cents` is optional: omit it and the campaign buys at floor; set it at or above the floor and the campaign competes for priority in contested inventory at **second price**, paying one cent above the next-highest competing bid. See [bidding](/bidding/).

### `pause_campaign` / `resume_campaign`

```
pause_campaign  { campaign_id } -> { status }
resume_campaign { campaign_id } -> { status }
```

### `get_ledger`

```
get_ledger { app_id, from, to } -> { lines: [ {
  id, ts, type, amount_cents, counterparty_app_id,
  event_chain_id, balance_after, note
} ] }
```

The audit-me tool. Every line carries an event-chain reference you can resolve and verify against your own signature. Types are `topup`, `earn`, `charge`, `escrow_hold`, `escrow_release`, `clawback`, `payout`, `adjustment`.

### `get_stats`

```
get_stats { app_id, period } -> {
  impressions, clicks, installs, cleared, retention_48h,
  floor_cents, avg_clearing_price_cents,
  fraud_rejected: { [reason_code]: count }
}
```

Rejections come back with reason codes. Transparency applies to the installs you were not paid for.

### `get_rate_card`

```
get_rate_card {} -> { categories: [ { key, label, floor_cents_min, floor_cents_max } ],
                      take_rate, credit_bonus, reserved_floor_share }
```

Public, unauthenticated.

### `get_network_stats`

```
get_network_stats {} -> { avg_cpi_by_category, clearing_rate, fraud_pct, period }
```

Public, unauthenticated. The published [transparency](/transparency/) numbers.

### `request_payout`

```
request_payout { app_id } -> { transfer_id, amount_cents } | { error: "below_minimum" }
```

Stripe Connect transfer, minimum EUR 50.

## A whole onboarding, as an agent sees it

```
1. get_rate_card {}                     # is this worth doing
2. register_app { ... }                 # -> app_id, api_key
3. get_score { app_id }                 # -> gate failures as codes, or approved
4. get_integration_guide {}             # -> wire the SDK, open a PR
   ── human ships the release ──
5. top_up { app_id, amount_cents }      # -> 402, or a checkout URL for a human
6. create_campaign { ... }              # -> throttled, ramps as chains clear
7. get_stats / get_ledger               # -> verify everything above
```

Two of those steps involve a person. The rest is agent to agent, which is the whole design.

## Questions people ask about this

### What is the MCP endpoint?

The GHAN MCP server speaks both stdio and streamable HTTP. The HTTP endpoint and its transport details are published in the server card at /mcp/server-card.json, which is discovered through the AI catalog at /.well-known/ai-catalog.json rather than from a well-known path, per the MCP specification.

### Which tools need authentication?

Read tools that describe the network rather than an account are public - get_rate_card, get_network_stats and get_integration_guide. Everything scoped to an app requires the API key issued by register_app, sent as a bearer token.

### Can an agent spend money without a human?

Within a ceiling a human set once. GHAN deliberately keeps two moments human - shipping the release that contains the SDK, and authorising money. The second can be satisfied ahead of time with a pre-capped agent wallet or an x402 machine payment, so an agent can top up and spend up to a limit without a click each time, but the limit itself is set by a person.

### Where is the server card?

At /mcp/server-card.json, next to the MCP endpoint, and referenced from /.well-known/ai-catalog.json. It is deliberately not at a /.well-known/mcp/ path, because the MCP specification rules that location out.

