Getting Started with ANCP
ANCP (Agentic Node Communication Protocol) is the unified, polyglot communication framework for building AI-driven distributed systems. It connects AI agents, workflow nodes, microservices, and frontend applications through a single protocol with four messaging patterns and three authentication modes.
Platform requirement: ANCP is part of the BizFirstAi platform. You will need a BizFirstAi account to use the hosted node runtime. Sign up here.
Installation
Install both npm packages. @bizfirst/ancp-core provides the base client and types; @bizfirst/ancp-react provides the React hooks.
npm install @bizfirst/ancp-core @bizfirst/ancp-react
AncpProvider Setup
Wrap your React application root with AncpProvider. Every child component can then access ANCP hooks without importing configuration.
import { AncpProvider } from '@bizfirst/ancp-react';
export function App() {
return (
<AncpProvider
baseUrl="https://your-node.bizfirstai.com"
nodeId="my-frontend-node"
auth={{
mode: 'jwt',
token: () => getAuthToken(), // returns Promise<string>
}}
>
<YourApp />
</AncpProvider>
);
}
Your First Call — useAncpInvoke
Use useAncpInvoke for request-reply actions. The hook is generic over your payload and result types — TypeScript validates everything at compile time.
import { useAncpInvoke } from '@bizfirst/ancp-react';
interface SearchPayload { query: string; topK: number; }
interface SearchResult { items: string[]; total: number; }
export function SearchBox() {
const { invoke, isLoading, data, error, reset } =
useAncpInvoke<SearchPayload, SearchResult>('search.documents');
async function handleSearch(query: string) {
reset();
await invoke({ query, topK: 10 });
}
return (
<div>
<button onClick={() => handleSearch('agentic AI')} disabled={isLoading}>
{isLoading ? 'Searching...' : 'Search'}
</button>
{error && <p>Error: {error.message}</p>}
{data && <p>Found {data.total} results</p>}
</div>
);
}
Messaging Patterns
ANCP provides four messaging patterns. Choose the right pattern at the call site — the protocol and transport handle the rest.
| Pattern | Response | Best For | HTTP Status |
|---|---|---|---|
| Fire-and-Forget | None | Email, background jobs, events | 202 Accepted |
| Request-Reply | Typed result | Queries, validation, calculations | 200 OK |
| Streaming (SSE) | Chunk stream | LLM output, reports, exports | 200 text/event-stream |
| Task-Start | taskId | Batch jobs, long analysis | 202 + poll endpoint |
Streaming — useAncpStream
Use useAncpStream for SSE-backed actions such as LLM chat responses. The hook accumulates chunks in real time and exposes isStreaming and isDone state flags.
import { useAncpStream } from '@bizfirst/ancp-react';
interface ChatPayload { message: string; sessionId: string; }
interface TextChunk { token: string; }
export function ChatInterface() {
const { start, stop, chunks, isStreaming, isDone } =
useAncpStream<ChatPayload, TextChunk>('agent.chat');
const fullText = chunks.map(c => c.token).join('');
return (
<div>
<button
onClick={() => start({ message: 'Explain ANCP', sessionId: 'abc123' })}
disabled={isStreaming}
>
{isStreaming ? 'Generating...' : 'Ask the Agent'}
</button>
{isStreaming && <button onClick={stop}>Stop</button>}
<pre>{fullText}{isStreaming && '▌'}</pre>
{isDone && <p>Response complete.</p>}
</div>
);
}
C# Handler Registration
On the server side, use the BizFirst.Ancp.Node host to register handlers. Each handler pattern maps to one ANCP messaging pattern.
using BizFirst.Ancp.Node;
var ancp = new AncpNodeHost(options);
// Request-Reply — returns a typed result
Ancp.OnInvoke<SearchPayload, SearchResult>("search.documents", async ctx =>
{
var results = await searchService.QueryAsync(ctx.Payload.Query, ctx.Payload.TopK);
return new SearchResult { Items = results, Total = results.Count };
});
// Fire-and-Forget — returns 202, runs in background
Ancp.OnFireAndForget<EmailPayload>("email.send", async ctx =>
{
await emailService.SendAsync(ctx.Payload.To, ctx.Payload.Subject, ctx.Payload.Body);
});
// Streaming — yields chunks as they are produced
Ancp.OnStream<ChatPayload, TextChunk>("agent.chat", async ctx =>
{
await foreach (var token in llmService.StreamAsync(ctx.Payload.Message))
{
yield return new TextChunk { Token = token };
}
});
// Task-Start — returns taskId, tracks Running/Completed/Failed/Cancelled
Ancp.OnTask<BatchPayload, BatchResult>("batch.analyse", async (ctx, ct) =>
{
var result = await batchService.RunAsync(ctx.Payload.DataSetId, ct);
return new BatchResult { ProcessedCount = result.Count };
});
await ancp.StartAsync();
AncpActionContext
Every handler receives a fully populated AncpActionContext<T>. Use it to access the caller identity, tenant context, and correlation data without custom middleware.
Payload— the typed request payloadNodeId— the receiving node's identifierCallerNodeId— the sending node's identifierTenantId— the receiving tenantCallerTenantId— the calling tenantMessageId— unique message identifierCorrelationId— distributed trace correlation IDTimestamp— message creation time (UTC)Extensions— domain-specific metadata dictionary
Authentication Modes
ANCP supports three authentication modes. Select the mode that fits your deployment security model.
| Mode | Description | When to Use |
|---|---|---|
| JWT | Bearer token via Authorization header. Standard OIDC flows supported. | User-facing apps, SSO environments, any identity provider |
| API Key | Static secret passed as a header. Simple and fast. | Service-to-service calls, CI pipelines, internal integrations |
| DID | Decentralised ID — cryptographic identity for autonomous agents. | Cross-org federation, agent-to-agent calls, Phase 3 marketplace |
LLM Providers
Configure the LLM provider directly on the node host. Per-model cost tracking is automatic — input and output token costs are stored per call.
- Anthropic Claude: claude-3-5-sonnet-20241022, claude-3-opus-20250219, claude-3-sonnet-20240229, claude-3-haiku-20240307, claude-2.1
- OpenAI: gpt-4o, gpt-4-turbo, gpt-4, gpt-3.5-turbo
- Ollama (self-hosted): llama2, mistral, neural-chat, any locally served model
- HuggingFace: any inference endpoint
- Custom: any OpenAI-compatible HTTP endpoint
Architecture Layers
ANCP is structured across three layers. Every message flows through all three on both the client and server sides.
| Layer | Component | Responsibility |
|---|---|---|
| Layer 3 | Client Applications | React apps via ancp-react hooks, chat interfaces, agent dashboards |
| Layer 2 | ANCP Transport | HTTP/HTTPS, POST /invoke, SSE streaming, GET /.well-known/ncp.json discovery |
| Layer 1 | ANCP Node Services | BizFirst.Ancp.Node C# host, AI Agent Nodes, LLM services, RAG services, Tool services |
| Foundation | IEnvelope | Message format, tenant context, payment tracking, audit logs |
Discovery Endpoint
Every running ANCP node exposes a machine-readable capability document at a well-known URL. Query it with a standard HTTP GET — no authentication required for the discovery endpoint.
GET https://your-node.bizfirstai.com/.well-known/ncp.json
# Returns AncpDiscoveryDocument:
# {
# "nodeId": "my-agent-node",
# "version": "0.9.0",
# "actions": [
# { "name": "search.documents", "pattern": "request-reply" },
# { "name": "agent.chat", "pattern": "streaming" },
# { "name": "batch.analyse", "pattern": "task-start" },
# { "name": "email.send", "pattern": "fire-and-forget" },
# { "name": "ping", "pattern": "request-reply" },
# { "name": "getCapabilities", "pattern": "request-reply" }
# ]
# }
Next Steps
Ready to go further? Explore these resources: