# MPP Payments

Pay for each model and MCP request through hooks that the SDK and the nanocode CLI expose, as [nanocode-machines](https://github.com/gakonst/nanocode-machines) does with Machine Payments Protocol (MPP) sessions.

## Overview

nanocode has no payment code of its own. It exposes hooks that let a host own each request: an HTTP layer around every model request, a host transport for Codex, and a payment provider for MCP tool calls. A paying host implements one of these hooks.

nanocode-machines is a separate project that uses these hooks. It runs the nanocode CLI with every model, tool, and MCP request paid from a Tempo wallet over MPP sessions, without a subscription login.

> [!NOTE]
> nanocode-machines lives in [gakonst/nanocode-machines](https://github.com/gakonst/nanocode-machines), outside the nanocode repository. Statements on this page about its payment flow, wallet, and costs come from that project, not from the nanocode sources.

```rust
use nanocode_cli::ModelClients;

// Every Codex and Claude request passes through the same HTTP layer
let clients = ModelClients::new()
    .openai_endpoint("https://llm.example.com/v1", layer.clone())
    .claude_endpoint("https://llm.example.com/v1/messages", layer);
nanocode_cli::run_with(clients)
```

## Walkthrough

The steps below are Rust only, because `nanocode-cli` is a Rust library. They build a CLI binary that starts the nanocode interface with both model clients behind a layer. This template adds a bearer token for a proxy that you run, and a paying layer runs its payment flow in the same place.

### Add the Crates

`nanocode-cli` is the library behind `nc`. `http` provides the header types that the layer uses.

```toml title="Cargo.toml"
[dependencies]
# The SDK facade, the CLI library, and HTTP header types
http = "1"
nanocode = { git = "https://github.com/gakonst/nanocode" }
nanocode-cli = { git = "https://github.com/gakonst/nanocode" }
```

### Write the Layer

A `nanocode::http::Layer` receives each request with a `Next` that sends it on. nanocode still builds the headers, maps errors, and decodes the stream. The layer changes only the request and its exchange.

```rust title="src/main.rs"
use std::process::ExitCode;

use http::{HeaderValue, header::AUTHORIZATION};
use nanocode::http::{BoxFuture, Error, Layer, Next, Request, Response};
use nanocode_cli::ModelClients;

#[derive(Clone)]
struct Bearer(HeaderValue);

impl Layer for Bearer {
    // Add the proxy token, then send the request on
    fn send<'a>(
        &'a self,
        mut request: Request,
        next: Next<'a>,
    ) -> BoxFuture<'a, Result<Response, Error>> {
        request.headers.insert(AUTHORIZATION, self.0.clone());
        next.send(request)
    }
}
```

### Start the CLI

`ModelClients::openai_endpoint` and `claude_endpoint` point the Codex and Claude clients at new endpoints through the layer. `run_with` starts the nanocode interface with those clients.

```rust title="src/main.rs"
fn main() -> ExitCode {
    // 1. Read the proxy token into a header value
    let token = std::env::var("PROXY_TOKEN").unwrap_or_default();
    let Ok(value) = HeaderValue::from_str(&format!("Bearer {token}"))
    else {
        eprintln!("PROXY_TOKEN is not a valid header value");
        return ExitCode::FAILURE;
    };
    let bearer = Bearer(value);

    // 2. Send both providers through the layer, and show the route in
    // the footer
    let clients = ModelClients::new()
        .openai_endpoint("https://llm.example.com/v1", bearer.clone())
        .claude_endpoint("https://llm.example.com/v1/messages", bearer)
        .status(|| Some("via llm.example.com".into()));
    nanocode_cli::run_with(clients)
}
```

`openai_endpoint` uses `OpenAiAuth::gateway_managed()` over HTTPS, so the client sends no OpenAI token of its own. The layer, or the proxy behind it, supplies authorization.

## Recipes

### Layer the SDK

Without the CLI, set the layer on each client. `OpenAiBuilder::http_layer` also carries the requests of the standalone web search and image generation tools, so the layer covers every OpenAI endpoint that the agent reaches.

In TypeScript, `dispatch` is the layer. It sends every Claude request and every Codex HTTPS request, and its `fetch` call is where a paying host runs its payment flow. A Codex subscription session sends over `socket.open` instead.

#### Rust

```rust
use nanocode::codex::auth::OpenAiAuth;
use nanocode::{ClaudeClient, Nanocode, OpenAi};

// Both clients send every request through the same layer, with no
// credentials of their own
let openai = OpenAi::builder(OpenAiAuth::gateway_managed())
    .api_base_url("https://llm.example.com/v1")
    .http_layer(layer.clone())
    .build()?;
let claude = ClaudeClient::with_auth_headers(
    Default::default(),
    "https://llm.example.com/v1/messages",
    Default::default(),
)
.with_http_layer(layer);
let runtime = Nanocode::builder()
    .openai(openai)
    .claude(claude)
    .build_async()
    .await?;
```

#### TypeScript

```ts
// Every model request reaches the proxy with the host's token, and the
// SDK sends no credentials
await runtime.open(
  {
    ...config,
    endpoint: 'https://llm.example.com/v1',
    provider: 'codex',
    subscription: false,
  },
  {
    dispatch: (request, signal) =>
      fetch(request.endpoint, {
        body: request.body as Uint8Array<ArrayBuffer>,
        headers: [
          ...request.headers,
          ['authorization', `Bearer ${process.env.PROXY_TOKEN}`],
        ],
        method: 'POST',
        redirect: 'error',
        signal,
      }),
  },
)
```

`http_layer` is native only, and it switches the Codex client to HTTPS without WebSocket warmup.

### Own the Transport

This recipe is Rust only. In TypeScript, `dispatch` already owns the transport. `OpenAiBuilder::host_transport` sends every Codex Responses request through a `HostTransport` that the host implements, such as a private egress. The host forwards requests that the client already built, with their headers.

```rust
use nanocode::OpenAi;
use nanocode::codex::auth::OpenAiAuth;

// The transport injects credentials, so the client sends no token
let openai = OpenAi::builder(OpenAiAuth::gateway_managed())
    .host_transport(transport)
    .build()?;
```

`OpenAiAuth::host_managed_chatgpt()` is the ChatGPT-mode equivalent, for transports that inject the subscription credential and account headers. Native host transports are HTTPS only and cannot combine with `ChatGateway`.

### Pay MCP Calls

This recipe is Rust only, because nanocode-wasm has no MCP client. `McpServer::payment_provider` attaches an `McpPaymentProvider` to one server. When a call fails with JSON-RPC error `-32042`, or returns `org.paymentauth/payment-required` in its `_meta`, the SDK asks the provider to `prepare` a payment. The trait uses `async-trait`, so add `async-trait = "0.1"` to `Cargo.toml`.

```rust
use std::sync::Arc;

use nanocode::tools::mcp::{
    McpPaymentProvider, McpPendingPayment, McpServer,
};
use serde_json::Value;

struct Wallet;

#[async_trait::async_trait]
impl McpPaymentProvider for Wallet {
    // Return None to decline, or a pending payment with a credential
    async fn prepare(
        &self,
        payment_required: &Value,
    ) -> Result<Option<Box<dyn McpPendingPayment>>, String> {
        let _ = payment_required;
        Ok(None)
    }
}

let server = McpServer::http("https://mcp.example.com/mcp")
    .payment_provider(Arc::new(Wallet));
```

The SDK retries the call once with the pending payment's `credential()` under `org.paymentauth/credential` in the request `_meta`. A success passes any `org.paymentauth/receipt` to `receipt` and calls `commit`. A second payment request or an RPC error calls `rollback`.

### Run nanocode-machines

`nanocode-mpp` from nanocode-machines opens the nanocode interface with payments on. These commands and their behavior come from that project.

```bash
# Install the paid CLI, sign in to a Tempo wallet, and start it
cargo install --locked --git https://github.com/gakonst/nanocode-machines --bin nanocode-mpp
tempo wallet login
nanocode-mpp
```

According to nanocode-machines, `/spend` shows what each service cost, and `nanocode-mpp exec "..."` prints the spend after the turn. On 2026-10-20, nanocode-machines recorded `exec "Reply with exactly: ok"` at 0.001743 MACH on Haiku 5.5 and 0.069736 MACH on Opus 5.5.

## More

- [Model Gateways](/docs/guides/gateways): Route Codex through third-party Responses gateways.

- [Tools](/docs/guides/tools): Connect MCP servers to the runtime.

- [OpenAi](/docs/api/rust/OpenAi): Reference for http_layer and host_transport.

- [ClaudeClient](/docs/api/rust/ClaudeClient): Reference for the Claude client and its layer.

- [Headless Runs](/docs/guides/exec): Run one turn from the command line.
