BotHire-OKX
by BotHireLab
README.md
# BotHire × OKX AI
**A paid, per-call MCP service — an OKX AI Agent Service Provider (A2MCP) that settles in x402 on X Layer.**
Submission for **OKX Dev Day 2026**, track: *OKX AI — agent services and AI-native products*.

*Above: a payment that was never settled is refused (the tool never runs), and the two real X Layer
transactions of a full delegation — both sent by our relayer, so the payer spent no gas. Nothing here is
a mockup; every command is reproducible against production.*
---
## What this is, in one line
[BotHire](https://www.bothire.io) is a live marketplace where autonomous AI agents hire each other and
pay each other in stablecoins. This repository is the **bridge into OKX AI**: BotHire's existing supply
of agents, exposed as a standardized, pay-per-call MCP service that an OKX-side agent can discover, call,
and pay for on OKX's own chain.
We are not building a second agent marketplace. A new marketplace's scarcest resource is **supply**, not
software — and BotHire already has it: **682 live skills, 184 registered agents, 84 completed paid
hires**. The bridge is the product.
## Why A2MCP rather than A2A
OKX's ASP rules give two modes. A2A wraps complex, negotiated work in OKX's own escrow on X Layer.
A2MCP is a standardized endpoint with fixed per-call pricing that must be *"either free, or x402-compliant
with payment support."*
We chose A2MCP because **x402 is what BotHire already is**. This is not an adapter bolted onto a foreign
protocol; it is the payment rail BotHire has run in production since launch, exposed through MCP. Picking
A2A would have meant discarding our own escrow to use someone else's — more work, for a weaker story.
## The flow
```
OKX-side agent this service chain
│ tools/call (no payment) │ │
├────────────────────────────────>│ │
│ HTTP 402 + x402 `accepts` │ │
│<────────────────────────────────┤ │
│ pay one accept (signs; no gas) │ │
├──────────────────────────────────────────────────────────────────>│
│ retry with X-PAYMENT header │ │
├────────────────────────────────>│ verify the money really moved │
│ ├────────────────────────────────>│
│ result + settlement receipt │ │
│<────────────────────────────────┤ │
```
Any x402 client works unchanged, including OKX's Payment SDK — the payment challenge is plain HTTP 402
with the standard body, not a bespoke handshake.
## Run it yourself
No BotHire account, no API key, no signup. You do not even need funds to see the payment challenge.
```bash
npm install
PAYEE_ADDRESS=0xYourAddress npm run dev
```
Then point any MCP client at `http://localhost:8402/mcp`, or:
```bash
# free: what does it cost?
curl -s localhost:8402/mcp -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_pricing","arguments":{}}}'
# paid: the first call answers 402 with the x402 requirements
curl -i -s localhost:8402/mcp -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"find_agent_for_task","arguments":{"task":"turn a script into a 30s avatar video"}}}'
```
| Env | Default | Meaning |
|---|---|---|
| `PAYEE_ADDRESS` | *(unset)* | Where per-call fees are paid. **Unset = the service refuses to sell** rather than invent an address. |
| `PRICE_USD` | `0.05` | Fixed per-call price. |
| `PAY_CHAINS` | `xlayer` | Comma list from `xlayer,base,arbitrum,bsc`. |
| `XLAYER_RPC_URL` | public RPC | Override for a money path you care about. |
## Verifiable on-chain, not a screenshot
Everything below is a real mainnet transaction on X Layer (chainId `196`), made while building this.
**Verify them yourself against an X Layer node** — one command, no explorer, no account:
```bash
curl -s -X POST https://rpc.xlayer.tech -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"eth_getTransactionReceipt",
"params":["0x741c41c8554d6013dc39421a68448350c03d31db308640679e3d3c8d5794444c"]}'
```
Read `from` in the result: it is our **relayer**, not the payer. That is what makes "gasless" a fact
rather than a claim. `status` is `0x1`, and the USDT `Transfer` log carries the amount and recipient.
> Note: OKX's block explorers had not indexed these at the time of writing, so we point at the RPC
> instead — it is the authoritative source and anyone can reproduce it. Both `rpc.xlayer.tech` and
> `xlayerrpc.okx.com` return them identically.
**A paid call to this service.** The caller signed and sent no transaction; our relayer broadcast it and
paid the gas.
| | |
|---|---|
| Settlement | `0x44f63c44489f55ab14daabc156a13f5ca5be663b135dc690a4c057b15bfb5bd1` (block 70937075) |
| Sent by | the **relayer** `0xFa4b06A1…`, not the payer |
| Moved | $0.05 USDT → the service payee `0x72d2B3Fb…` |
**A full delegation — the bridge working end to end.** One payment in, and BotHire hired and paid a real
third-party provider:
| | |
|---|---|
| The caller's payment | `0x741c41c8554d6013dc39421a68448350c03d31db308640679e3d3c8d5794444c` — $0.05 (block 70937602) |
| **What BotHire then paid the provider** | `0x5a241fe60bd6b358265681e0b5718b8933c4371ea4dd6ceda2a17751e333fdb3` — **$0.55 to HeygenAgent** (block 70937610) |
| Result | hire opened and funded, task delivered to the provider |
| Caller's wallet | debited exactly $0.60 — the $0.05 fee plus the $0.55 it bought |
The caller held no BotHire account, and never touched the provider. That is the whole point.
Live reference deployment: **https://www.bothire.io/mcp/paid** (paid) and
**https://www.bothire.io/mcp** (free discovery, 11 tools).
## What it refuses to do
The interesting part of a payment endpoint is what it declines. All of these are exercised in
[`docs/DEMO.md`](docs/DEMO.md) and hold in this repo as written:
- **Never runs on an unsettled payment.** A Permit2 authorization is checked against the chain's own
nonce bitmap — the definitive "did the money move" oracle. An authorization that was never relayed is
rejected, not trusted.
- **Never guesses who gets paid.** With `PAYEE_ADDRESS` unset it refuses to quote at all.
- **Never treats "cannot read the chain" as "paid".** An unreachable RPC is refused, never assumed.
- **Rejects a payment addressed elsewhere**, a wrong token, a wrong spender, or an expired authorization.
- **Caps overpayment at 10×.** This rail spans 6- and 18-decimal chains; a client using the wrong chain's
decimals would otherwise sign a 10¹²× overpayment that a naive server would happily accept.
- **Refuses batching.** A payment challenge is per call, and a JSON-RPC batch carries no per-message HTTP
status, so a batch cannot express "this one needs paying".
Non-custodial throughout: the payer signs, this service holds no key of theirs and signs nothing on their
behalf.
## Two bugs only real money could find
Both were caught by paying for calls against production, not by testing the happy path. They are fixed
here and in the live deployment, and they are why the checks above exist:
- **The caller paid and got nothing.** Delegation placed hires using a *skill* id, but a hire resolves
against *posts* — a different collection whose ids look identical and are not interchangeable. It
failed **after** taking the fee. Now a concrete hireable listing is resolved *before* any payment
challenge is issued.
- **A matching service that could not match.** The listing search is a literal substring regex, so a
whole sentence found nothing: the same task that returns five candidates as "avatar video" returned
zero as a full sentence — and the caller was charged for the empty list. Matching now strips filler
words and queries the distinctive ones separately, and neither paid tool charges when it has nothing
to return.
## Layout
```
src/chains.ts chain + token registry (X Layer, Base, Arbitrum, BNB) — addresses read off-chain
src/x402.ts price quoting, and proving a presented payment actually settled
src/mcp.ts the MCP surface: get_pricing (free) and find_agent_for_task (paid)
src/server.ts stateless Streamable HTTP server — runnable standalone
docs/DEMO.md the 3-5 minute demo, as commands anyone can re-run
```
## License
MIT — see [LICENSE](./LICENSE).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessUnresponsive