Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 108 additions & 34 deletions apps/agentstack-sdk-ts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ TypeScript/JavaScript client SDK for building applications that interact with Ag

## Overview

The `agentstack-sdk` provides TypeScript/JavaScript tools for building client applications that communicate with agents deployed on Agent Stack. It includes utilities for handling the A2A (Agent-to-Agent) protocol, managing agent extensions, and working with the Agent Stack platform API.
The `agentstack-sdk` provides TypeScript and JavaScript tools for building client applications that communicate with agents deployed on Agent Stack. It includes utilities for handling the A2A (Agent2Agent) protocol, working with extensions, and calling the Agent Stack platform API.

## Key Features

- **A2A Protocol Support** - Full support for Agent-to-Agent communication
- **Extension System** - Built-in handlers for forms, OAuth, LLM services, MCP, and more
- **Platform API Client** - Utilities for context and token management
- **TypeScript Types** - Comprehensive types for all APIs
- **A2A Protocol Support** - Parse agent cards and task status updates with typed utilities
- **Extension System** - Resolve service demands and UI metadata with typed helpers
- **Platform API Client** - Typed access to core platform resources
- **Type Safe Responses** - Zod validated payloads with structured API error helpers

## Installation

Expand All @@ -26,47 +26,121 @@ npm install agentstack-sdk
## Quickstart

```typescript
import { handleAgentCard, handleTaskStatusUpdate, TaskStatusUpdateType } from 'agentstack-sdk';

// Parse agent capabilities
const { extensions, fulfillments } = await handleAgentCard(agentCard);

// Send message and handle responses
const stream = client.sendMessage(message);
import {
buildApiClient,
buildLLMExtensionFulfillmentResolver,
handleAgentCard,
handleTaskStatusUpdate,
TaskStatusUpdateType,
unwrapResult,
} from "agentstack-sdk";

const api = buildApiClient({ baseUrl: "https://your-agentstack-instance.com" });

// 1. Receive an agent card and resolve metadata for service demands.
const { resolveMetadata } = handleAgentCard(agentCard);
const context = unwrapResult(await api.createContext({ provider_id: "provider-id" }));
const token = unwrapResult(
await api.createContextToken({
context_id: context.id,
grant_global_permissions: { llm: ["*"], a2a_proxy: ["*"] },
}),
);

const llmResolver = buildLLMExtensionFulfillmentResolver(api, token);
const metadata = await resolveMetadata({ llm: llmResolver });

// 2. Send a message with metadata using your A2A client.
const stream = client.sendMessageStream({
message: {
messageId: 'message-id',
kind: "message",
role: "user",
contextId: context.id,
parts: [{ kind: "text", text: "Hello" }],
metadata,
}
});

// 3. Handle task status updates.
for await (const event of stream) {
const result = handleTaskStatusUpdate(event);

switch (result.type) {
case TaskStatusUpdateType.Message:
console.log('Agent response:', result.message.parts[0].text);
break;
case TaskStatusUpdateType.InputRequired:
// Handle extension demands (forms, OAuth, etc.)
break;
if (event.kind === "status-update") {
const message = event.status.message;

if (message) {
for (const part of message.parts) {
if (part.kind === "text") {
console.log("Agent:", part.text);
}
}

if (message.metadata) {
console.log("Metadata keys:", Object.keys(message.metadata));
}
}

handleTaskStatusUpdate(event).forEach((result) => {
switch (result.type) {
case TaskStatusUpdateType.FormRequired:
// Show form to the user
break;
case TaskStatusUpdateType.OAuthRequired:
// Redirect to result.url
break;
case TaskStatusUpdateType.SecretRequired:
// Prompt for secrets
break;
case TaskStatusUpdateType.ApprovalRequired:
// Request approval from the user
break;
}
});
}
}
```

## Available Extensions
## Core APIs

