TypeScript Agent Examples
This guide provides complete, runnable examples for building TraceMem agents in TypeScript. Each example demonstrates a specific pattern for interacting with TraceMem's Agent MCP server.
Prerequisites
- Node.js 18 or higher (for built-in fetch support, or use axios)
- TypeScript 5.3 or higher
axioslibrary:npm install axiostsxfor running TypeScript directly:npm install -D tsx- Access to a TraceMem Agent MCP server (default:
https://mcp.tracemem.com) - A valid TraceMem API key
Connection Details
- Endpoint:
https://mcp.tracemem.com(or set viaMCP_AGENT_URLenvironment variable) - Protocol: JSON-RPC 2.0 over HTTP
- Authentication:
Authorization: Agent <your-api-key>header
MCP Client Implementation
First, let's create a reusable MCP client class that handles JSON-RPC 2.0 communication:
import axios, { AxiosInstance } from 'axios';
/**
* JSON-RPC 2.0 request structure
*/
interface JsonRpcRequest {
jsonrpc: '2.0';
id: number;
method: string;
params?: Record<string, any>;
}
/**
* JSON-RPC 2.0 response structure
*/
interface JsonRpcResponse {
jsonrpc: '2.0';
id: number;
result?: any;
error?: {
code: number;
message: string;
data?: any;
};
}
/**
* MCP tool call result content
*/
interface ToolContent {
type: string;
text?: string;
[key: string]: any;
}
/**
* MCP tool call result
*/
interface ToolCallResult {
content: ToolContent[];
isError?: boolean;
}
/**
* MCP Client for interacting with TraceMem Agent MCP Server
*/
export class MCPClient {
private baseUrl: string;
private apiKey: string;
private httpClient: AxiosInstance;
private requestId: number = 0;
private initialized: boolean = false;
constructor(baseUrl: string, apiKey: string) {
this.baseUrl = baseUrl;
this.apiKey = apiKey;
this.httpClient = axios.create({
baseURL: baseUrl,
headers: {
'Authorization': `Agent ${apiKey}`,
'Content-Type': 'application/json',
},
});
}
/**
* Get next request ID
*/
private nextId(): number {
this.requestId += 1;
return this.requestId;
}
/**
* Make a JSON-RPC call to MCP server
*/
private async call(method: string, params?: Record<string, any>): Promise<any> {
const request: JsonRpcRequest = {
jsonrpc: '2.0',
id: this.nextId(),
method,
params: params || {},
};
try {
const response = await this.httpClient.post<JsonRpcResponse>('', request);
const result = response.data;
if (result.error) {
const error = result.error;
throw new Error(`MCP Error ${error.code}: ${error.message}${error.data ? ` - ${JSON.stringify(error.data)}` : ''}`);
}
return result.result || {};
} catch (error: any) {
if (error.response) {
const errorData = error.response.data;
if (errorData?.error) {
throw new Error(`MCP Error ${errorData.error.code}: ${errorData.error.message}`);
}
}
throw new Error(`HTTP Error: ${error.message}`);
}
}
/**
* Initialize MCP session
*/
async initialize(): Promise<any> {
const result = await this.call('initialize', {
protocolVersion: '2024-11-05',
capabilities: {},
clientInfo: {
name: 'tracemem-test-agent',
version: '1.0.0',
},
});
this.initialized = true;
return result;
}
/**
* List available tools
*/
async listTools(): Promise<any> {
return await this.call('tools/list');
}
/**
* Call a tool
*/
async callTool(name: string, arguments_: Record<string, any>): Promise<any> {
if (!this.initialized) {
await this.initialize();
}
const result = await this.call('tools/call', {
name,
arguments: arguments_,
});
// Check if the tool result indicates an error
if (result.isError) {
// Extract error message from content
let errorMessage = 'Tool call failed';
if (result.content && Array.isArray(result.content) && result.content.length > 0) {
const content = result.content[0];
if (content.type === 'text' && content.text) {
errorMessage = content.text;
}
}
throw new Error(errorMessage);
}
// Parse content from tool result
if (result.content && Array.isArray(result.content) && result.content.length > 0) {
const content = result.content[0];
if (content.type === 'text' && content.text) {
// Try to parse as JSON
try {
return JSON.parse(content.text);
} catch (e) {
// If not JSON, return as text
return { raw_text: content.text };
}
}
}
return result;
}
/**
* Check if client is initialized
*/
isInitialized(): boolean {
return this.initialized;
}
}
Example 1: Read Agent
This example demonstrates reading customer data from a data product.
#!/usr/bin/env tsx
/**
* Read Test Agent
*
* Tests the read operation on planetscale_read_customer_v1 data product.
* This agent:
* 1. Creates a decision envelope
* 2. Reads customer data using decision_read
* 3. Closes the decision envelope
*/
import { MCPClient } from './mcp-client.js';
async function runReadTest() {
// Get configuration from environment
const mcpUrl = process.env.MCP_AGENT_URL || 'https://mcp.tracemem.com';
const apiKey = process.env.TRACEMEM_API_KEY;
const instance = process.env.TRACEMEM_INSTANCE;
const actor = process.env.TRACEMEM_ACTOR || 'test-read-agent';
const customerId = process.env.CUSTOMER_ID || '1003';
if (!apiKey) {
console.error('ERROR: TRACEMEM_API_KEY environment variable is required');
process.exit(1);
}
console.log('='.repeat(60));
console.log('TraceMem MCP - Read Test Agent');
console.log('='.repeat(60));
console.log();
console.log(`Connecting to Agent MCP at: ${mcpUrl}`);
if (instance) console.log(`Instance: ${instance}`);
console.log(`Actor: ${actor}`);
console.log(`Customer ID: ${customerId}`);
console.log();
const client = new MCPClient(mcpUrl, apiKey);
let decisionId: string | null = null;
try {
// Initialize MCP session
console.log('Initializing MCP session...');
const initResult = await client.initialize();
console.log(`✓ Connected to ${initResult.serverInfo?.name || 'TraceMem Agent MCP'}`);
console.log();
// Step 1: Create decision envelope
console.log('Step 1: Creating decision envelope...');
const decision = await client.callTool('decision_create', {
intent: 'test.read.customer',
automation_mode: 'autonomous',
instance: instance,
actor: actor,
metadata: {
customer_id: customerId,
test_type: 'read',
},
});
decisionId = decision.decision_id || decision.id;
if (!decisionId) {
throw new Error('Failed to get decision_id from decision_create response');
}
console.log(`✓ Decision envelope created: ${decisionId}`);
console.log();
// Step 2: Read customer data
console.log('Step 2: Reading customer data...');
const readResult = await client.callTool('decision_read', {
decision_id: decisionId,
product: 'planetscale_read_customer_v1',
purpose: 'web_order',
query: {
id: parseInt(customerId, 10),
},
});
console.log('✓ Customer data retrieved');
if (readResult.event_id) {
console.log(` Event ID: ${readResult.event_id}`);
}
if (readResult.data_ref) {
console.log(` Data Reference: ${readResult.data_ref}`);
}
if (readResult.records && readResult.records.length > 0) {
console.log(` Records found: ${readResult.records.length}`);
console.log(' Customer data:');
console.log(JSON.stringify(readResult.records[0], null, 2));
} else {
console.log(' No records found');
}
console.log();
// Step 3: Close decision (commit)
console.log('Step 3: Committing decision...');
const closeResult = await client.callTool('decision_close', {
decision_id: decisionId,
action: 'commit',
});
console.log(`✓ Decision committed`);
if (closeResult.status) {
console.log(` Status: ${closeResult.status}`);
}
console.log();
// Summary
console.log('='.repeat(60));
console.log('Summary');
console.log('='.repeat(60));
console.log(`Decision ID: ${decisionId}`);
console.log('Result: ✓ Read operation completed successfully');
console.log();
} catch (error: any) {
console.error(`✗ Error: ${error.message}`);
if (error.stack) {
console.error(error.stack);
}
// Try to close decision on error
if (decisionId) {
try {
console.log('Attempting to abort decision...');
await client.callTool('decision_close', {
decision_id: decisionId,
action: 'abort',
reason: `Error occurred: ${error.message}`,
});
console.log('✓ Decision aborted');
} catch (closeError: any) {
console.error(`Failed to abort decision: ${closeError.message}`);
}
}
process.exit(1);
}
}
// Run the test
runReadTest().catch((error) => {
console.error('Fatal error:', error);
process.exit(1);
});
Environment Variables
TRACEMEM_API_KEY(required): Your TraceMem agent API keyMCP_AGENT_URL(optional): MCP server URL (default:https://mcp.tracemem.com)TRACEMEM_INSTANCE(optional): Instance identifierTRACEMEM_ACTOR(optional): Actor identifier (default:test-read-agent)CUSTOMER_ID(optional): Customer ID to read (default:1003)
Running the Example
export TRACEMEM_API_KEY="your-api-key"
export CUSTOMER_ID="1"
tsx src/test-read-agent.ts
Example 2: Insert Agent (with Policy)
This example demonstrates inserting an order with policy evaluation.
#!/usr/bin/env tsx
/**
* Insert Test Agent
*
* Tests the insert operation on planetscale_insert_order_v1 data product.
* This agent:
* 1. Creates a decision envelope
* 2. Evaluates discount_cap_v1 policy
* 3. Inserts an order using decision_write with operation: "insert"
* 4. Closes the decision envelope
*/
import { MCPClient } from './mcp-client.js';
async function runInsertTest() {
// Get configuration from environment
const mcpUrl = process.env.MCP_AGENT_URL || 'https://mcp.tracemem.com';
const apiKey = process.env.TRACEMEM_API_KEY;
const instance = process.env.TRACEMEM_INSTANCE;
const actor = process.env.TRACEMEM_ACTOR || 'test-insert-agent';
// Test data - can be overridden via environment variables
const customerId = parseInt(process.env.CUSTOMER_ID || '1001', 10);
const productId = parseInt(process.env.PRODUCT_ID || '2', 10);
const quantity = parseInt(process.env.QUANTITY || '1', 10);
const totalAmount = parseFloat(process.env.TOTAL_AMOUNT || '99.99');
const orderStatus = process.env.ORDER_STATUS || 'pending';
const proposedDiscount = parseFloat(process.env.PROPOSED_DISCOUNT || '0');
if (!apiKey) {
console.error('ERROR: TRACEMEM_API_KEY environment variable is required');
process.exit(1);
}
console.log('='.repeat(60));
console.log('TraceMem MCP - Insert Test Agent');
console.log('='.repeat(60));
console.log();
console.log(`Connecting to Agent MCP at: ${mcpUrl}`);
if (instance) console.log(`Instance: ${instance}`);
console.log(`Actor: ${actor}`);
console.log(`Order Data:`);
console.log(` Customer ID: ${customerId}`);
console.log(` Product ID: ${productId}`);
console.log(` Quantity: ${quantity}`);
console.log(` Total Amount: ${totalAmount}`);
console.log(` Order Status: ${orderStatus}`);
console.log(` Proposed Discount: ${proposedDiscount}`);
console.log();
const client = new MCPClient(mcpUrl, apiKey);
let decisionId: string | null = null;
try {
// Initialize MCP session
console.log('Initializing MCP session...');
const initResult = await client.initialize();
console.log(`✓ Connected to ${initResult.serverInfo?.name || 'TraceMem Agent MCP'}`);
console.log();
// Step 1: Create decision envelope
console.log('Step 1: Creating decision envelope...');
const decision = await client.callTool('decision_create', {
intent: 'test.insert.order',
automation_mode: 'autonomous',
instance: instance,
actor: actor,
metadata: {
customer_id: customerId.toString(),
product_id: productId.toString(),
test_type: 'insert',
},
});
decisionId = decision.decision_id || decision.id;
if (!decisionId) {
throw new Error('Failed to get decision_id from decision_create response');
}
console.log(`✓ Decision envelope created: ${decisionId}`);
console.log();
// Step 2: Evaluate policy
console.log('Step 2: Evaluating discount_cap_v1 policy...');
const policyResult = await client.callTool('decision_evaluate', {
decision_id: decisionId,
policy_id: 'discount_cap_v1',
inputs: {
proposed_discount: proposedDiscount,
},
});
const outcome = policyResult.outcome || 'unknown';
console.log(`✓ Policy evaluation completed`);
console.log(` Policy ID: discount_cap_v1`);
console.log(` Proposed Discount: ${proposedDiscount}`);
console.log(` Outcome: ${outcome}`);
if (policyResult.rationale) {
const rationaleMessage = typeof policyResult.rationale === 'object'
? policyResult.rationale.message || JSON.stringify(policyResult.rationale)
: policyResult.rationale;
console.log(` Rationale: ${rationaleMessage}`);
}
if (policyResult.event_id) {
console.log(` Event ID: ${policyResult.event_id}`);
}
console.log();
// Handle policy outcomes
if (outcome === 'deny') {
throw new Error(`Policy evaluation denied the operation. Rationale: ${policyResult.rationale?.message || 'No rationale provided'}`);
} else if (outcome === 'requires_exception') {
console.log('⚠ Policy requires exception/approval, but continuing with test...');
console.log();
} else if (outcome !== 'allow') {
console.log(`⚠ Unexpected policy outcome: ${outcome}, continuing with test...`);
console.log();
}
// Step 3: Insert order
console.log('Step 3: Inserting order...');
const writeResult = await client.callTool('decision_write', {
decision_id: decisionId,
product: 'planetscale_insert_order_v1',
purpose: 'web_order',
mutation: {
operation: 'insert',
records: [{
customer_id: customerId,
product_id: productId,
quantity: quantity,
total_amount: totalAmount,
order_status: orderStatus,
}],
},
});
console.log('✓ Order inserted');
if (writeResult.event_id) {
console.log(` Event ID: ${writeResult.event_id}`);
}
if (writeResult.status) {
console.log(` Status: ${writeResult.status}`);
}
if (writeResult.created_records && writeResult.created_records.length > 0) {
console.log(` Created ${writeResult.created_records.length} record(s):`);
writeResult.created_records.forEach((record: any, index: number) => {
console.log(` Record ${index + 1}:`);
console.log(JSON.stringify(record, null, 2));
});
}
if (writeResult.mutation_summary) {
console.log(` Mutation Summary: ${JSON.stringify(writeResult.mutation_summary)}`);
}
console.log();
// Step 4: Close decision (commit)
console.log('Step 4: Committing decision...');
const closeResult = await client.callTool('decision_close', {
decision_id: decisionId,
action: 'commit',
});
console.log(`✓ Decision committed`);
if (closeResult.status) {
console.log(` Status: ${closeResult.status}`);
}
console.log();
// Summary
console.log('='.repeat(60));
console.log('Summary');
console.log('='.repeat(60));
console.log(`Decision ID: ${decisionId}`);
console.log('Result: ✓ Insert operation completed successfully');
if (writeResult.created_records && writeResult.created_records.length > 0) {
const firstRecord = writeResult.created_records[0];
if (firstRecord.order_id) {
console.log(`Created Order ID: ${firstRecord.order_id}`);
}
}
console.log();
} catch (error: any) {
console.error(`✗ Error: ${error.message}`);
if (error.stack) {
console.error(error.stack);
}
// Try to close decision on error
if (decisionId) {
try {
console.log('Attempting to abort decision...');
await client.callTool('decision_close', {
decision_id: decisionId,
action: 'abort',
reason: `Error occurred: ${error.message}`,
});
console.log('✓ Decision aborted');
} catch (closeError: any) {
console.error(`Failed to abort decision: ${closeError.message}`);
}
}
process.exit(1);
}
}
// Run the test
runInsertTest().catch((error) => {
console.error('Fatal error:', error);
process.exit(1);
});
Environment Variables
TRACEMEM_API_KEY(required): Your TraceMem agent API keyMCP_AGENT_URL(optional): MCP server URL (default:https://mcp.tracemem.com)TRACEMEM_INSTANCE(optional): Instance identifierTRACEMEM_ACTOR(optional): Actor identifier (default:test-insert-agent)CUSTOMER_ID(optional): Customer ID for order (default:1001)PRODUCT_ID(optional): Product ID for order (default:2)QUANTITY(optional): Order quantity (default:1)TOTAL_AMOUNT(optional): Order total amount (default:99.99)ORDER_STATUS(optional): Order status (default:pending)PROPOSED_DISCOUNT(optional): Proposed discount for policy evaluation (default:0)
Example 3: Insert Agent (without Policy)
This example demonstrates inserting an order without policy evaluation.
#!/usr/bin/env tsx
/**
* Insert Test Agent
*
* Tests the insert operation on planetscale_insert_order_no_policy_v1 data product.
* This agent:
* 1. Creates a decision envelope
* 2. Inserts an order using decision_write with operation: "insert"
* 3. Closes the decision envelope
*/
import { MCPClient } from './mcp-client.js';
async function runInsertTest() {
// Get configuration from environment
const mcpUrl = process.env.MCP_AGENT_URL || 'https://mcp.tracemem.com';
const apiKey = process.env.TRACEMEM_API_KEY;
const instance = process.env.TRACEMEM_INSTANCE;
const actor = process.env.TRACEMEM_ACTOR || 'test-insert-agent';
// Test data - can be overridden via environment variables
const customerId = parseInt(process.env.CUSTOMER_ID || '1002', 10);
const productId = parseInt(process.env.PRODUCT_ID || '4', 10);
const quantity = parseInt(process.env.QUANTITY || '1', 10);
const totalAmount = parseFloat(process.env.TOTAL_AMOUNT || '99.99');
const orderStatus = process.env.ORDER_STATUS || 'pending';
if (!apiKey) {
console.error('ERROR: TRACEMEM_API_KEY environment variable is required');
process.exit(1);
}
console.log('='.repeat(60));
console.log('TraceMem MCP - Insert Test Agent');
console.log('='.repeat(60));
console.log();
console.log(`Connecting to Agent MCP at: ${mcpUrl}`);
if (instance) console.log(`Instance: ${instance}`);
console.log(`Actor: ${actor}`);
console.log(`Order Data:`);
console.log(` Customer ID: ${customerId}`);
console.log(` Product ID: ${productId}`);
console.log(` Quantity: ${quantity}`);
console.log(` Total Amount: ${totalAmount}`);
console.log(` Order Status: ${orderStatus}`);
console.log();
const client = new MCPClient(mcpUrl, apiKey);
let decisionId: string | null = null;
try {
// Initialize MCP session
console.log('Initializing MCP session...');
const initResult = await client.initialize();
console.log(`✓ Connected to ${initResult.serverInfo?.name || 'TraceMem Agent MCP'}`);
console.log();
// Step 1: Create decision envelope
console.log('Step 1: Creating decision envelope...');
const decision = await client.callTool('decision_create', {
intent: 'test.insert.order',
automation_mode: 'autonomous',
instance: instance,
actor: actor,
metadata: {
customer_id: customerId.toString(),
product_id: productId.toString(),
test_type: 'insert',
},
});
decisionId = decision.decision_id || decision.id;
if (!decisionId) {
throw new Error('Failed to get decision_id from decision_create response');
}
console.log(`✓ Decision envelope created: ${decisionId}`);
console.log();
// Step 2: Insert order
console.log('Step 2: Inserting order...');
const writeResult = await client.callTool('decision_write', {
decision_id: decisionId,
product: 'planetscale_insert_order_no_policy_v1',
purpose: 'web_order',
mutation: {
operation: 'insert',
records: [{
customer_id: customerId,
product_id: productId,
quantity: quantity,
total_amount: totalAmount,
order_status: orderStatus,
}],
},
});
console.log('✓ Order inserted');
if (writeResult.event_id) {
console.log(` Event ID: ${writeResult.event_id}`);
}
if (writeResult.status) {
console.log(` Status: ${writeResult.status}`);
}
if (writeResult.created_records && writeResult.created_records.length > 0) {
console.log(` Created ${writeResult.created_records.length} record(s):`);
writeResult.created_records.forEach((record: any, index: number) => {
console.log(` Record ${index + 1}:`);
// Display key fields prominently
if (record.order_id !== undefined) {
console.log(` Order ID: ${record.order_id}`);
}
if (record.customer_id !== undefined) {
console.log(` Customer ID: ${record.customer_id}`);
}
if (record.product_id !== undefined) {
console.log(` Product ID: ${record.product_id}`);
}
if (record.quantity !== undefined) {
console.log(` Quantity: ${record.quantity}`);
}
if (record.total_amount !== undefined) {
console.log(` Total Amount: ${record.total_amount}`);
}
if (record.order_status !== undefined) {
console.log(` Order Status: ${record.order_status}`);
}
if (record.order_date !== undefined) {
console.log(` Order Date: ${record.order_date}`);
}
// Display full record as JSON for complete details
console.log(` Full Record:`);
console.log(JSON.stringify(record, null, 4));
});
} else {
console.log(' ⚠ No created records returned (check if return_created is enabled)');
}
if (writeResult.mutation_summary) {
console.log(` Mutation Summary: ${JSON.stringify(writeResult.mutation_summary)}`);
}
console.log();
// Step 3: Close decision (commit)
console.log('Step 3: Committing decision...');
const closeResult = await client.callTool('decision_close', {
decision_id: decisionId,
action: 'commit',
});
console.log(`✓ Decision committed`);
if (closeResult.status) {
console.log(` Status: ${closeResult.status}`);
}
console.log();
// Summary
console.log('='.repeat(60));
console.log('Summary');
console.log('='.repeat(60));
console.log(`Decision ID: ${decisionId}`);
console.log('Result: ✓ Insert operation completed successfully');
if (writeResult.created_records && writeResult.created_records.length > 0) {
const firstRecord = writeResult.created_records[0];
console.log(`Created Record:`);
if (firstRecord.order_id !== undefined) {
console.log(` Order ID: ${firstRecord.order_id}`);
}
if (firstRecord.customer_id !== undefined) {
console.log(` Customer ID: ${firstRecord.customer_id}`);
}
if (firstRecord.product_id !== undefined) {
console.log(` Product ID: ${firstRecord.product_id}`);
}
if (firstRecord.quantity !== undefined) {
console.log(` Quantity: ${firstRecord.quantity}`);
}
if (firstRecord.total_amount !== undefined) {
console.log(` Total Amount: ${firstRecord.total_amount}`);
}
if (firstRecord.order_status !== undefined) {
console.log(` Order Status: ${firstRecord.order_status}`);
}
console.log(` Full Record: ${JSON.stringify(firstRecord, null, 2)}`);
} else {
console.log('⚠ No created records returned');
}
console.log();
} catch (error: any) {
console.error(`✗ Error: ${error.message}`);
if (error.stack) {
console.error(error.stack);
}
// Try to close decision on error
if (decisionId) {
try {
console.log('Attempting to abort decision...');
await client.callTool('decision_close', {
decision_id: decisionId,
action: 'abort',
reason: `Error occurred: ${error.message}`,
});
console.log('✓ Decision aborted');
} catch (closeError: any) {
console.error(`Failed to abort decision: ${closeError.message}`);
}
}
process.exit(1);
}
}
// Run the test
runInsertTest().catch((error) => {
console.error('Fatal error:', error);
process.exit(1);
});
Example 4: Update Agent
This example demonstrates updating product stock.
#!/usr/bin/env tsx
/**
* Update Test Agent
*
* Tests the update operation on planetscale_update_product_stock_v1 data product.
* This agent:
* 1. Creates a decision envelope
* 2. Updates product stock using decision_write with operation: "update"
* 3. Closes the decision envelope
*/
import { MCPClient } from './mcp-client.js';
async function runUpdateTest() {
// Get configuration from environment
const mcpUrl = process.env.MCP_AGENT_URL || 'https://mcp.tracemem.com';
const apiKey = process.env.TRACEMEM_API_KEY;
const instance = process.env.TRACEMEM_INSTANCE;
const actor = process.env.TRACEMEM_ACTOR || 'test-update-agent';
// Test data - can be overridden via environment variables
const productId = parseInt(process.env.PRODUCT_ID || '4', 10);
const stockQuantity = parseInt(process.env.STOCK_QUANTITY || '90', 10);
if (!apiKey) {
console.error('ERROR: TRACEMEM_API_KEY environment variable is required');
process.exit(1);
}
console.log('='.repeat(60));
console.log('TraceMem MCP - Update Test Agent');
console.log('='.repeat(60));
console.log();
console.log(`Connecting to Agent MCP at: ${mcpUrl}`);
if (instance) console.log(`Instance: ${instance}`);
console.log(`Actor: ${actor}`);
console.log(`Update Data:`);
console.log(` Product ID: ${productId}`);
console.log(` Stock Quantity: ${stockQuantity}`);
console.log();
const client = new MCPClient(mcpUrl, apiKey);
let decisionId: string | null = null;
try {
// Initialize MCP session
console.log('Initializing MCP session...');
const initResult = await client.initialize();
console.log(`✓ Connected to ${initResult.serverInfo?.name || 'TraceMem Agent MCP'}`);
console.log();
// Step 1: Create decision envelope
console.log('Step 1: Creating decision envelope...');
const decision = await client.callTool('decision_create', {
intent: 'test.update.product.stock',
automation_mode: 'autonomous',
instance: instance,
actor: actor,
metadata: {
product_id: productId.toString(),
stock_quantity: stockQuantity.toString(),
test_type: 'update',
},
});
decisionId = decision.decision_id || decision.id;
if (!decisionId) {
throw new Error('Failed to get decision_id from decision_create response');
}
console.log(`✓ Decision envelope created: ${decisionId}`);
console.log();
// Step 2: Update product stock
console.log('Step 2: Updating product stock...');
const writeResult = await client.callTool('decision_write', {
decision_id: decisionId,
product: 'planetscale_update_product_stock_v1',
purpose: 'web_order',
mutation: {
operation: 'update',
records: [{
product_id: productId, // Key field for identification
stock_quantity: stockQuantity, // Field to update (is_updatable: true)
}],
},
});
console.log('✓ Product stock updated');
if (writeResult.event_id) {
console.log(` Event ID: ${writeResult.event_id}`);
}
if (writeResult.status) {
console.log(` Status: ${writeResult.status}`);
}
if (writeResult.mutation_summary) {
console.log(` Mutation Summary: ${JSON.stringify(writeResult.mutation_summary)}`);
}
console.log();
// Step 3: Close decision (commit)
console.log('Step 3: Committing decision...');
const closeResult = await client.callTool('decision_close', {
decision_id: decisionId,
action: 'commit',
});
console.log(`✓ Decision committed`);
if (closeResult.status) {
console.log(` Status: ${closeResult.status}`);
}
console.log();
// Summary
console.log('='.repeat(60));
console.log('Summary');
console.log('='.repeat(60));
console.log(`Decision ID: ${decisionId}`);
console.log('Result: ✓ Update operation completed successfully');
console.log(`Product ID: ${productId}`);
console.log(`New Stock Quantity: ${stockQuantity}`);
console.log();
} catch (error: any) {
console.error(`✗ Error: ${error.message}`);
if (error.stack) {
console.error(error.stack);
}
// Try to close decision on error
if (decisionId) {
try {
console.log('Attempting to abort decision...');
await client.callTool('decision_close', {
decision_id: decisionId,
action: 'abort',
reason: `Error occurred: ${error.message}`,
});
console.log('✓ Decision aborted');
} catch (closeError: any) {
console.error(`Failed to abort decision: ${closeError.message}`);
}
}
process.exit(1);
}
}
// Run the test
runUpdateTest().catch((error) => {
console.error('Fatal error:', error);
process.exit(1);
});
Environment Variables
TRACEMEM_API_KEY(required): Your TraceMem agent API keyMCP_AGENT_URL(optional): MCP server URL (default:https://mcp.tracemem.com)TRACEMEM_INSTANCE(optional): Instance identifierTRACEMEM_ACTOR(optional): Actor identifier (default:test-update-agent)PRODUCT_ID(optional): Product ID to update (default:4)STOCK_QUANTITY(optional): New stock quantity (default:90)
Example 5: Delete Agent
This example demonstrates deleting a target record.
#!/usr/bin/env tsx
/**
* Delete Test Agent
*
* Tests the delete operation on planetscale_delete_target_v1 data product.
* This agent:
* 1. Creates a decision envelope
* 2. Deletes a target record using decision_write with operation: "delete"
* 3. Closes the decision envelope
*/
import { MCPClient } from './mcp-client.js';
async function runDeleteTest() {
// Get configuration from environment
const mcpUrl = process.env.MCP_AGENT_URL || 'https://mcp.tracemem.com';
const apiKey = process.env.TRACEMEM_API_KEY;
const instance = process.env.TRACEMEM_INSTANCE;
const actor = process.env.TRACEMEM_ACTOR || 'test-delete-agent';
// Test data - target ID must be a UUID
const targetId = process.env.TARGET_ID || '0cf1e19e-00ed-4a4c-8d82-ee70f590fad8';
if (!apiKey) {
console.error('ERROR: TRACEMEM_API_KEY environment variable is required');
process.exit(1);
}
if (!targetId) {
console.error('ERROR: TARGET_ID environment variable is required (must be a UUID)');
console.error('Example: TARGET_ID=550e8400-e29b-41d4-a716-446655440000');
process.exit(1);
}
// Basic UUID format validation
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
if (!uuidRegex.test(targetId)) {
console.error('ERROR: TARGET_ID must be a valid UUID format');
console.error(`Received: ${targetId}`);
console.error('Expected format: 550e8400-e29b-41d4-a716-446655440000');
process.exit(1);
}
console.log('='.repeat(60));
console.log('TraceMem MCP - Delete Test Agent');
console.log('='.repeat(60));
console.log();
console.log(`Connecting to Agent MCP at: ${mcpUrl}`);
if (instance) console.log(`Instance: ${instance}`);
console.log(`Actor: ${actor}`);
console.log(`Delete Data:`);
console.log(` Target ID: ${targetId}`);
console.log();
console.log('⚠ WARNING: This will delete a record from the database!');
console.log();
const client = new MCPClient(mcpUrl, apiKey);
let decisionId: string | null = null;
try {
// Initialize MCP session
console.log('Initializing MCP session...');
const initResult = await client.initialize();
console.log(`✓ Connected to ${initResult.serverInfo?.name || 'TraceMem Agent MCP'}`);
console.log();
// Step 1: Create decision envelope
console.log('Step 1: Creating decision envelope...');
const decision = await client.callTool('decision_create', {
intent: 'test.delete.target',
automation_mode: 'autonomous',
instance: instance,
actor: actor,
metadata: {
target_id: targetId,
test_type: 'delete',
},
});
decisionId = decision.decision_id || decision.id;
if (!decisionId) {
throw new Error('Failed to get decision_id from decision_create response');
}
console.log(`✓ Decision envelope created: ${decisionId}`);
console.log();
// Step 2: Delete target record
console.log('Step 2: Deleting target record...');
const writeResult = await client.callTool('decision_write', {
decision_id: decisionId,
product: 'planetscale_delete_target_v1',
purpose: 'delete_target',
mutation: {
operation: 'delete',
records: [{
id: targetId, // Key field for deletion (is_key: true, type: uuid)
}],
},
});
console.log('✓ Target record deleted');
if (writeResult.event_id) {
console.log(` Event ID: ${writeResult.event_id}`);
}
if (writeResult.status) {
console.log(` Status: ${writeResult.status}`);
}
if (writeResult.mutation_summary) {
console.log(` Mutation Summary: ${JSON.stringify(writeResult.mutation_summary)}`);
}
console.log();
// Step 3: Close decision (commit)
console.log('Step 3: Committing decision...');
const closeResult = await client.callTool('decision_close', {
decision_id: decisionId,
action: 'commit',
});
console.log(`✓ Decision committed`);
if (closeResult.status) {
console.log(` Status: ${closeResult.status}`);
}
console.log();
// Summary
console.log('='.repeat(60));
console.log('Summary');
console.log('='.repeat(60));
console.log(`Decision ID: ${decisionId}`);
console.log('Result: ✓ Delete operation completed successfully');
console.log(`Deleted Target ID: ${targetId}`);
console.log();
} catch (error: any) {
console.error(`✗ Error: ${error.message}`);
if (error.stack) {
console.error(error.stack);
}
// Try to close decision on error
if (decisionId) {
try {
console.log('Attempting to abort decision...');
await client.callTool('decision_close', {
decision_id: decisionId,
action: 'abort',
reason: `Error occurred: ${error.message}`,
});
console.log('✓ Decision aborted');
} catch (closeError: any) {
console.error(`Failed to abort decision: ${closeError.message}`);
}
}
process.exit(1);
}
}
// Run the test
runDeleteTest().catch((error) => {
console.error('Fatal error:', error);
process.exit(1);
});
Environment Variables
TRACEMEM_API_KEY(required): Your TraceMem agent API keyMCP_AGENT_URL(optional): MCP server URL (default:https://mcp.tracemem.com)TRACEMEM_INSTANCE(optional): Instance identifierTRACEMEM_ACTOR(optional): Actor identifier (default:test-delete-agent)TARGET_ID(required): UUID of target record to delete
Common Patterns
Error Handling Best Practices
Always ensure decision envelopes are properly closed, even on errors:
let decisionId: string | null = null;
try {
// Create decision and perform operations
const decision = await client.callTool('decision_create', {...});
decisionId = decision.decision_id;
// Perform operations...
// Commit on success
await client.callTool('decision_close', {
decision_id: decisionId,
action: 'commit',
});
} catch (error: any) {
// Always abort on error
if (decisionId) {
try {
await client.callTool('decision_close', {
decision_id: decisionId,
action: 'abort',
reason: `Error occurred: ${error.message}`,
});
} catch (closeError) {
// Log but don't fail on abort failure
console.error(`Failed to abort decision: ${closeError}`);
}
}
throw error;
}
Decision Envelope Lifecycle
Every agent operation should follow this pattern:
- Initialize MCP Session: Connect to the Agent MCP server
- Create Decision Envelope: Open a decision with appropriate intent
- Perform Operations: Read, write, evaluate policies, etc.
- Close Decision: Commit on success, abort on error
Environment Variable Configuration
All examples use environment variables for configuration:
# Required
export TRACEMEM_API_KEY="your-api-key"
# Optional
export MCP_AGENT_URL="https://mcp.tracemem.com"
export TRACEMEM_INSTANCE="my-instance"
export TRACEMEM_ACTOR="my-agent"
Testing Tips
- Start with Read Operations: Test read operations first to verify connectivity
- Use Test Data: Use non-production data products for testing
- Check Decision Traces: Review decision traces in the TraceMem dashboard
- Handle Errors Gracefully: Always implement proper error handling and decision cleanup