> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agentfront.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Agents

Agents are **autonomous AI units** that have their own LLM provider, can execute tools, and are themselves callable as tools. They enable sophisticated multi-agent architectures where specialized agents can be composed and orchestrated.

<Info>
  Agents build on the MCP Tools specification—each agent is automatically exposed as an `invoke_<agent-id>` tool that any MCP client can call.
</Info>

## Why Agents?

In the Model Context Protocol, agents serve a distinct purpose from tools, resources, and prompts:

| Aspect           | Agent                               | Tool                       | Resource         | Prompt                         |
| ---------------- | ----------------------------------- | -------------------------- | ---------------- | ------------------------------ |
| **Purpose**      | Autonomous AI execution             | Execute actions            | Provide data     | Provide templated instructions |
| **Has LLM**      | Yes (own LLM provider)              | No                         | No               | No                             |
| **Direction**    | Model or user triggers execution    | Model triggers execution   | Model pulls data | Model uses messages            |
| **Side effects** | Yes (LLM calls, tool execution)     | Yes (mutations, API calls) | No (read-only)   | No (message generation)        |
| **Use case**     | Complex reasoning, multi-step tasks | Actions, integrations      | Context loading  | Conversation templates         |

Agents are ideal for:

* **Complex reasoning** — tasks requiring multiple LLM calls and tool use
* **Specialized expertise** — domain-specific agents (research, writing, coding)
* **Orchestration** — coordinating multiple sub-agents for complex workflows
* **Isolation** — agents with their own tools, resources, and providers

***

## Creating Agents

### Class Style (Default Behavior)

The simplest agent requires **no `execute()` method**. The default behavior automatically:

* Runs the execution loop with the LLM
* Connects tools and executes them as needed
* Sends notifications on tool calls and output

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
import { Agent, AgentContext } from '@frontmcp/sdk';
import { z } from 'zod';

@Agent({
  name: 'research-agent',
  description: 'Researches topics and compiles summaries',
  systemInstructions: 'You are a research assistant. Search for information and provide concise summaries.',
  inputSchema: {
    topic: z.string().describe('Topic to research'),
  },
  llm: {
    adapter: 'openai',
    model: 'gpt-4-turbo',
    apiKey: { env: 'OPENAI_API_KEY' },
  },
  tools: [WebSearchTool, SummarizeTool],
})
class ResearchAgent extends AgentContext {}  // No execute() needed!
```

### Class Style (Custom Behavior)

Override `execute()` only when you need custom pre/post processing:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@Agent({
  name: 'custom-agent',
  description: 'Agent with custom logic',
  systemInstructions: 'You are a helpful assistant.',
  inputSchema: {
    query: z.string(),
  },
  llm: {
    adapter: 'openai',
    model: 'gpt-4-turbo',
    apiKey: { env: 'OPENAI_API_KEY' },
  },
})
class CustomAgent extends AgentContext {
  async execute(input: { query: string }) {
    // Custom pre-processing
    this.notify('Starting custom processing...', 'info');

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

    // Custom post-processing
    return { ...result, customField: 'added' };
  }
}
```

### Function Style

For simpler agents, use the functional builder:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
import { agent } from '@frontmcp/sdk';
import { z } from 'zod';

const EchoAgent = agent({
  name: 'echo-agent',
  description: 'Echoes back the input message',
  inputSchema: {
    message: z.string(),
  },
  llm: {
    adapter: 'openai',
    model: 'gpt-3.5-turbo',
    apiKey: { env: 'OPENAI_API_KEY' },
  },
})(({ message }) => ({ echoed: `Echo: ${message}` }));
```

***

## Registering Agents

Add agents to your app via the `agents` array:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
import { App } from '@frontmcp/sdk';

@App({
  id: 'my-app',
  name: 'My Application',
  agents: [ResearchAgent, CalculatorAgent, WriterAgent],
})
class MyApp {}
```

Each agent is automatically exposed as a tool:

* `invoke_research-agent`
* `invoke_calculator-agent`
* `invoke_writer-agent`

***

## LLM Configuration

Agents require an LLM configuration. FrontMCP uses **LangChain** as the standard adapter layer, providing consistent APIs across all LLM providers with built-in retry logic, streaming support, and token tracking.

### Using LangChain Adapters

First, install the LangChain package for your provider:

```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
# For OpenAI
npm install @langchain/openai

# For Anthropic
npm install @langchain/anthropic

# For Google
npm install @langchain/google-genai

# For Mistral
npm install @langchain/mistralai
```

### OpenAI

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
import { ChatOpenAI } from '@langchain/openai';
import { LangChainAdapter } from '@frontmcp/sdk';