The SDK includes clients and specs for handling:
- `buildApiClient` returns a typed API client for platform endpoints.
- `handleAgentCard` extracts extension demands and returns `resolveMetadata`.
- `handleTaskStatusUpdate` parses A2A status updates into UI actions.
- `resolveUserMetadata` builds metadata when the user submits forms, canvas edits, or approvals.
- `createAuthenticatedFetch` helps add bearer auth headers to API calls.
- `buildLLMExtensionFulfillmentResolver` matches LLM providers and returns fulfillments.
- `unwrapResult` returns the response data on success, throws an `ApiErrorException` on error

- **Forms** - Static and dynamic form handling (`FormExtensionClient`, `FormExtensionSpec`)
- **OAuth** - Authentication flows (`OAuthExtensionClient`, `OAuthExtensionSpec`)
- **LLM Services** - Model access and credentials (`LLMServiceExtensionClient`, `buildLLMExtensionFulfillmentResolver`)
- **Platform API** - Context and resource access (`PlatformApiExtensionClient`, `buildApiClient`)
- **MCP** - Model Context Protocol integration (`MCPServiceExtensionClient`)
- **Embeddings** - Vector embedding services (`EmbeddingServiceExtensionClient`)
- **Secrets** - Secure credential management (`SecretsExtensionClient`)
- **Citations** - Source attribution (`citationExtension`)
- **Agent Details** - Metadata and UI enhancements (`AgentDetailExtensionSpec`)
## Extensions

Each extension has a corresponding `ExtensionClient` for sending data and `ExtensionSpec` for parsing agent cards.
Service extensions (client fulfillments):

## Resources
- **Embedding** - Provide embedding access (`api_base`, `api_key`, `api_model`) for RAG or search.
- **Form** - Request structured user input via forms.
- **LLM** - Resolve model access and credentials for text generation.
- **MCP** - Connect Model Context Protocol services and tools.
- **OAuth** - Provide OAuth credentials or redirect URIs.
- **Platform API** - Inject context token metadata for platform access.
- **Secrets** - Supply or request secret values securely.

UI extensions (message metadata your UI can render):

- **Agent Detail** - Show agent specific metadata and context.
- **Approval** - Ask the user to approve actions or tool calls.
- **Canvas** - Provide canvas edit requests and updates.
- **Citation** - Display inline source references.
- **Error** - Render structured error messages.
- **Form Request** - Render interactive forms in the UI.
- **Settings** - Read or update runtime configuration values.
- **Trajectory** - Render execution traces or reasoning steps.

## Documentation

