Skip to main content

Basic Usage

import { Agent, AgentContext, Tool, ToolContext } from '@frontmcp/sdk';
import { z } from 'zod';

@Tool({
  name: 'search_web',
  inputSchema: { query: z.string() },
})
class SearchWebTool extends ToolContext {
  async execute(input: { query: string }) {
    return { results: ['Result 1', 'Result 2'] };
  }
}

@Agent({
  name: 'research-agent',
  description: 'Researches topics using web search',
  llm: {
    adapter: 'openai',
    model: 'gpt-4-turbo',
    apiKey: { env: 'OPENAI_API_KEY' },
  },
  tools: [SearchWebTool],
})
export default class ResearchAgent extends AgentContext {
  // Default behavior: runs agent loop automatically
}

Signature

function Agent<InSchema, OutSchema>(
  providedMetadata: AgentMetadata<InSchema, OutSchema>
): ClassDecorator

Configuration Options

Required Properties

PropertyTypeDescription
namestringUnique agent identifier
llmAgentLlmAdapterConfigLLM provider configuration

Optional Properties

PropertyTypeDescription
descriptionstringAgent description
inputSchemaZodShapeInput validation schema
outputSchemaZodTypeOutput validation schema
toolsToolType[]Tools available to the agent
systemInstructionsstringSystem prompt for the agent
idstringStable identifier
tagsstring[]Categorization tags

LLM Configuration

interface AgentLlmAdapterConfig {
  adapter: 'openai' | 'anthropic' | 'google' | 'mistral' | 'groq' | string;
  model: string;
  apiKey?: string | { env: string } | { config: string };
  temperature?: number;
  maxTokens?: number;
  // Provider-specific options...
}

LLM Providers

OpenAI

@Agent({
  name: 'assistant',
  llm: {
    adapter: 'openai',
    model: 'gpt-4-turbo',
    apiKey: { env: 'OPENAI_API_KEY' },
    temperature: 0.7,
  },
})

Anthropic (Claude)

@Agent({
  name: 'assistant',
  llm: {
    adapter: 'anthropic',
    model: 'claude-3-opus-20240229',
    apiKey: { env: 'ANTHROPIC_API_KEY' },
  },
})

Google (Gemini)

@Agent({
  name: 'assistant',
  llm: {
    adapter: 'google',
    model: 'gemini-pro',
    apiKey: { env: 'GOOGLE_API_KEY' },
  },
})

Agent Loop

By default, agents run an automatic loop:
  1. Send input to LLM with available tools
  2. If LLM requests tool call, execute tool and return result
  3. Repeat until LLM returns final response
  4. Parse and return output
@Agent({
  name: 'task-agent',
  llm: { adapter: 'openai', model: 'gpt-4' },
  tools: [Tool1, Tool2],
  systemInstructions: 'You are a helpful assistant.',
})
class TaskAgent extends AgentContext {
  // No execute() override needed - uses default loop
}

Custom Execution

Override execute() for custom behavior:
@Agent({
  name: 'custom-agent',
  inputSchema: { task: z.string() },
  outputSchema: z.object({ result: z.string() }),
  llm: { adapter: 'openai', model: 'gpt-4' },
})
class CustomAgent extends AgentContext {
  async execute(input: { task: string }) {
    // Pre-processing
    await this.notify('Starting task...', 'info');

    // Custom validation
    if (input.task.length < 10) {
      return { result: 'Task too short' };
    }

    // Run default agent loop
    const result = await super.execute(input);

    // Post-processing
    return {
      result: `Completed: ${result}`,
    };
  }
}

Function-Based Alternative

import { agent } from '@frontmcp/sdk';
import { z } from 'zod';

const researchAgent = agent({
  name: 'research',
  inputSchema: { topic: z.string() },
  llm: { adapter: 'openai', model: 'gpt-4' },
})((input, ctx) => {
  // Custom execution logic
  return { findings: '...' };
});

Context Methods

LLM Completion

