@abstraxn/agent-kit
v1.0.1
Published
Agent Kit SDK for creating and managing Web3 AI agents with Abstraxn server wallets
Readme
@abstraxn/agent-kit
Backend SDK for creating and managing Web3 AI agents with auto-provisioned Abstraxn server wallets.
Installation
npm install @abstraxn/agent-kitPrerequisites
- Node.js >= 18
- API Key from the Abstraxn Dashboard
Basic Integration
1. Initialize the SDK
import { AgentKitClient } from '@abstraxn/agent-kit';
const agentKit = new AgentKitClient({
apiKey: process.env.ABSTRAXN_API_KEY!,
wallet: 'server', // default — auto-provision Abstraxn server wallet
});2. Create an Agent (with auto-provisioned wallet)
This is the primary flow. The SDK will:
- Generate a P-256 access key pair
- Create a server wallet via Abstraxn
- Retrieve the wallet's EVM address
- Register the agent in the backend
const { agent, wallet } = await agentKit.createAgent({
name: 'Trading Assistant',
description: 'AI agent that executes trades within spend policy',
userIdentity: '[email protected]',
userName: 'John Doe', // optional
userEmail: '[email protected]', // optional
metadata: { role: 'trader' }, // optional
});
// ─── Agent Details ───
console.log('Agent ID:', agent.id);
console.log('Agent API Key:', agent.apiKey);
console.log('Is Active:', agent.isActive);
// ─── Wallet Details ───
console.log('EVM Address:', wallet.evmAddress);
console.log('Organization ID:', wallet.organizationId);
// ⚠️ IMPORTANT: Store these securely in your backend database.
// The accessKey is the ONLY way to sign transactions for this wallet.
// It CANNOT be retrieved again.
console.log('Access Key:', wallet.accessKey);
await saveToDatabase({
agentId: agent.id,
accessKey: wallet.accessKey!, // secret — encrypt at rest (server wallet only)
evmAddress: wallet.evmAddress,
organizationId: wallet.organizationId,
});2b. Create an Agent (external / bring-your-own wallet)
Use your own EVM or Solana address — no server wallet or accessKey is created:
const agentKit = new AgentKitClient({
apiKey: process.env.ABSTRAXN_API_KEY!,
wallet: 'external',
});
const { agent, wallet } = await agentKit.createAgent({
name: 'Trading Assistant',
description: 'Uses the user MetaMask wallet',
userIdentity: '[email protected]',
evmAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb', // required (or solanaAddress)
});
console.log(wallet.type); // 'external'
console.log(wallet.evmAddress); // saved on the agent record
// wallet.accessKey is undefined — sign txs with your own wallet stackYou can also override per call without changing client config:
const { agent, wallet } = await agentKit.createAgent({
wallet: 'external',
evmAddress: '0x...',
name: '...',
description: '...',
userIdentity: '...',
});createAgentDirect() is equivalent to external mode (POST /agents with addresses only).
3. List Agents
const { items, total } = await agentKit.listAgents({
offset: 0,
limit: 20,
});
console.log(`Total agents: ${total}`);
items.forEach((agent) => {
console.log(`${agent.name} — ${agent.evmAddress} — active: ${agent.isActive}`);
});4. Get Agent by ID
const agent = await agentKit.getAgent('agent-uuid-here');
console.log(agent.name, agent.evmAddress);5. Update Agent
const updated = await agentKit.updateAgent('agent-uuid-here', {
name: 'Updated Agent Name',
description: 'Updated description',
isActive: false,
});6. Set Spend Policy
// Enable a daily budget of $5
const agent = await agentKit.updateSpendPolicy('agent-uuid-here', {
enabled: true,
budgetUsd: '5.00',
period: 'daily',
hardBlock: true, // false = advisory only
});
// Disable spend policy
await agentKit.updateSpendPolicy('agent-uuid-here', {
enabled: false,
});Spend policy controls paid MCP tool budgets (x402). For on-chain interaction guardrails (contracts, recipients, amounts), use interaction policies below.
7. Interaction Policies (off-chain guardrails)
Configure per-agent rules enforced before every MCP tool call. All enabled policies must pass (AND). Enforcement is server-side — agents cannot bypass it.
Rule types (per-chain):
| Rule | Purpose |
|------|---------|
| contractWhitelist | Allow only listed contract/mint addresses |
| methodWhitelist | Allow only matching calldata patterns (* = hex wildcard) |
| recipientBlacklist | Block transfers to listed addresses |
| nativeAmountLimits | Min/max for native token amounts |
| tokenAmountLimits | Min/max per token symbol or contract |
// Create a policy
const policy = await agentKit.createInteractionPolicy('agent-uuid-here', {
name: 'transfer-guardrails',
enabled: true,
hardBlock: true,
rules: {
recipientBlacklist: [{
chain: 'ethereum',
addresses: ['0xBadAddress000000000000000000000000000001'],
}],
nativeAmountLimits: [{
chain: 'ethereum',
max: '0.5',
}],
contractWhitelist: [{
chain: 'ethereum',
addresses: ['0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'], // USDC
}],
methodWhitelist: [{
chain: 'ethereum',
contract: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
// Full calldata pattern — length must match; * = any hex digit
calldataPatterns: ['0xa9059cbb' + '*'.repeat(128)],
}],
tokenAmountLimits: [{
chain: 'ethereum',
token: 'USDC',
max: '1000',
}],
},
});
// List all policies for an agent
const { items, total } = await agentKit.listInteractionPolicies('agent-uuid-here');
// Get, update, or delete
const one = await agentKit.getInteractionPolicy('agent-uuid-here', policy.id);
await agentKit.updateInteractionPolicy('agent-uuid-here', policy.id, {
enabled: false,
});
await agentKit.deleteInteractionPolicy('agent-uuid-here', policy.id);Agent app integration: expose policy CRUD from your backend (not the browser) using your dashboard API key. Map your user → agent.id, then call the SDK methods above. See your agent app's policy UI → backend → SDK flow.
Auth: policies can be managed with your dashboard API key (all agents under your app) or the agent's own Kong API key (agent.apiKey from createAgent) for that agent only.
MCP violation: when a tool call breaks a policy, MCP returns JSON-RPC -32404 with data.interactionPolicy.violations[].
8. Delete Agent
await agentKit.deleteAgent('agent-uuid-here');9. Register ERC-8004 On-Chain Identity
Server wallet
After creating an agent and funding its server wallet with native gas on the target chain:
const identity = await agentKit.registerAgentIdentity({
agentId: agent.id,
userIdentity: '[email protected]',
accessKey: wallet.accessKey,
organizationId: wallet.organizationId,
evmAddress: wallet.evmAddress,
chainId: 11155111, // Sepolia — backend must have IdentityRegistry configured for this chain
});
console.log(identity.registration.agentIdentity);
// eip155:11155111:0x8004A818BFB912233c491871b3d84c89A494BD9e:42
const stored = await agentKit.getAgentIdentity(agent.id, 11155111);External wallet (bring-your-own signer)
Registry is a 3-step flow: prepare (backend) → sign (your wallet) → confirm (backend).
import { createWalletClient, http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { sepolia } from 'viem/chains';
// 1. Create agent with your address (no server wallet)
const { agent } = await agentKit.createAgent({
wallet: 'external',
evmAddress: process.env.DEV_EVM_ADDRESS!,
name: 'My Agent',
description: '...',
userIdentity: '[email protected]',
});
// 2. Register — you provide the signer
const identity = await agentKit.registerAgentIdentityExternal({
agentId: agent.id,
evmAddress: process.env.DEV_EVM_ADDRESS!,
chainId: 11155111,
signTransaction: async ({ prepare, fromAddress }) => {
const account = privateKeyToAccount(process.env.DEV_PRIVATE_KEY as `0x${string}`);
const walletClient = createWalletClient({
account,
chain: sepolia,
transport: http(prepare.rpcUrl),
});
const hash = await walletClient.sendTransaction({
account: fromAddress,
chain: sepolia,
to: prepare.registerCall.to as `0x${string}`,
data: prepare.registerCall.data,
value: BigInt(prepare.registerCall.value || '0'),
});
return hash;
},
});Manual steps (same APIs, full control):
const prepare = await agentKit.prepareAgentIdentity(agent.id, { chainId: 11155111 });
// sign prepare.registerCall with MetaMask / viem / ethers…
const confirmed = await agentKit.confirmAgentIdentity(agent.id, {
chainId: 11155111,
txHash: '0x…',
});Requirements for external registry:
- Agent
evmAddressmust match the account that signs (and holds gas on thatchainId). - Backend must have IdentityRegistry configured for the
chainId. registerAgentIdentity()(server) needsaccessKey+organizationId; external flow does not.
Registration file (public): GET {AGENT_KIT_PUBLIC_URL}/agents/{agentId}/registration
Signing Transactions with the Wallet
After creating an agent, use the stored accessKey to sign transactions via the ServerSignerClient:
const signer = agentKit.getServerSigner();
// Authenticate using the stored access key
const session = await signer.authenticate({
userIdentity: '[email protected]',
accessKey: storedAccessKey, // retrieved from your database
});
// Create a viem public client for EVM operations
const publicClient = signer.createPublicClient({
rpcUrl: 'https://rpc-amoy.polygon.technology',
chainId: 80002,
organizationId: storedOrganizationId,
fromAddress: storedEvmAddress as `0x${string}`,
});
// Send a transaction
const prepared = await publicClient.prepareTransaction({
to: '0x1111111111111111111111111111111111111111' as `0x${string}`,
value: 1_000_000_000_000_000n, // 0.001 ETH
});
const txHash = await publicClient.signAndSendPreparedTransaction(
prepared.unsignedTransaction,
);
const receipt = await publicClient.waitForTransactionReceipt(txHash);
console.log('Transaction confirmed:', receipt.transactionHash);
// Sign a message
const signature = await publicClient.signMessage({
message: 'Hello from my agent',
});Full Integration Example
A complete backend service that creates an agent and later uses it to send a transaction:
import { AgentKitClient } from '@abstraxn/agent-kit';
const agentKit = new AgentKitClient({
apiKey: process.env.ABSTRAXN_API_KEY!,
});
// ── Step 1: Create agent + wallet (run once, store results) ──
async function onboardAgent(userEmail: string) {
const { agent, wallet } = await agentKit.createAgent({
name: `Agent for ${userEmail}`,
description: 'Autonomous trading agent',
userIdentity: userEmail,
userEmail,
});
// Store in your database
return {
agentId: agent.id,
apiKey: agent.apiKey,
accessKey: wallet.accessKey, // ⚠️ encrypt at rest
evmAddress: wallet.evmAddress,
organizationId: wallet.organizationId,
};
}
// ── Step 2: Use the agent to sign & send transactions ──
async function executeAgentTrade(agentRecord: {
accessKey: string;
evmAddress: string;
organizationId: string;
userIdentity: string;
}) {
const signer = agentKit.getServerSigner();
await signer.authenticate({
userIdentity: agentRecord.userIdentity,
accessKey: agentRecord.accessKey,
});
const client = signer.createPublicClient({
rpcUrl: process.env.RPC_URL!,
chainId: Number(process.env.CHAIN_ID!),
organizationId: agentRecord.organizationId,
fromAddress: agentRecord.evmAddress as `0x${string}`,
});
const prepared = await client.prepareTransaction({
to: '0xRecipientAddress...' as `0x${string}`,
value: 500_000_000_000_000n,
});
const txHash = await client.signAndSendPreparedTransaction(
prepared.unsignedTransaction,
);
const receipt = await client.waitForTransactionReceipt(txHash);
return receipt;
}
// ── Step 3: Manage agents ──
async function manageAgents() {
// List all agents
const { items } = await agentKit.listAgents();
// Set spend limits
for (const agent of items) {
await agentKit.updateSpendPolicy(agent.id, {
enabled: true,
budgetUsd: '10.00',
period: 'daily',
hardBlock: true,
});
// Set interaction guardrails (optional)
await agentKit.createInteractionPolicy(agent.id, {
name: 'default-guardrails',
rules: {
nativeAmountLimits: [{ chain: 'ethereum', max: '1.0' }],
},
});
}
// Deactivate an agent
await agentKit.updateAgent(items[0].id, { isActive: false });
}Configuration
| Option | Required | Default | Description |
| ------------ | -------- | -------------------------------- | ---------------------------------------- |
| apiKey | Yes | — | API key from the Abstraxn dashboard |
| baseUrl | No | https://agent-kit.abstraxn.com | Agent Kit service base URL |
| retryCount | No | 1 | Number of retries for transient failures |
| fetch | No | Global fetch | Custom fetch implementation |
Error Handling
All errors extend AgentKitError with code and statusCode properties:
import {
AgentKitError,
ValidationError, // 400 — invalid input
BadRequestError, // 400 — server rejected request
UnauthorizedError, // 401 — invalid or missing API key
ForbiddenError, // 403 — insufficient permissions
NotFoundError, // 404 — agent not found
ConflictError, // 409 — duplicate resource
NetworkError, // network connectivity issue
} from '@abstraxn/agent-kit';
try {
await agentKit.createAgent({ ... });
} catch (error) {
if (error instanceof UnauthorizedError) {
console.error('Invalid API key — check your dashboard');
} else if (error instanceof NotFoundError) {
console.error('Agent not found');
} else if (error instanceof AgentKitError) {
console.error(`[${error.code}] ${error.message}`);
}
}Security Notes
- Store
accessKeyin a secure secrets manager or encrypted database column. - Never expose
accessKeyin frontend code or logs. - The
mcpToken(frombindAgent) is a one-time value — store it securely on first receipt. - Rotate API keys via the Abstraxn dashboard if compromised.
License
MIT
