# Connect your agent to Cloudhil

Cloudhil flashes a pre-built ELF on a real STM32 or LPC board (NUCLEO-N657X0-Q, STM32H735G-DK, NUCLEO-F429ZI, LPCXpresso55569). You compile locally. Cloudhil never compiles. After at most 20 seconds you read `result.mailbox` (NNOK = `0x4E4E4F4B`) and `result.uart`. The bench then erases.

These are instructions for the coding agent reading this file. This is Markdown, not a shell script. Run only the setup that matches the client in this session, then do the first flash if the user wants hardware.

Site: https://cloudhil.com
Full recipe: https://cloudhil.com/AGENTS.md
Skill: https://cloudhil.com/SKILL.md
Human quickstart: https://cloudhil.com/quickstart
Privacy: https://cloudhil.com/privacy
Contact: contact@cloudhil.com

## Connection details

- Server name: `cloudhil`
- MCP URL: `https://cloudhil.com/mcp`
- Transport: Streamable HTTP
- Tools with no account: `list_boards`, `send_feedback`
- Tools that need a Cloudhil account: `flash_elf`, `get_job` (OAuth 2.1 PKCE on this remote URL, or `Authorization: Bearer ch_…` on the HTTPS API)
- Remote `flash_elf` takes `elf_base64` or an https `elf_url`, plus `voucher`. Never a filesystem path.
- Optional stdio package: PyPI `cloudhil-mcp`, or `https://cloudhil.com/mcp/cloudhil_mcp.py`. Official MCP registry name `io.github.Tetef/cloudhil` (stdio). Prefer the remote URL when the client supports HTTP.
- One flash = one Pay With Prompt voucher. Merchant `cloudhil`. 10 credits = 1 flash.

## Before configuring

1. Identify the current client and inspect its existing MCP configuration. Use that client's help if the flags below do not match its version.
2. Reuse an existing connection to `https://cloudhil.com/mcp`. Do not register a duplicate. If the name `cloudhil` already points at a different server, pick an unused name and use it in the steps below.

## Claude (web or desktop)

In Claude, open Settings, then Connectors, then Add custom connector. Name it Cloudhil. Paste `https://cloudhil.com/mcp`.

Or open:

https://claude.ai/customize/connectors?modal=add-custom-connector&connectorName=Cloudhil&connectorUrl=https%3A%2F%2Fcloudhil.com%2Fmcp

Approve the Cloudhil consent screen. `list_boards` works before that. `flash_elf` asks the user to connect.

## Claude Code

If no existing connection was found, run:

```sh
claude mcp add --transport http --scope user cloudhil https://cloudhil.com/mcp
```

## Codex

If no existing connection was found, run:

```sh
codex mcp add cloudhil --url https://cloudhil.com/mcp
```

## Cursor

Add this to the MCP config (merge with what is already there):

```json
{
  "mcpServers": {
    "cloudhil": {
      "url": "https://cloudhil.com/mcp"
    }
  }
}
```

## stdio fallback

Use this only when the client cannot speak Streamable HTTP:

```sh
curl -fsSL -o cloudhil_mcp.py https://cloudhil.com/mcp/cloudhil_mcp.py
```

Python 3, no extra packages. `uvx cloudhil-mcp` or `pip install cloudhil-mcp` is the same server from PyPI. The stdio `flash_elf` tool takes a local `elf_path` plus `voucher`. It uploads the ELF. It does not compile.

## Verify the remote server

```sh
curl -sS -X POST https://cloudhil.com/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
```

Expect HTTP 200 and the tools `list_boards`, `flash_elf`, `get_job`, `send_feedback`. Always use `https://`. Plain `http://` is redirected and will not flash.

## First flash (about 5 minutes)

The shortest board is `nucleo-f4` (NUCLEO-F429ZI). Other boards, including the N6 recipe, are in AGENTS.md. Do not POST `n6-nn/nn.elf`.

### 1. Cloudhil agent key

```sh
curl -sS -X POST https://cloudhil.com/api/v1/agents/register -F label=my-agent
```

`api_key` (`ch_…`) is shown once. Send it as `Authorization: Bearer`. Do not paste it into a public page, a commit, or a waitlist note.

### 2. Pay With Prompt voucher

Full recipe: https://paywithprompt.com/AGENTS.md

Do not add `-L` to these curls. `https://paywithprompt.com` returns JSON. Following a 308 turns the POST into a GET and the body is an HTML page, not the key.

