| Sample | Stack | Role |
|---|---|---|
| Sample backend API | NestJS, Postgres | Your server: users, DB, @abstraxn/agent-kit, MCP + LLM |
| Sample web app | Next.js | Your UI: login, agents, chat, portfolio |
What you are building
| Layer | Your project | Responsibility |
|---|---|---|
| Dashboard | Abstraxn UI | API key, MCP URL, monitor agents |
| Backend API | Sample backend API (NestJS) | Users, DB, @abstraxn/agent-kit, MCP + LLM |
| Web app | Sample web app (Next.js) | Login, pick agent, chat, portfolio |
| Infrastructure | Agent Kit host | REST + MCP + x402 |
Prerequisites
- Dashboard setup — application API key and MCP URL.
- SDK quickstart — understand
createAgentandaccessKey. - Postgres (or adapt persistence) for the sample service.
Part 1 — Sample backend API (NestJS)
Install and configure
| Variable | Example | Purpose |
|---|---|---|
AGENT_KIT_API_KEY | From dashboard Overview | AgentKitClient + MCP auth |
AGENT_KIT_BASE_URL | https://dev-agent-kit.abstraxn.com | REST base |
MCP_SERVER_URL | https://dev-agent-kit.abstraxn.com/mcp | Same as dashboard MCP URL |
MCP_SERVER_AUTH_TOKEN | Same as AGENT_KIT_API_KEY | MCP Authorization header |
ENCRYPTION_KEY | Long random secret | Encrypt wallet.accessKey at rest |
LLM_API_KEY / LLM_BASE_URL / LLM_MODEL | OpenRouter, etc. | Chat model |
JWT_SECRET | Random | End-user sessions |
POSTGRES_* | Local DB | Agents + chat tables |
Create agent — SDK + server wallet
When the UI callsPOST /agents, AgentsService.create:
- Calls
agentKitClient.createAgent({ name, description, userIdentity: userId }). - Receives
agent(Kit id, per-agentapiKey) andwallet(evmAddress,organizationId,accessKey). - Encrypts
wallet.accessKeywithENCRYPTION_KEYand saves to Postgres. - Never returns
accessKeyin API responses.
- Map
userIdentityto your stable user id (the sample usesuserId). - Fund the agent EVM address with native gas before transfers or x402.
- Store
agentId(Kit UUID) for MCPagent_idin tool arguments.
REST surface (your API, JWT)
| Method | Path | Behavior |
|---|---|---|
POST | /agents | createAgent via SDK |
GET | /agents | List agents for logged-in user |
GET | /agents/:id | Agent detail (no accessKey) |
PATCH | /agents/:id | Update name / prompt |
POST | /agents/:id/register-identity | Body { "chainId": … } — supported chain IDs (1, 11155111, 137, 80002, 8453, 84532, 56, 97, 42161, 43114) |
GET | /agents/:id/identity?chainId= | Read registration for one supported chain |
GET | /agents/assets | Multi-chain balances (Tatum) |
POST | /chat/sessions | New session for agentId |
POST | /chat/sessions/:id/messages | SSE stream; runs LLM + MCP tools |
MCP + LLM chat loop
McpClientService (on module init when MCP_SERVER_URL is set):
initialize+notifications/initializedtools/list→ cache tool schemas- Expose tools to the LLM as OpenAI functions
ChatService.sendMessageStreaming:
- Loads session messages.
- Calls LLM with tool definitions.
- On tool call →
McpClientService.callTool(name, args, paymentPayload?). - For application key, include
agent_id: session.agentIdin args (Kit UUID from DB). - Streams assistant text to the client over SSE.
src/chat/mcp-client.service.tssrc/chat/chat.service.tssrc/chat/llm.service.ts
Transfer auto-sign (unsigned MCP → broadcast)
When the LLM calls MCPtransfer, the tool returns an unsigned transaction. The sample app:
- Detects transfer metadata in the stream (
transfer_confirmation_ui/ pending transfer flow). - Asks the user to confirm in the UI.
AgentSigningServiceloads decryptedaccessKey, authenticates server signer, signs and sends via viem.
src/agents/agent-signing.service.ts and src/agents/pending-transfer.service.ts.
x402 in chat (paid_fetch, get_token_price)
When MCP returns payment required (-32402 or paymentRequired):
AgentSigningService.createX402PaymentPayloadForAgentbuildspaymentPayloadusing the agent’s access key.- Chat layer retries
tools/callwith top-levelpaymentPayload.
src/chat/mcp-x402.util.ts.
Part 2 — Sample web app (Next.js)
Install and configure
lib/constants.ts:
Auth
- Email OTP or Google OAuth →
auth-storeJWT. lib/api.tsaddsAuthorization: Beareron every request to your API only.
Agents UI
| File | Role |
|---|---|
components/providers/agents-provider.tsx | Load/create agents via /agents |
app/dashboard/page.tsx | Agent picker + chat entry |
stores/agents-store.ts | Selected agent persistence |
POST /agents on your backend → SDK createAgent → dashboard All agents updates on refresh.
Chat (BFF)
The browser never calls Agent Kit or the LLM directly.- UI posts to Next.js
app/api/chat/route.tswith Bearer JWT. - Route proxies to
POST /chat/sessions/:sessionId/messageson the backend with SSE. lib/chat/create-chat-session.tscreates sessions via your API.
Portfolio and ERC-8004 identity
components/portfolio/— balances from/agents/assetsRegisterIdentityModal— user picks a supportedchainId; your API posts{ "chainId": … }to/agents/:id/register-identity(full list)RegisterIdentityFundRequiredModal— prompts to fund the agent EVM wallet when gas is insufficientAgentIdentityDetails— global id, on-chain agent id, registry, owner, explorer linksuseAgentIdentity/lib/fetch-agent-identity.ts— load identity from your backend (proxies Agent Kit)
@abstraxn/agent-kit registerAgentIdentity (or Kit REST prepare/confirm). The browser never signs registration transactions directly.
Full standard and dashboard parity: ERC-8004 agent identity.
Transaction history
hooks/use-agent-transactions.ts— on-chain transfer history helpers
Part 3 — Map dashboard UI to your code
| Dashboard screen | Your implementation |
|---|---|
| Overview API key | AGENT_KIT_API_KEY, MCP_SERVER_AUTH_TOKEN |
| Overview MCP URL | MCP_SERVER_URL |
| All agents table | Rows from your DB after createAgent |
| Identity tab | metadata.erc8004 after registerAgentIdentity |
| Activity log tab | MCP tool call logs for that agent |
| Tools catalog | Same tools McpClientService discovers |
Security model
Secrets stay on your backend API
Secrets stay on your backend API
Application API key, MCP token,
ENCRYPTION_KEY, and LLM keys live only in backend env. The web app holds the user JWT only.accessKey is the signing root
accessKey is the signing root
Losing
accessKey means you cannot sign transfers or x402 for that agent. Encrypt the DB column; never expose in GET /agents/:id.Do not call MCP from the browser
Do not call MCP from the browser
Always proxy tool execution through your backend so keys and
paymentPayload signing stay server-side.Customize for production
| Sample choice | Alternative |
|---|---|
userIdentity: userId | Email, external id |
| OpenRouter | Azure OpenAI, Anthropic via compatible proxy |
| Tatum balances | Your indexer |
Next.js /api/chat BFF | SSE from API + CORS |
Single AGENT_KIT_API_KEY | Per-agent apiKey for MCP |