const adapter = new LangChainAdapter({
  model: new ChatOpenAI({
    model: 'gpt-4-turbo',
    openAIApiKey: process.env.OPENAI_API_KEY,
  }),
});

@Agent({
  name: 'research-agent',
  llm: { adapter },
  // ...
})
class ResearchAgent extends AgentContext { }
```

### Anthropic

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
import { ChatAnthropic } from '@langchain/anthropic';
import { LangChainAdapter } from '@frontmcp/sdk';

const adapter = new LangChainAdapter({
  model: new ChatAnthropic({
    model: 'claude-3-opus-20240229',
    anthropicApiKey: process.env.ANTHROPIC_API_KEY,
  }),
});

@Agent({
  name: 'claude-agent',
  llm: { adapter },
  // ...
})
class ClaudeAgent extends AgentContext { }
```

### OpenRouter

Access 100+ models through OpenRouter using the OpenAI-compatible API:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
import { ChatOpenAI } from '@langchain/openai';
import { LangChainAdapter } from '@frontmcp/sdk';

const adapter = new LangChainAdapter({
  model: new ChatOpenAI({
    model: 'anthropic/claude-3-opus',  // Any OpenRouter model
    openAIApiKey: process.env.OPENROUTER_API_KEY,
    configuration: {
      baseURL: 'https://openrouter.ai/api/v1',
    },
  }),
});

@Agent({
  name: 'openrouter-agent',
  llm: { adapter },
  // ...
})
class OpenRouterAgent extends AgentContext { }
```

### Custom Adapter

Implement `AgentLlmAdapter` for providers not covered by LangChain:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
import { AgentLlmAdapter, AgentCompletion, AgentPrompt, AgentToolDefinition } from '@frontmcp/sdk';

const myAdapter: AgentLlmAdapter = {
  async completion(prompt: AgentPrompt, tools?: AgentToolDefinition[]): Promise<AgentCompletion> {
    // Call your LLM provider
    const response = await myLlmClient.chat({
      messages: prompt.messages,
      system: prompt.system,
      tools: tools?.map(t => ({ name: t.name, description: t.description })),
    });

    return {
      content: response.text,
      finishReason: response.hasToolCalls ? 'tool_calls' : 'stop',
      toolCalls: response.toolCalls,
    };
  },
};

@Agent({
  name: 'custom-agent',
  llm: { adapter: myAdapter },
})
class CustomAgent extends AgentContext { }
```

***

## Agent-Scoped Components

Agents can have their own isolated tools, resources, prompts, and providers:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@Agent({
  name: 'isolated-agent',
  description: 'Agent with its own scope',

  // Agent-scoped tools (only this agent can use them)
  tools: [PrivateTool],

  // Agent-scoped resources
  resources: [AgentConfig],

  // Agent-scoped prompts
  prompts: [AgentInstructions],

  // Agent-scoped providers
  providers: [DatabaseService],

  llm: { ... },
})
class IsolatedAgent extends AgentContext { ... }
```

***

## Swarm Configuration

Control agent visibility for multi-agent coordination:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@Agent({
  name: 'orchestrator-agent',
  description: 'Coordinates other agents',
  swarm: {
    canSeeOtherAgents: true,              // Can this agent invoke others?
    visibleAgents: ['worker-a', 'worker-b'], // Which agents can it see?
    isVisible: true,                      // Can other agents see this one?
    maxCallDepth: 3,                      // Max nested agent invocations
  },
  llm: { ... },
})
class OrchestratorAgent extends AgentContext {
  async execute({ task }: { task: string }) {
    // Can invoke visible agents
    const result = await this.invokeAgent('worker-a', { data: task });
    return { orchestrated: result };
  }
}
```

### Visibility Patterns

**Orchestrator Pattern** — A central agent coordinates specialized workers:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
// Workers (can't see each other)
@Agent({ name: 'research-worker', swarm: { isVisible: true, canSeeOtherAgents: false }, ... })
class ResearchWorker extends AgentContext { ... }

@Agent({ name: 'writer-worker', swarm: { isVisible: true, canSeeOtherAgents: false }, ... })
class WriterWorker extends AgentContext { ... }

// Orchestrator (can see and invoke workers)
@Agent({
  name: 'orchestrator',
  swarm: { canSeeOtherAgents: true, visibleAgents: ['research-worker', 'writer-worker'] },
  ...
})
class Orchestrator extends AgentContext { ... }
```

***

## Execution Configuration

Control agent execution behavior:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@Agent({
  name: 'long-running-agent',
  execution: {
    maxIterations: 10,          // Max tool call rounds (default: 10)
    timeout: 120000,            // Timeout in ms (default: 120000)
    enableStreaming: false,     // Stream responses
    enableNotifications: true,  // Send progress notifications
    enableAutoProgress: false,  // Auto progress during LLM calls (default: false)
    inheritParentTools: true,   // Include parent scope tools (default: true)
    useToolFlow: true,          // Execute tools through call-tool flow (default: true)
  },
  llm: { ... },
})
class LongRunningAgent extends AgentContext { ... }
```

