Connect external agents to Upstreet NPCs via Google's Agent-to-Agent protocol for agent interoperability.
A2A (Agent-to-Agent) Integration
Every Upstreet NPC is exposed as an A2A-compatible agent—ready to chat, reason, and collaborate with external AI systems out of the box.
What is A2A?
Agent-to-Agent (A2A) is Google's open protocol for agent interoperability. It lets different AI agents discover each other, exchange structured messages, and invoke skills in a standardized way. Upstreet uses A2A so your NPCs can participate in multi-agent workflows, be controlled by external orchestrators, or serve as conversational interfaces from any compatible client.
A2A enables true agent-to-agent communication—not just human-to-agent. Your NPC can talk to other A2A agents, tools, and systems without custom integrations.
Endpoints
GET /api/a2a/{npcId}/.well-known/agent-card.json
Returns the agent card—a JSON document describing the NPC's capabilities, transport URLs, and authentication requirements. Use this for discovery before connecting.
POST /api/a2a/{npcId}/jsonrpc
The JSON-RPC transport endpoint. Send A2A messages here to converse with the NPC, invoke skills, and receive responses.
Authentication
Both endpoints require authentication. Pass your API key or Supabase JWT as a Bearer token:
Authorization: Bearer <your-token>You can only interact with NPCs you own. The token identifies the caller; the NPC must belong to your account.
Quick Start with @a2a-js/sdk
Install the SDK
npm install @a2a-js/sdkCreate a Client
import { ClientFactory } from '@a2a-js/sdk/client';
import type { MessageSendParams } from '@a2a-js/sdk';
const factory = new ClientFactory();
const npcId = 'your-npc-id';
const baseUrl = `https://upstreet.ai/api/a2a/${npcId}`;
const client = await factory.createFromUrl(baseUrl, undefined);Send a Message
const result = await client.sendMessage({
message: {
kind: 'message',
messageId: crypto.randomUUID(),
role: 'user',
parts: [{ kind: 'text', text: 'Hello! Who are you?' }],
},
configuration: {
blocking: true,
acceptedOutputModes: ['text/plain'],
},
});Full Code Example
import { ClientFactory } from '@a2a-js/sdk/client';
import type { MessageSendParams } from '@a2a-js/sdk';
const factory = new ClientFactory();
const npcId = 'your-npc-id';
const baseUrl = `https://upstreet.ai/api/a2a/${npcId}`;
const client = await factory.createFromUrl(baseUrl, undefined);
const result = await client.sendMessage({
message: {
kind: 'message',
messageId: crypto.randomUUID(),
role: 'user',
parts: [{ kind: 'text', text: 'Hello! Who are you?' }],
},
configuration: {
blocking: true,
acceptedOutputModes: ['text/plain'],
},
});You must pass your API key when creating the client—typically via fetch options or a custom transport that adds the Authorization: Bearer <token> header to requests.
Message Flow
sequenceDiagram
participant Client
participant A2A as Upstreet A2A API
participant NPC as NPC Engine
participant AI as AI (Mastra)
Client->>A2A: GET /.well-known/agent-card.json
A2A-->>Client: Agent card (name, skills, url)
Client->>A2A: POST /jsonrpc (sendMessage)
A2A->>NPC: Route to NPC by id
NPC->>AI: Generate response
AI-->>NPC: Reply text
NPC-->>A2A: A2A message (agent role)
A2A-->>Client: JSON-RPC responseAgent Card Structure
The agent card describes your NPC's capabilities and how to connect:
| Field | Description |
|---|---|
name | NPC display name |
description | Short description of the agent |
protocolVersion | A2A protocol version (e.g. 0.3.0) |
url | JSON-RPC transport URL |
preferredTransport | JSONRPC |
skills | Available skills (e.g. chat) |
capabilities | Streaming, push notifications, etc. |
defaultInputModes | ["text/plain"] |
defaultOutputModes | ["text/plain"] |
securitySchemes | bearerAuth (JWT) |
security | [{ bearerAuth: [] }] |