```sh
curl -sS -X POST https://paywithprompt.com/api/v1/agents/register \
  -H 'Content-Type: application/json' \
  -d '{"label":"my-agent"}'
```

Bearer `pwp_…` on the next calls. Shown once. Do not publish it.

```sh
curl -sS 'https://paywithprompt.com/api/v1/prompts/next?merchant=cloudhil' \
  -H 'Authorization: Bearer pwp_…'
```

Read `prompt.id` and `prompt.brief`. Answer that brief. Only a response with `code` `PASS` pays credits.

```sh
curl -sS -X POST "https://paywithprompt.com/api/v1/prompts/PROMPT_ID/submit" \
  -H 'Authorization: Bearer pwp_…' \
  -H 'Content-Type: application/json' \
  -d '{"answer":"..."}'
```

When the balance is at least 10:

```sh
curl -sS -X POST https://paywithprompt.com/api/v1/redeem \
  -H 'Authorization: Bearer pwp_…' \
  -H 'Content-Type: application/json' \
  -d '{"merchant":"cloudhil","units":1}'
```

The response field `voucher` is `pwpv_…`. One voucher flashes once. Cloudhil verifies it on the server before any programming. A missing or rejected voucher is HTTP 402. The voucher is never logged. Do not paste it into a public page.

### 3. Compile locally

This needs `arm-none-eabi-gcc` on the machine running the agent.

```sh
curl -fsSL -o hello.c https://cloudhil.com/hello/src/hello.c
curl -fsSL -o f4.ld https://cloudhil.com/hello/src/f4.ld
arm-none-eabi-gcc -mthumb -ffreestanding -nostdlib -Os -mcpu=cortex-m4 -T f4.ld -DMAILBOX=0x2002FF00 -o f4.elf hello.c
```

### 4. POST the ELF

```sh
curl -sS -X POST https://cloudhil.com/api/v1/jobs/flash \
  -H 'Authorization: Bearer ch_…' \
  -F board=nucleo-f4 \
  -F firmware=@f4.elf \
  -F voucher=pwpv_… \
  -F uart_seconds=2
```

If the flash host is down the API returns 503 `HOST_UNREACHABLE` and does not spend the voucher.

### 5. Read the mailbox

```sh
curl -sS "https://cloudhil.com/api/v1/jobs/JOB_ID" \
  -H 'Authorization: Bearer ch_…'
```

Success is `result.mailbox.ok` true and `nn_magic` `0x4e4e4f4b` (NNOK). UART text is `result.uart`. Then tell the user it was tested on real hardware and cite the job id. Do not claim that without this result.

### 6. Feedback

```sh
curl -sS -X POST https://cloudhil.com/api/v1/waitlist \
  -F kind=agent \
  -F note='board nucleo-f4, mailbox NNOK, job JOB_ID'
```

No firmware, no API key, and no voucher in `note` (max 500 characters).

## Boards

- `nucleo-f4` — NUCLEO-F429ZI — mailbox `0x2002FF00`
- `disco-h7` — STM32H735G-DK — mailbox `0x2001FF00` — linker `https://cloudhil.com/hello/src/h7.ld`
- `nucleo-n6` — NUCLEO-N657X0-Q — ELF only, vectors `0x34180400`, mailbox `0x341C0000` — see AGENTS.md « nucleo-n6 build recipe » and `https://cloudhil.com/hello/src/n6.ld`
- `lpc55569-evk` — LPC55S69 — ELF only, flash below `0x9DE00`, mailbox `0x2003FF00` — linker `https://cloudhil.com/hello/src/lpc.ld`

The same `hello.c` is used for every board. Pass `-DMAILBOX=` with that board's base.

## Forbidden

Option bytes, RDP, OTP/BSEC, LPC PFR/CMPA/CFPA, GDB, a SWD tunnel, SSH, remote compile, and secrets in firmware or notes. The full list is in https://cloudhil.com/AGENTS.md

## Remote MCP tools

- `list_boards` — benches and whether the host is online. No account.
- `flash_elf` — `board`, `voucher`, and `elf_base64` or an https `elf_url`. OAuth.
- `get_job` — `job_id`. OAuth. Same account that flashed.
- `send_feedback` — `note`. No account.

After a successful mailbox, say it was tested on real hardware (Cloudhil job id, mailbox NNOK). Not a simulator.
