Portal Community

Route and Component

// Route in App.tsx
<Route path="/ancp" element={<AncpPage />} />

// AncpPage source: packages/app-pages-react/src/AncpPage.tsx
// Uses @bizfirst/ancp-react hooks + AncpProvider

Three-Panel Layout

AncpPage renders three stacked panels. Panels B and C appear only once a connection is established (Panel A).

PanelNamePurpose
ANode ConnectionEnter Base URL, Node ID, auth mode, and timeout. Click Connect to establish an ANCP session.
BNode InfoPing status, capability table (actions + patterns + pricing), and raw Discovery JSON viewer.
CAction InvokerSelect action + pattern, enter payload JSON, send and view the response or stream.

Panel A — Node Connection

// AncpClientOptions supplied to AncpProvider:
interface AncpClientOptions {
  baseUrl:  string;   // e.g. 'https://server2.bizfirst.internal'
  nodeId:   number;   // integer node ID
  auth?:    { jwtToken?: string } | { apiKey?: string } | { didProof?: string };
  timeout?: number;   // default 30000 ms
}

// Auth modes supported: None | JWT | ApiKey | DID
// Selecting a mode reveals a text field for the credential.

// Once Connect is clicked, AncpProvider wraps Panels B and C:
<AncpProvider options={ancpOptions}>
  <NodeInfoPanel />
  <ActionInvokerPanel />
</AncpProvider>

Panel B — Node Info

// Hooks used in NodeInfoPanel:
import {
  useAncpPing,          // fires ping on mount, returns { response, isLoading, isReachable, error }
  useAncpCapabilities,  // fetches capability list, returns { actions, isLoading, error, refresh }
  useAncpClient,        // raw ANCP client for custom calls
} from '@bizfirst/ancp-react';

// Ping response fields:
// { nodeId, tenantId, status, timestamp }

// Capability table columns:
// Action | Pattern | Description | Price (USDC)
// Each row is an AncpActionDescriptor:
interface AncpActionDescriptor {
  action:          string;           // e.g. 'get-order-status'
  pattern:         AncpMessagePattern;
  description?:    string;
  requiresPayment: boolean;
  priceUsdc?:      number;
}

// Discovery JSON: client.discover() fetches full capability document
const doc = await client.discover();  // shown in JsonViewer
ANCP vs EdgeStream The Node Explorer connects to ANCP nodes directly via REST/HTTP — it does not use the EdgeStream SignalR hub. ANCP nodes expose their own HTTP endpoint separate from the EdgeStream hub URL.

Panel C — Action Invoker (ANCP)

The Action Invoker in the ANCP panel supports all four ANCP message patterns. This is different from the Demo page Action Invoker, which sends EdgeStream messages. The ANCP Action Invoker sends directly to the node's HTTP endpoint.

// AncpMessagePattern type:
type AncpMessagePattern =
  | 'request-reply'    // synchronous: send + await response
  | 'fire-and-forget'  // async: POST returns 202 Accepted
  | 'streaming'        // chunked SSE stream: collect chunks until done
  | 'task-start';      // long-running: returns taskId, then poll status

// Hooks used per pattern:
const invokeHook = useAncpInvoke(actionName);  // request-reply
const streamHook = useAncpStream(actionName);  // streaming
const taskHook   = useAncpTask(actionName);    // task-start
// fire-and-forget: client.fireAndForget(action, payload)

// Pattern: request-reply
invokeHook.invoke(payload, { timeout: 5000 });
// Response shown in JsonViewer

// Pattern: streaming
streamHook.start(payload);
// streamHook.chunks[] grows as server sends chunks
// streamHook.isDone === true when stream ends

// Pattern: task-start
taskHook.start(payload);
// taskHook.taskId = 'task-abc-123'
// taskHook.status = { state: 'Running' | 'Completed' | 'Failed' | 'Cancelled', result?, error? }
// Poll: taskHook.refresh() (auto-polled every 1500ms while Running)
// Cancel: taskHook.cancel()

Streaming Log Display

// Chunk display format in the streaming log:
[0] {"type":"partial","text":"Hello"}
[1] {"type":"partial","text":" world"}
[2] [last] {"type":"done","text":""}

// Auto-scrolls as chunks arrive:
useEffect(() => {
  if (streamLogRef.current) {
    streamLogRef.current.scrollTop = streamLogRef.current.scrollHeight;
  }
}, [streamHook.chunks.length]);

Task Status Badges

StateBadge ColorMeaning
RunningBlueTask is executing — polling active every 1500ms (configurable)
CompletedGreenTask finished — result available in taskHook.status.result
FailedRedTask errored — message in taskHook.status.error
CancelledGrayTask was cancelled via Cancel Task button
Use Case: Debugging Agent Nodes The Node Explorer is the primary tool for verifying that an ANCP-enabled agent node is reachable, advertising the correct capabilities, and responding to actions with the right payload shapes — without writing any client code.