### Tool Execution Mode

By default, agents execute tools through the full `call-tool` flow, which includes:

* Plugin hooks (caching, rate limiting, audit logging)
* Authorization checks
* Tool middleware and transformations

For performance-critical scenarios, you can disable flow execution:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@Agent({
  name: 'fast-agent',
  execution: {
    useToolFlow: false,  // Direct execution, bypasses plugins
  },
  llm: { ... },
})
class FastAgent extends AgentContext { ... }
```

<Warning>
  Setting `useToolFlow: false` bypasses all plugin hooks and middleware. Only use this when you need maximum performance and don't require plugin features.
</Warning>

***

## Overriding Behavior

Customize agent behavior by overriding methods in `AgentContext`:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@Agent({ name: 'custom-agent', llm: { ... } })
class CustomAgent extends AgentContext {
  // Override LLM completion for custom logic
  protected override async completion(prompt, tools, options) {
    this.logger.info('Making LLM request...');
    const result = await super.completion(prompt, tools, options);
    this.logger.info(`LLM returned: ${result.finishReason}`);
    return result;
  }

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

  async execute(input: { query: string }) {
    return { result: '...' };
  }
}
```

***

## Progress Notifications

Keep users informed during long operations using manual or automatic notifications.

### Manual Notifications

Use `this.notify()` to send custom messages at specific points:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@Agent({ name: 'notifying-agent', llm: { ... } })
class NotifyingAgent extends AgentContext {
  async execute({ task }: { task: string }) {
    await this.notify('Starting task...', 'info');

    // Step 1
    await this.notify('Gathering data...', 'info');
    const data = await this.gatherData();

    // Step 2
    await this.notify('Processing...', 'info');
    const result = await this.process(data);

    await this.notify('Complete!', 'info');
    return { result };
  }
}
```

Use `this.progress()` for progress bars when the client provides a `progressToken`:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@Agent({ name: 'progress-agent', llm: { ... } })
class ProgressAgent extends AgentContext {
  async execute({ files }: { files: string[] }) {
    for (let i = 0; i < files.length; i++) {
      await this.progress(i + 1, files.length, `Processing ${files[i]}`);
      await this.processFile(files[i]);
    }
    return { processed: files.length };
  }
}
```

### Automatic Progress (Opt-in)

Enable `enableAutoProgress` to automatically send progress notifications during the agent execution loop:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@Agent({
  name: 'auto-progress-agent',
  execution: {
    enableAutoProgress: true,  // Opt-in to automatic progress
  },
  llm: { ... },
})
class AutoProgressAgent extends AgentContext { }
```

When enabled, the agent automatically sends progress updates at these lifecycle points:

| Event                 | Progress % | Message Example                                |
| --------------------- | ---------- | ---------------------------------------------- |
| LLM call start        | 0-80%      | "Starting LLM call (iteration 1/10)"           |
| LLM response received | varies     | "LLM response received (500P + 200C tokens)"   |
| Tools identified      | -          | "Identified 2 tool call(s): search, calculate" |
| Tool execution        | varies     | "Executing tool 1/2: search"                   |
| Completion            | 100%       | "Agent completed"                              |

<Info>
  Auto progress requires both `enableAutoProgress: true` and `enableNotifications: true` (the default).
  Progress notifications are only sent if the client includes a `progressToken` in the request's `_meta` field.
</Info>

***

## Error Handling

Handle errors gracefully in agents:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
import { AgentExecutionError, AgentTimeoutError } from '@frontmcp/sdk';

@Agent({ name: 'safe-agent', llm: { ... } })
class SafeAgent extends AgentContext {
  async execute(input: { data: string }) {
    try {
      return await this.riskyOperation(input.data);
    } catch (error) {
      if (error instanceof ValidationError) {
        // Return error response with fail()
        this.fail(`Invalid input: ${error.message}`);
      }
      throw error;
    }
  }
}
```

***

## Nested Agents

Agents can contain other agents, creating hierarchical structures:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@Agent({
  name: 'parent-agent',
  description: 'Parent with nested child agents',

  // Nested agents — registered as tools within this agent
  agents: [ChildAgentA, ChildAgentB],

  // Parent's own tools
  tools: [PlanningTool],

  llm: { ... },
})
class ParentAgent extends AgentContext {
  async execute({ task }: { task: string }) {
    // LLM can call: invoke_child-agent-a, invoke_child-agent-b, planning-tool
    return { result: '...' };
  }
}
```