- [Agent Stack Documentation](https://agentstack.beeai.dev)
- [Client SDK Overview](https://agentstack.beeai.dev/development/custom-ui/client-sdk/overview)
- [Extensions](https://agentstack.beeai.dev/development/custom-ui/client-sdk/extensions)
- [API Client](https://agentstack.beeai.dev/development/custom-ui/client-sdk/api-client)

## Resources

- [GitHub Repository](https://github.com/i-am-bee/agentstack)
- [npm Package](https://www.npmjs.com/package/agentstack-sdk)

Expand Down
195 changes: 195 additions & 0 deletions docs/development/custom-ui/client-sdk/a2a-client-integration.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
---
title: A2A Client Integration
description: Wire @a2a-js/sdk with AgentStack helpers to send messages and render UI updates
---

This guide shows how to integrate `@a2a-js/sdk` with the AgentStack SDK helpers. It mirrors the flow used in `agentstack-ui`.

## 1. Create an A2A client

Use the A2A client factory and the AgentStack authenticated fetch helper.

```typescript
import {
ClientFactory,
ClientFactoryOptions,
DefaultAgentCardResolver,
JsonRpcTransportFactory,
} from "@a2a-js/sdk/client";
import { createAuthenticatedFetch } from "agentstack-sdk";

async function getAgentClient(baseUrl: string, providerId: string, token?: string) {
const fetchImpl = token ? createAuthenticatedFetch(token) : fetch;
const agentCardPath = `api/v1/a2a/${providerId}/.well-known/agent-card.json`;

const factory = new ClientFactory(
ClientFactoryOptions.createFrom(ClientFactoryOptions.default, {
transports: [new JsonRpcTransportFactory({ fetchImpl })],
cardResolver: new DefaultAgentCardResolver({ fetchImpl }),
}),
);

return factory.createFromUrl(baseUrl, agentCardPath);
}
```

## 2. Resolve agent card demands

Once you have the client, read the agent card and resolve its demands.

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

const client = await getAgentClient(baseUrl, providerId, token);
const card = await client.getAgentCard();

const { resolveMetadata, demands } = handleAgentCard(card);
```

Use `demands` to decide which fulfillments you can satisfy, then call `resolveMetadata`.

```typescript
const fulfillments = {
llm: async (llmDemands) => ({
llm_fulfillments: Object.fromEntries(
Object.keys(llmDemands.llm_demands).map((key) => [
key,
{
identifier: "llm_proxy",
api_base: "{platform_url}/api/v1/openai/",
api_key: contextToken.token,
api_model: "gpt-4o",
},
]),
),
}),
oauth: demands.oauthDemands
? async (oauthDemands) => ({
oauth_fulfillments: Object.fromEntries(
Object.keys(oauthDemands.oauth_demands).map((key) => [
key,
{ redirect_uri: "https://app.example.com/oauth/callback" },
]),
),
})
: undefined,
};

const metadata = await resolveMetadata(fulfillments);
```

See [Extensions](/development/custom-ui/client-sdk/extensions) for the available service and UI extension helpers.

## 3. Send a message stream and handle updates

Merge agent card metadata with user metadata and stream events.

```typescript
import { handleTaskStatusUpdate, resolveUserMetadata, TaskStatusUpdateType } from "agentstack-sdk";

const agentCardMetadata = await resolveMetadata(fulfillments);
const userMetadata = await resolveUserMetadata(inputs);

const stream = client.sendMessageStream({
message: {
kind: "message",
role: "user",
messageId: "message-id",
contextId: "context-id",
parts: [{ kind: "text", text: "Hello" }],
metadata: { ...agentCardMetadata, ...userMetadata },
},
});

for await (const event of stream) {
if (event.kind === "task") {
const taskId = event.id;
// Store taskId for cancellation or follow up requests
}

if (event.kind === "status-update") {
for (const update of handleTaskStatusUpdate(event)) {
switch (update.type) {
case TaskStatusUpdateType.FormRequired:
// Render update.form
break;
case TaskStatusUpdateType.OAuthRequired:
// Redirect to update.url
break;
case TaskStatusUpdateType.SecretRequired:
// Prompt for update.demands
break;
case TaskStatusUpdateType.ApprovalRequired:
// Ask user to approve update.request
break;
}
}
}

if (event.kind === "artifact-update") {
// Render event.artifact parts and metadata
}
}
```

In `agentstack-ui`, status updates are also used to render message parts, and artifact updates are rendered as rich UI blocks.

For rendering message parts and citations, see [Message Parts and Rendering](/development/custom-ui/client-sdk/message-parts).

## 4. Send user responses

When the user replies to forms, approvals, or canvas requests, build metadata with `resolveUserMetadata` and send another message.

Include `taskId` when responding to an in progress task. Omit it when starting a new task.

```typescript
const metadata = await resolveUserMetadata({
form: { name: "Ada" },
approvalResponse: { decision: "approve" },
});

const responseStream = client.sendMessageStream({
message: {
kind: "message",
role: "user",
messageId: "message-id",
contextId: "context-id",
taskId,
parts: [{ kind: "text", text: "Approved" }],
metadata,
},
});

for await (const event of responseStream) {
if (event.kind === "status-update") {
// Handle follow up updates
}
}
```

For a focused look at composing messages, see [Message Building](/development/custom-ui/client-sdk/message-building).

## Cancel a task

The A2A client exposes cancellation by task ID.

```typescript
await client.cancelTask({ id: taskId });
```

## Error handling

When a status update fails, read error metadata from the error extension to show a user friendly message.

```typescript
import { errorExtension, extractUiExtensionData } from "agentstack-sdk";

const readError = extractUiExtensionData(errorExtension);
const errorMetadata = readError(event.status.message?.metadata);

if (errorMetadata) {
console.error(errorMetadata.message ?? "Agent error");
}
```

For more about error handling, see [Error Handling](/development/custom-ui/client-sdk/error-handling).
Loading