protected async completion(
  prompt: AgentPrompt,
  tools?: AgentToolDefinition[],
  options?: AgentCompletionOptions
): Promise<AgentCompletion>

protected async *streamCompletion(
  prompt: AgentPrompt,
  tools?: AgentToolDefinition[]
): AsyncGenerator<AgentCompletionChunk>

Tool Execution

protected async executeTool(
  name: string,
  args: Record<string, unknown>
): Promise<unknown>

protected async invokeAgent(
  agentId: string,
  input: unknown
): Promise<unknown>

Notifications

protected async notify(
  message: string | Record<string, unknown>,
  level?: 'debug' | 'info' | 'warning' | 'error'
): Promise<boolean>

protected async progress(
  progress: number,
  total?: number,
  message?: string
): Promise<boolean>

Elicitation

protected async elicit<S extends ZodType>(
  message: string,
  requestedSchema: S,
  options?: ElicitOptions
): Promise<ElicitResult>

Agent Visibility

Agents can invoke other agents:
@Agent({
  name: 'orchestrator',
  llm: { adapter: 'openai', model: 'gpt-4' },
  tools: [ResearchAgent, WriterAgent], // Agents as tools
})
class OrchestratorAgent extends AgentContext {
  async execute(input: { task: string }) {
    // Can invoke sub-agents
    const research = await this.invokeAgent('research-agent', { topic: input.task });
    return research;
  }
}

Full Example

import { Agent, AgentContext, Tool, ToolContext, App, FrontMcp } from '@frontmcp/sdk';
import { z } from 'zod';

// Tools for the agent
@Tool({
  name: 'search_database',
  inputSchema: { query: z.string(), table: z.string() },
})
class SearchDatabaseTool extends ToolContext {
  async execute(input) {
    const db = this.get(DatabaseToken);
    return db.search(input.table, input.query);
  }
}

@Tool({
  name: 'create_report',
  inputSchema: { title: z.string(), data: z.any() },
})
class CreateReportTool extends ToolContext {
  async execute(input) {
    return { reportId: 'rpt_123', title: input.title };
  }
}

// Agent definition
@Agent({
  name: 'data-analyst',
  description: 'Analyzes data and creates reports',
  systemInstructions: `You are a data analyst. Use the available tools to:
1. Search the database for relevant data
2. Analyze the results
3. Create a comprehensive report

Always explain your reasoning before taking actions.`,
  inputSchema: {
    request: z.string().describe('Analysis request'),
    tables: z.array(z.string()).describe('Tables to analyze'),
  },
  outputSchema: z.object({
    reportId: z.string(),
    summary: z.string(),
  }),
  llm: {
    adapter: 'openai',
    model: 'gpt-4-turbo',
    apiKey: { env: 'OPENAI_API_KEY' },
    temperature: 0.3,
  },
  tools: [SearchDatabaseTool, CreateReportTool],
  tags: ['analytics', 'reports'],
})
class DataAnalystAgent extends AgentContext {
  // Override for custom pre/post processing
  async execute(input) {
    await this.notify(`Starting analysis: ${input.request}`, 'info');
    await this.progress(0, 100, 'Initializing...');

    // Run the default agent loop
    const result = await super.execute(input);

    await this.progress(100, 100, 'Complete');
    return result;
  }

  // Override tool execution for logging
  protected async executeTool(name: string, args: Record<string, unknown>) {
    this.logger.info(`Executing tool: ${name}`, { args });
    return super.executeTool(name, args);
  }
}

@App({
  name: 'analytics',
  agents: [DataAnalystAgent],
  tools: [SearchDatabaseTool, CreateReportTool],
})
class AnalyticsApp {}

@FrontMcp({
  info: { name: 'Analytics Platform', version: '1.0.0' },
  apps: [AnalyticsApp],
})
export default class AnalyticsPlatform {}

AgentContext

Context class details

AgentRegistry

Agent registry API

Agent Errors

Agent-related errors

@Tool

Define tools