> ## Documentation Index
> Fetch the complete documentation index at: https://agentstack.beeai.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent Requirements

> Fulfill agent service demands and render UI extensions in your custom interface

Agent Stack extends the [A2A Protocol](https://a2a-protocol.org/) with
[A2A extensions](https://a2a-protocol.org/latest/topics/extensions/) that enable agents to access platform services and
enhance the user interface beyond what the base protocol provides.

Extensions fall into two categories:

1. **Service extensions:** Demands in the agent card that your client fulfills (LLMs, embeddings, OAuth, secrets)
2. **UI extensions:** Message metadata your UI renders (forms, approvals, citations, trajectories)

All extension definitions are exported from `agentstack-sdk/extensions` if you prefer a narrower import surface.

## Core Helpers

The SDK includes helpers for reading and producing extension metadata:

* `extractServiceExtensionDemands(extension)` reads demands from an agent card.
* `fulfillServiceExtensionDemand(extension)` merges fulfillments into metadata.
* `extractUiExtensionData(extension)` reads UI metadata from a message.
* `resolveUserMetadata(inputs)` builds metadata for form, approval, and canvas responses.
* `handleAgentCard(agentCard)` returns `demands` and a `resolveMetadata` function.
* `handleTaskStatusUpdate(event)` maps status updates to actionable UI results.
* `buildMessageBuilder(agentCard)` builds user messages with resolved metadata.

Use these directly when you want fine grained control beyond `handleAgentCard`.

`extractServiceExtensionDemands`, `fulfillServiceExtensionDemand`, and `extractUiExtensionData` are factories. You pass
an extension definition once and reuse the returned function for that extension.

For end to end usage, see **[A2A Client Integration](./a2a-client)** and **[User Messages](./user-messages)**.

## Service Extensions

Service extensions use a dependency injection pattern. The agent declares demands and the client provides fulfillments.

### LLM Service

Provides OpenAI compatible LLM access with `api_base`, `api_key`, and `api_model`.

```typescript theme={null}
import { buildLLMExtensionFulfillmentResolver, handleAgentCard } from 'agentstack-sdk';

const { resolveMetadata } = handleAgentCard(agentCard);
const llmResolver = buildLLMExtensionFulfillmentResolver(api, contextToken);

const metadata = await resolveMetadata({
  llm: llmResolver,
});
```

### Embedding Service

Provides OpenAI compatible embedding access with `api_base`, `api_key`, and `api_model`.

```typescript theme={null}
import { handleAgentCard } from 'agentstack-sdk';

const { resolveMetadata } = handleAgentCard(agentCard);

const metadata = await resolveMetadata({
  embedding: async (demands) => ({
    embedding_fulfillments: Object.fromEntries(
      Object.keys(demands.embedding_demands).map((key) => [
        key,
        {
          identifier: 'openai/text-embedding-3-small',
          api_base: 'https://api.openai.com/v1',
          api_key: 'your-api-key',
          api_model: 'text-embedding-3-small',
        },
      ]),
    ),
  }),
});
```

### MCP

Provides Model Context Protocol transports for tool and connector access.

```typescript theme={null}
import { MCPTransportType, handleAgentCard } from 'agentstack-sdk';

const { resolveMetadata } = handleAgentCard(agentCard);

const metadata = await resolveMetadata({
  mcp: async (demands) => ({
    mcp_fulfillments: Object.fromEntries(
      Object.keys(demands.mcp_demands).map((key) => [
        key,
        {
          transport: {
            type: MCPTransportType.StreamableHttp,
            url: 'https://mcp.example.com',
            headers: { Authorization: 'Bearer token' },
          },
        },
      ]),
    ),
  }),
});
```

### OAuth

Provides OAuth redirect metadata for authentication flows.

```typescript theme={null}
import { handleAgentCard } from 'agentstack-sdk';

const { resolveMetadata } = handleAgentCard(agentCard);

const metadata = await resolveMetadata({
  oauth: async (demands) => ({
    oauth_fulfillments: Object.fromEntries(
      Object.keys(demands.oauth_demands).map((key) => [
        key,
        { redirect_uri: 'https://app.example.com/oauth/callback' },
      ]),
    ),
  }),
  oauthRedirectUri: () => 'https://app.example.com/oauth/callback',
});
```

### Secrets

Provides secret values required by the agent.

```typescript theme={null}
import { handleAgentCard } from 'agentstack-sdk';

const { resolveMetadata } = handleAgentCard(agentCard);

const metadata = await resolveMetadata({
  secrets: async (demands) => ({
    secret_fulfillments: Object.fromEntries(
      Object.keys(demands.secret_demands).map((key) => [key, { secret: 'your-secret' }]),
    ),
  }),
});
```

### Settings (deprecated)

Deprecated. Planned for removal in the next release. Use [Settings Form](#settings-form) instead.

This extension lives under `ui/settings` in the SDK, but it is treated as a service extension because it carries demands
and fulfillments.

```typescript theme={null}
import { handleAgentCard } from 'agentstack-sdk';

const { resolveMetadata } = handleAgentCard(agentCard);

const metadata = await resolveMetadata({
  settings: async (demands) => ({
    values: Object.fromEntries(
      demands.fields.map((field) => {
        if (field.type === 'single_select') {
          return [field.id, { type: 'single_select', value: field.default_value }];
        }
        return [
          field.id,
          {
            type: 'checkbox_group',
            values: Object.fromEntries(
              field.fields.map((checkbox) => [checkbox.id, { value: checkbox.default_value }]),
            ),
          },
        ];
      }),
    ),
  }),
});
```

#### Migration guide

* Agent cards: replace the `ui/settings` extension with the `services/form` extension and move settings fields to `form_demands.settings_form`.
* Single select: change type `single_select` to `singleselect`, and change `options` items from `{ value }` to `{ id }`.
* Checkbox group: `checkbox_group` now requires a non empty `label`.
* Checkbox items: `checkbox` now requires `content` for the visible text. Keep `label` populated too and map the legacy checkbox label to `content` when migrating.
* Fulfillments: change checkbox group values from `{ values: { [id]: { value } } }` to `{ value: { [id]: boolean | null } }`.

### Form

Provides form responses when the agent requests structured input.

```typescript theme={null}
import { handleAgentCard } from 'agentstack-sdk';

const { resolveMetadata } = handleAgentCard(agentCard);

const metadata = await resolveMetadata({
  form: async () => ({
    form_fulfillments: {
      default: {
        values: { name: 'Ada' },
      },
    },
  }),
});
```

#### Settings Form

Settings UI is delivered through the Form service extension. Agents request settings with `form_demands.settings_form`, and clients respond with `form_fulfillments.settings_form`.

```typescript theme={null}
import { handleAgentCard } from "agentstack-sdk";

const { resolveMetadata } = handleAgentCard(agentCard);

const metadata = await resolveMetadata({
  form: async (demands) => {
    const settingsForm = demands.form_demands.settings_form;

    if (!settingsForm) {
      return { form_fulfillments: {} };
    }

    return {
      form_fulfillments: {
        settings_form: {
          values: Object.fromEntries(
            settingsForm.fields.map((field) => {
              if (field.type === "singleselect") {
                return [
                  field.id,
                  {
                    type: "singleselect",
                    value: field.default_value
                  }
                ];
              }

              if (field.type === "checkbox_group") {
                return [
                  field.id,
                  {
                    type: "checkbox_group",
                    value: Object.fromEntries(
                      field.fields.map((checkbox) => [checkbox.id, checkbox.default_value]),
                    ),
                  },
                ];
              }

              throw new Error("Unsupported settings field type");
            }),
          ),
        },
      },
    };
  },
});
```

Prefer `settings_form` when both `settings_form` and legacy settings demands are present.

### Platform API

Adds context token metadata so the agent can call platform services. This is typically used only when you cannot pass
the token through A2A client headers. `getContextToken` is deprecated and kept for backward compatibility. Prefer
passing the context token via A2A client headers when possible.

```typescript theme={null}
import { handleAgentCard } from 'agentstack-sdk';

const { resolveMetadata } = handleAgentCard(agentCard);

const metadata = await resolveMetadata({
  getContextToken: () => contextToken,
});
```

`getContextToken` is deprecated. Prefer sending the context token via A2A client headers when possible.

## UI Extensions

UI extensions are message metadata your UI can render. The SDK includes typed schemas for extracting these payloads.

### Form Request

Requests a form render payload.

```typescript theme={null}
import { extractUiExtensionData, formRequestExtension } from 'agentstack-sdk';

const readForm = extractUiExtensionData(formRequestExtension);
const form = readForm(message.metadata);
```

### Approval

Requests user approval for an action or tool call.

```typescript theme={null}
import { approvalExtension, extractUiExtensionData } from 'agentstack-sdk';

const readApproval = extractUiExtensionData(approvalExtension);
const approval = readApproval(message.metadata);
```

### Canvas

Requests a canvas edit with indices and description.

```typescript theme={null}
import { canvasExtension, extractUiExtensionData } from 'agentstack-sdk';

const readCanvas = extractUiExtensionData(canvasExtension);
const request = readCanvas(message.metadata);
```

### Citation

Provides citation ranges and URLs for inline references.

```typescript theme={null}
import { citationExtension, extractUiExtensionData } from 'agentstack-sdk';

const readCitations = extractUiExtensionData(citationExtension);
const citations = readCitations(message.metadata);
```

### Trajectory

Provides trace entries for reasoning or execution steps.

```typescript theme={null}
import { extractUiExtensionData, trajectoryExtension } from 'agentstack-sdk';

const readTrajectory = extractUiExtensionData(trajectoryExtension);
const trajectory = readTrajectory(message.metadata);
```

### Agent Detail

Provides agent metadata to display in the UI. This extension is sent in the agent card capabilities, not in message
metadata.

```typescript theme={null}
import { agentDetailExtension, extractUiExtensionData } from 'agentstack-sdk';

const readAgentDetail = extractUiExtensionData(agentDetailExtension);
const uri = agentDetailExtension.getUri();
const params = agentCard.capabilities.extensions?.find((extension) => extension.uri === uri)?.params;

const agentDetail = params ? readAgentDetail({ [uri]: params }) : null;
```

### Error

Provides structured error information.

```typescript theme={null}
import { errorExtension, extractUiExtensionData } from 'agentstack-sdk';

const readError = extractUiExtensionData(errorExtension);
const error = readError(message.metadata);
```

### OAuth Request

Provides an OAuth authorization endpoint to redirect the user.

```typescript theme={null}
import { extractUiExtensionData, oauthRequestExtension } from 'agentstack-sdk';

const readOAuth = extractUiExtensionData(oauthRequestExtension);
const oauthRequest = readOAuth(message.metadata);
```

### Secrets Request

Provides secret demand prompts.

```typescript theme={null}
import { extractUiExtensionData, secretsRequestExtension } from 'agentstack-sdk';

const readSecrets = extractUiExtensionData(secretsRequestExtension);
const secretDemands = readSecrets(message.metadata);
```

### Handling task status updates

Use `handleTaskStatusUpdate` to parse status updates into UI actions. This covers OAuth, secrets, forms, and approval
flows.

```typescript theme={null}
import { handleTaskStatusUpdate, TaskStatusUpdateType } from 'agentstack-sdk';

for await (const event of stream) {
  if (event.kind === 'status-update') {
    handleTaskStatusUpdate(event).forEach((result) => {
      switch (result.type) {
        case TaskStatusUpdateType.FormRequired:
          // Render result.form
          break;
        case TaskStatusUpdateType.OAuthRequired:
          // Redirect to result.url
          break;
        case TaskStatusUpdateType.SecretRequired:
          // Prompt for result.demands
          break;
        case TaskStatusUpdateType.ApprovalRequired:
          // Ask the user to approve result.request
          break;
        case TaskStatusUpdateType.TextInputRequired:
        // Prompt for text input
      }
    });
  }
}
```

### Sending user metadata

When the user responds to a form, approval, or canvas request, use `resolveUserMetadata` to build message metadata:

```typescript theme={null}
import { resolveUserMetadata } from 'agentstack-sdk';

const metadata = await resolveUserMetadata({
  form: formValues,
  approvalResponse: { decision: 'approve' },
  canvasEditRequest: {
    start_index: 0,
    end_index: 12,
    artifact_id: 'artifact-id',
    description: 'Replace the title',
  },
});
```

## Type Safety

All extension schemas are typed in TypeScript and validated with Zod at runtime. This helps catch malformed extension
payloads early and keeps your UI logic aligned with the protocol.
