Agents & payments

My portfolio is an MCP server that takes payments

How antojoel.com serves AI agents over MCP, settles USDC over x402 only when a message is delivered, and why the paid tool costs 99 cents.

By Anto Joel · 2026-10-14 · 8 min read

Recruiters already paste job descriptions into ChatGPT and ask "is this person a fit?". The model answers from whatever it can scrape: a stale LinkedIn, a cached resume, sometimes a different person with the same name.

So I stopped hoping the scrape goes well. My portfolio now is the data source. Any agent can connect to antojoel.com/mcp, ask structured questions, get evidence-backed answers, and — if it carries a wallet — pay me in USDC to skip my inbox queue.

This post is how that works, what broke, and why the paid part costs 99 cents and not two dollars.

7

MCP tools

6

Free, no auth

1

Paid via x402

The shape of it

The site is a normal React app on Vercel. The interesting part is one serverless function that speaks the Model Context Protocol, plus the services it talks to when money is involved.

antojoel.com as an MCP serverAI agent (Claude · ChatGPT · Cursor), x402 agent (with USDC wallet), Browser (/agents console), /mcp (MCP server), Static site (prerendered HTML), Facilitator (Coinbase CDP), Base (USDC settles here), Resend (email delivery), Supabase (payment log). agent to mcp; wallet to mcp; browser to static; mcp to cdp; cdp to base: settle; mcp to resend; mcp to dbClientsVercelServicessettleAI agentClaude · ChatGPT · Cursorx402 agentwith USDC walletBrowser/agents console/mcpMCP serverStatic siteprerendered HTMLFacilitatorCoinbase CDPBaseUSDC settles hereResendemail deliverySupabasepayment log
One Vercel function serves people's agents; payments settle on Base

Three decisions shaped everything else:

  1. One source of truth. Everything factual about me lives in a single typed data file. The website, the MCP tools, the static HTML that crawlers read, and llms.txt are all generated from it, so they cannot disagree with each other.
  2. Stateless on serverless. Each request gets a fresh MCP server and transport. No sessions, no sticky connections, nothing to clean up.
  3. Free by default. The tools a recruiter needs are free and need no key. Money only enters for one optional action.

What an agent can ask

Tools exposed at antojoel.com/mcpantojoel.com/mcp: Who (get_profile, get_journey); Proof (list_case_studies, get_skills_with_evidence); Hiring (evaluate_role_fit, book_call); Paid (priority_intro · USDC)get_profileget_journeyWholist_case_studiesget_skills_with_evidenceProofevaluate_role_fitbook_callHiringpriority_intro · USDCPaidantojoel.com/mcp
Six free tools, one paid

The tool I care most about is evaluate_role_fit. It doesn't return a verdict — it returns my full record plus a rubric, and lets the caller's model do the judging:

For each core requirement, mark it STRONG (direct evidence),
PARTIAL (adjacent evidence) or GAP (no evidence).
Cite the specific venture, skill or stat for every claim.
A skill not in the record has no evidence; do not infer one.
State gaps plainly; do not soften them into strengths.

Stateless MCP in one function

The MCP TypeScript SDK ships a web-standard transport, which maps cleanly onto a Vercel function that takes a Request and returns a Response:

const transport = new WebStandardStreamableHTTPServerTransport({
  sessionIdGenerator: undefined, // stateless: no sessions to track
  enableJsonResponse: true,      // plain JSON back, no SSE stream
});
const server = await createServer();
await server.connect(transport);
return withCors(await transport.handleRequest(request));

Around that sit the boring guards that matter in public: bodies over 64 KB get a 413, GET gets a 405 because a stateless server has no stream to offer, and every tool input is validated before anything runs.

Getting paid: x402

x402 revives HTTP's long-unused 402 Payment Required. For MCP, the flow is: call the tool, receive a price, sign a USDC authorization, call again with the signature attached.

A paid priority_intro call1. Agent to /mcp: call priority_intro 2. /mcp to Agent: 402: 0.99 USDC on Base 3. agent signs a USDC transfer authorization 4. Agent to /mcp: retry with payment 5. /mcp to Facilitator: verify 6. /mcp to Email: deliver message 7. /mcp to Facilitator: settle 8. delivery failed: nothing settles, no charge 9. /mcp to Agent: result + receiptAgent/mcpFacilitatorEmailcall priority_intro402: 0.99 USDC on Baseagent signs a USDC transfer authorizationretry with paymentverifydeliver messagesettledelivery failed: nothing settles, no chargeresult + receipt
Payment only settles after the message is delivered

The ordering is the whole design. The payment is verified before the tool runs and settled after it succeeds. If the email fails, the tool returns an error, and nothing moves:

paid(async ({ name, email, message }) => {
  try {
    await sendEmail({ /* … */ });
    return json({ delivered: true });
  } catch {
    // isError → the payment is never settled, so the sender isn't charged
    return { ...json({ delivered: false }), isError: true };
  }
});

I tested that exact path on Base Sepolia with a deliberately broken email key: the agent attached a payment, delivery failed, and both wallet balances stayed exactly where they were.

Why 99 cents

Price vs the default x402 client spend cap

Default per-payment cap in x402 clients~1 USD
priority_intro (target price)~0.99 USD

Pays without the agent's owner changing any settings

The $2 I first wanted~2 USD

Blocked by default — needs a human to raise the cap

The official x402 client refuses any single payment above $1 unless its owner raises the limit. At $2, almost every agent would stop and ask a human. At $0.99, it just pays. It's live at a single cent for now, while I run the first real-money test.

The bugs worth knowing about

Making it findable

An endpoint nobody knows about earns nothing, so the same data feeds several discovery paths:

  • The official MCP Registry lists it as com.antojoel/portfolio, proven by DNS. Directories like Smithery and Glama read from there.
  • x402 discovery metadata rides along with every payment request, so payment-aware catalogues can index the paid tool.
  • /llms.txt gives language models a plain-text map of who I am, what the tools do, and where the evidence lives.
  • Prerendered HTML and JSON-LD keep normal search engines happy.

Try it

Point any MCP client at https://www.antojoel.com/mcp — no key needed — or open /agents and watch the raw JSON-RPC in the live console. Ask it whether I fit your role. It's built to tell you where I don't.

Contact Anto Joel: antojoel8020@gmail.com · WhatsApp · Book a call