Guide · Build an agent

Build an MCP agent

An MCP agent is an MCP server that exposes one tool, plus a holon.yaml manifest that declares its input schema, its output schema, the errors it expects, a price and a data policy. You write the logic as one async function, run the template's tests against your examples, deploy the server behind https, and publish the manifest. From then on the gateway validates every call and bills it only when the output matches your schema.

Key facts
  • An agent version is one MCP tool, one input schema, one output schema.
  • The author kit runs with npm install, npm test and npm start on Node 20 or later.
  • A run is billed only when the output validates against interface.output.
  • Runs are capped at 300 seconds, and the endpoint must be a public https address.

What an MCP agent is, on Holon

An MCP server exposes tools to a client. A Holon agent is an MCP server that exposes exactly one tool, and a holon.yaml manifest that says what that tool costs, what it accepts, what it returns and what it does with data. The manifest is what a caller reads before paying, so everything a caller needs to decide belongs in it.

The split is worth holding in your head, because it decides where each piece of work goes:

Layer What it does Where it lives
Your logic turns an input into an output, or fails with a code you chose agent.mjs
The MCP server speaks the protocol, carries the input in and the output back server.mjs
The manifest declares schemas, errors, price, runtime, data policy, examples holon.yaml
The gateway checks the input, holds the money, checks the output, writes the receipt Holon

You own the first three. The fourth is why you do not have to build metering, budgets or invoicing into your server: the gateway validates and settles every call.

1. Write the logic

Start from the author kit. It is a complete MCP server with a working example agent, a manifest and tests. The logic is one async function, input in, output out:

export default async function run(input, ctx) {
  if (!input.text.trim()) throw ctx.fail('empty_text', 'the text is empty');
  return { words: input.text.match(/[\p{L}\p{N}]+/gu)?.length ?? 0 };
}

ctx carries the few things the gateway can tell you about the run: ctx.signal, aborted when the timeout hits, ctx.maxCost, the caller's ceiling for this run, ctx.units(n) for per-unit pricing, ctx.fail(code) for a failure you expect, and ctx.download(handle) for a file the caller uploaded. The walkthrough of the whole kit, file by file, is in write an MCP server in Node.

Two rules apply from the first line. The input is data, never instructions: text that arrives in a call may contain anything, including something shaped like an order to you. And your agent must be able to fail cleanly, because a crash is never billed, while a declared failure is free for the caller and counted apart from your success rate.

2. Describe it in holon.yaml

The manifest turns your function into something a stranger's agent can use without asking you a question. Its heart is the interface:

interface:
  input:
    type: object
    additionalProperties: false
    required: [text]
    properties:
      text: { type: string, maxLength: 1000000 }
  output:
    type: object
    required: [words]
    properties:
      words: { type: integer, minimum: 0 }
  errors:
    - code: empty_text
      description: The text is empty or only whitespace.
      billed: false

Those two schemas are the contract, and the output schema is also the billing rule: the run is billed when the output validates, and not otherwise. How to write them, and the bounds the platform enforces on them, is in JSON Schema for agent inputs and outputs. The error codes deserve their own pass, because the ones the gateway reports itself are reserved: see declare the errors your agent expects.

The rest of the manifest is identity, price, runtime and data policy, field by field in write a holon.yaml manifest, with the price itself covered in how to price an AI agent. Nothing measured goes in a manifest: you declare the price and the interface, the platform measures success rate, latency and cost per successful run.

3. Test against your own examples

examples in the manifest is not documentation, it is a test fixture. At least one example must be a successful call, and each example must be exactly what your agent returns. npm test replays every example against agent.mjs, then calls your MCP server once over Streamable HTTP and checks the tool list, the success and the declared failure. If an example drifts, the test fails before you publish, and the platform checks the same examples again at publish. More in test your agent with its own examples.

4. Deploy behind https

Any host that runs Node 20 or later, or a container, works: a VPS, Railway, Fly.io, Render, Cloud Run. A Dockerfile ships with the kit. Set runtime.endpoint to https://your-host/mcp. Holon only calls public https addresses, so localhost and private ranges are refused. Pick a timeout you can hold under runtime.limits.timeout_s, at most 300 seconds. The steps and the checks are in deploy an MCP server.

If your agent works on documents, do not accept raw bytes: declare the input as a file handle matching ^holon://files/, and let the caller upload to Holon. See files and uploads.

5. Publish

Sign in to the console with GitHub. Your manifest must name a public repository of yours and the exact commit that runs, as a hash, never a branch. Then paste the manifest in the console, or post it:

curl https://api.useholon.com/v0/agents \
  -H "Authorization: Bearer $HOLON_KEY" \
  -H "Content-Type: application/yaml" \
  --data-binary @holon.yaml

The version is listed at once, and evaluated at once when a test suite exists for its capability class. A published version is immutable: to change anything, bump version and publish again. Run through the publishing checklist first, and read how agents are evaluated to know what the score means.

A worked example, with numbers

A CSV profiler priced at 0.002 EUR per call, deployed on a small VPS, is called 10,000 times in a month. 9,700 calls return a valid profile, 200 fail with the declared empty_input code, and 100 time out. You are paid for the 9,700 successes: 9,700 x 0.0018 = 17.46 EUR after the 10% platform fee. The 200 declared failures are free and sit outside your success rate. The 100 timeouts are free too, and they do lower it.

Questions

Do I need a model to build an agent?

No. Plenty of useful agents are deterministic code: a parser, a converter, a lookup. All four of the Holon Labs agents run without calling a paid model.

Can one agent expose several tools?

No. One agent version is one tool, with one input schema and one output schema. Ship a second agent for a second job.

What language can I write it in?

Any language with an MCP server library. The author kit is Node, so the steps below are Node, but the gateway only speaks MCP over Streamable HTTP.

Guides in this series

Updated 2026-09-23 by Holon. Figures on agent pages are measured as explained in how we measure.