lucidAGENTS
Build

Define a capability

Describe one machine-callable service with schemas, fulfillment, pricing, and operation modes.

A capability is what a buyer understands: a description, input contract, output contract, price, and fulfillment behavior. EntrypointDef is the SDK object that represents it.

Define the public contract

runtime.entrypoints.add({
  key: 'classify',
  description: 'Classify text into one supported category',
  input: z.object({
    text: z.string().min(1),
    categories: z.array(z.string().min(1)).min(2),
  }),
  output: z.object({
    category: z.string(),
    confidence: z.number().min(0).max(1),
  }),
  price: '0.02',
  handler: async ({ input, signal, runId }) => {
    const result = await classify(input, { signal, runId });
    return { output: result };
  },
});

Contract rules

  • key is stable API identity. Changing it changes URLs, task targets, and discovery metadata.
  • Validate both input and output. A paid response with the wrong shape is a fulfillment failure.
  • Make cancellation observable through signal.
  • Use runId for logs; use an idempotency key for business deduplication.
  • Set paymentProtocol when both x402 and MPP are installed.
  • Keep framework objects out of the handler. Request headers belong in typed metadata supplied by the HTTP extension.

The invoke route accepts either the input value directly or the canonical { "input": ... } envelope. A successful response contains run_id, status: "succeeded", the validated output, and optional usage/model. Treat that wire shape as public even when a generated storefront hides it.

Choose price and operation modes

price is a USD decimal string, not a token atomic amount or JavaScript number. A single string applies to the capability's priced operation; use an object when invoke and stream differ:

price: { invoke: '0.02', stream: '0.03' }

When both x402 and MPP extensions are installed, set paymentProtocol explicitly. An entrypoint-level network overrides the payments runtime network for that capability. Do not advertise a stream price without a stream handler or a task capability without a2a().

Bound fulfillment

Schema validation is necessary but not sufficient. Put explicit limits on input bytes, array sizes, remote fetches, model/tool iterations, duration, concurrency, and output size. Propagate signal to every downstream operation and stop work when the caller disconnects or the task is cancelled.

Use runId for correlation. Use a stable business idempotency key and a database uniqueness constraint for effects that must not repeat; a run ID is new for each execution and is not a deduplication key.

Failure contract

FailureHTTP behaviorWhat to fix
Invalid JSON/outer request400 invalid_requestClient envelope
Input schema failure400 invalid_inputClient fields/limits
Output schema failure500 invalid_outputHandler implementation
Handler throw500 internal_errorApplication/dependency; inspect payment state before retry
Missing handler501 not_implementedCapability registration

Input/output errors can occur inside the payment transaction. Read Payment lifecycle before promising whether a specific failure is charged or refundable.

Verify the capability

  1. Unit-test the handler with minimum, maximum, invalid, cancelled, and downstream-failure inputs.
  2. Invoke the free route and assert the exact response schema.
  3. Add a testnet price and assert an unpaid request returns the expected 402 without running the handler.
  4. Complete one paid request and reconcile schema-valid output plus settlement evidence.
  5. Retry the same operation concurrently with one idempotency key and assert the business effect occurs once.

The adapter-level addEntrypoint helper remains a compatibility delegate. New runtime-oriented code should register through the canonical registry so the same definition serves every adapter.

Next: receive x402, choose streaming or asynchronous tasks, and consult the core package reference.

On this page