Private beta

Request beta access

Interagentic is in private beta. Leave your email and we'll send you an access key when there's room.

Interagentic
Protocols

Command model

How a service declares its commands as arrays of segments, and how a client resolves them into a subcommand chain of any depth.

Status v1 · Part of the Services specification

An action declares its command as an ordered list of segments:

{ "command": ["orders", "refunds", "create"] }

A client turns that into the subcommand chain developers already know from git, gh and vercel:

npx interagentic popcorn.club orders refunds create --id ord_8fJ2

1. Design

The command tree is declared by the service, never inferred by the client. Four properties follow from that.

Any depth works the same way. A deeper command is a longer array. Nothing changes at the third or the tenth level.

Every level can explain itself. Intermediate nodes are declared as groups with their own description, so stopping halfway down a chain prints something useful instead of failing.

A chain is never ambiguous. Actions are leaves and groups are branches, so every sequence of words resolves to exactly one thing.

Mistakes are caught locally. The client holds the whole tree from the manifest, so it can complete a segment, or reject an unknown one, before any request is made.

2. Resolution

Given the arguments after the domain, a client resolves them greedily:

  1. Take the longest prefix of arguments that exactly matches an action's command. That is the action; the remainder are its arguments.
  2. If no action matches but the arguments are a prefix of one or more commands, the node is a group: print its description and children, and exit 0.
  3. Otherwise the command is unknown: print the nearest matches and exit 2, before making any request.

Stopping at a group prints help rather than failing, which is what makes a deep tree explorable:

npx interagentic popcorn.club orders
orders — Place and track orders

  list      List your orders
  create    Place an order                [$]
  refunds   Issue refunds                 → 2 more

3. Segments

Each segment MUST match:

^[a-z0-9]+(-[a-z0-9]+)*$

Lowercase, digits, single hyphens between words. Depth is unbounded.

Commands MUST be unique within a service, and an action's command MUST NOT be a prefix of another action's command. An action is always a leaf: if orders needs children, it is a group, and the thing it did becomes one of them — orders list, for instance.

4. Groups

{
  "groups": [
    { "command": ["orders"], "description": "Place and track orders" },
    { "command": ["orders", "refunds"], "description": "Issue refunds" }
  ]
}

Groups are optional. A client encountering an undeclared intermediate node MUST still resolve it, listing its children without a description. Declaring groups is how a service makes itself pleasant to explore.

5. Arguments

Everything after the resolved command is an argument.

  • Names listed in the action's args array may be given positionally, in order.
  • Everything else is a --flag.
  • --json switches off human-readable rendering and is reserved by clients.
  • --help prints the action's parameters, types and example, and sends nothing.
npx interagentic popcorn.club orders create 2
npx interagentic popcorn.club orders create --qty 2

6. Action files

Each action's detail file lives at a path that mirrors its command, one directory per segment:

CommandDetail file
["orders", "list"]/.well-known/interagentic/services/orders/list.json
["orders", "create"]/.well-known/interagentic/services/orders/create.json
["orders", "refunds", "create"]/.well-known/interagentic/services/orders/refunds/create.json

Because an action is always a leaf, the tree on disk has the same shape as the tree in the CLI, and a static file server needs no configuration to serve it.

7. Deriving commands from OpenAPI

If you already publish OpenAPI, commands fall out of your paths and methods:

OperationCommand
GET /ordersorders list
GET /orders/{id}orders get
POST /ordersorders create
PATCH /orders/{id}orders update
DELETE /orders/{id}orders delete
POST /orders/{id}/refundsorders refunds create

Path segments become groups and path parameters are skipped. The method chooses the verb: GET on a collection is list, GET on a single item is get, POST is create, PUT and PATCH are update, DELETE is delete.

Path parameters become request templates in the endpoint, so /orders/{id}/refunds is published as /orders/{{$id}}/refunds with a required id argument.

To name something differently, set x-interagentic-command on the operation:

paths:
  /orders/{id}/ship:
    post:
      x-interagentic-command: [orders, ship]
      summary: Mark an order as shipped

On this page