# Microservices Were Built for Programs. Agents Need Capabilities.

> Source: <https://naftiko.io/blog/microservices-were-built-for-programs-agents-need-capabilities/>
> Published: 2026-10-04 00:00:00+00:00

Microservices were designed for **deterministic clients**. A developer reads the contract, writes the call sequence, handles the auth, picks the fields that matter — once.

An agent decides all of that at runtime, pays for every byte it reads, and retries when unsure. Expose your services one-to-one as MCP tools and every decision the developer made once, the model now makes on every request. **The services are fine. The unit of exposure is wrong.**

## Same container infrastructure as your microservices

This is not a new tier of infrastructure. A running Ikanos capability uses the same container infrastructure as your microservices: the [`naftiko/ikanos`](https://hub.docker.com/r/naftiko/ikanos) image from Docker Hub serving one spec file, deployed in your Kubernetes cluster as a `Deployment` behind a `Service`.

It scales like any `Deployment`, sits behind your gateway, and a control port exposes `/health/live`, `/health/ready` and `/metrics` for your probes and Prometheus. Your platform team operates it with the tools it already has. What differs is the contract: it is declared for an agent, not coded for a program.

## Five decisions the agent should not be making

**1. Granularity.** `POST /orders`, `GET /users/{id}/payment-methods`, `POST /inventory/reserve` are each correct; none of them is “fulfil this order”. An Ikanos aggregate composes them in `steps` — `call` and `lookup` — and exposes the flow as one tool.

**2. Formats.** The legacy service returns XML, the partner feed CSV. Each consumed operation declares its `outputRawFormat` (JSON, XML, Avro, Protobuf, CSV, YAML and more); the engine parses it and the agent only sees JSON.

**3. Payload size.** Responses carry database keys, trace IDs and audit fields. A capability maps only the fields it declares; everything else never leaves the engine.

**4. Credentials.** Auth is declared per consumed service (`basic`, `apikey`, `bearer`, `digest`, `oauth2`) with values resolved from `binds` at runtime. The model sees a tool name and its inputs, never a secret.

**5. Side effects.** An unsure agent retries, which is harmless for `GET` and costly for `POST`. Flows declare `semantics` (`safe`, `idempotent`, `cacheable`) and the engine derives MCP hints from them. Endpoints you don’t expose are tools the agent can’t call; rate limiting stays [in your gateway](https://naftiko.io/blog/you-have-apigee-now-your-ai-agents-need-to-use-it/).

## One file, five answers

Trimmed from step 9 of the Shipyard tutorial:

```
capability:
  consumes:
  - namespace: registry                  # JSON, bearer
    type: http
    baseUri: "https://registry.internal"
    authentication: { type: bearer, token: "{{REGISTRY_TOKEN}}" }
  - namespace: legacy                    # XML, API key
    type: http
    baseUri: "https://dockyard.internal"
    authentication: { type: apikey, key: X-Dock-Key, placement: header, value: "{{DOCKYARD_API_KEY}}" }

  aggregates:
  - namespace: crew-resolver
    flows:
      resolve-crew-for-ship:
        semantics: { safe: true, idempotent: true, cacheable: true }
        steps:
          get-ship:  { type: call, call: registry.get-ship, with: { imo_number: "{{imo}}" } }
          list-crew: { type: call, call: registry.list-crew }
          resolve-crew:
            type: lookup
            index: list-crew
            match: crewId
            lookupValue: "$.get-ship.assignedCrew"
            outputParameters: [ fullName, role ]
        mappings:
        - { target: name, value: "$.get-ship.vessel_name" }
        - { target: crew, value: "$.resolve-crew" }

  exposes:
  - type: mcp
    namespace: shipyard-tools
    tools:
      get-ship-with-crew:
        description: "Get ship details with resolved crew names"
        ref: crew-resolver.resolve-crew-for-ship
```

Two services, two auth schemes, one goal-shaped tool. None of the services changed.

## The takeaway

- Microservices are the right way to build the backend, and the wrong unit to hand an agent.
- Capabilities run on the same container infrastructure: one container, one spec, in the cluster you already run.
- Make the five decisions once, in a reviewable spec, and expose the result as MCP, REST or a skill.
