> ## 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.

# @Tool

> The @Tool decorator defines an MCP tool with validated input/output schemas and full TypeScript type inference.

## Basic Usage

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
import { Tool, ToolContext } from '@frontmcp/sdk';
import { z } from '@frontmcp/sdk';

@Tool({
  name: 'get_weather',
  description: 'Get current weather for a location',
  inputSchema: {
    city: z.string().describe('City name'),
    units: z.enum(['celsius', 'fahrenheit']).default('celsius'),
  },
})
class GetWeatherTool extends ToolContext {
  async execute(input: { city: string; units: 'celsius' | 'fahrenheit' }) {
    const weather = await fetchWeather(input.city, input.units);
    return { temperature: weather.temp, conditions: weather.conditions };
  }
}
```

## Signature

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
function Tool<I extends ZodRawShape, O extends OutputSchema>(
  opts: ToolMetadataOptions<I, O>
): TypedClassDecorator
```

## Type Safety

The `@Tool` decorator provides compile-time type checking:

* **Input validation**: The `execute()` parameter type must exactly match the `inputSchema`. Mismatches produce a descriptive error at compile time.
* **Output validation**: When `outputSchema` is provided, the `execute()` return type must be assignable to the inferred output type.
* **Context check**: The decorated class must extend `ToolContext`. Using `@Tool` on a plain class produces a compile error.
* **Invalid options**: Typos in decorator options (e.g., `concurrency: { maxConcurrensst: 5 }`) are caught at compile time with specific error messages, without losing autocomplete on other fields.

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
// Compile error: parameter type { query: number } doesn't match inputSchema { query: string }
@Tool({ name: 'search', inputSchema: { query: z.string() } })
class BadTool extends ToolContext {
  async execute(input: { query: number }) { // TS error on this class
    return { result: input.query };
  }
}
```

## Configuration Options

### Required Properties

| Property      | Type       | Description                           |
| ------------- | ---------- | ------------------------------------- |
| `name`        | `string`   | Unique tool identifier                |
| `inputSchema` | `ZodShape` | Zod object shape for input validation |

### Optional Properties

| Property            | Type                                                        | Description                                                                                                                          |
| ------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `description`       | `string`                                                    | Tool description for AI                                                                                                              |
| `outputSchema`      | `ZodType`                                                   | Output validation schema                                                                                                             |
| `id`                | `string`                                                    | Stable identifier for tracking                                                                                                       |
| `tags`              | `string[]`                                                  | Categorization tags                                                                                                                  |
| `visibility`        | `'public' \| 'hidden' \| 'internal'`                        | Discovery visibility. `hidden` hides from public lists; `internal` keeps it for in-process callers only                              |
| `rateLimit`         | `RateLimitConfig`                                           | Per-tool rate limit (`maxRequests`, `windowMs`, `partitionBy`)                                                                       |
| `concurrency`       | `ConcurrencyConfig`                                         | Per-tool concurrency cap (`maxConcurrent`, optional queue config)                                                                    |
| `timeout`           | `TimeoutConfig`                                             | Per-tool execution timeout (`executeMs`)                                                                                             |
| `availableWhen`     | `EntryAvailability`                                         | Restrict discovery to specific platforms/runtimes/deployments. See [Environment Awareness](/frontmcp/features/environment-awareness) |
| `execution`         | `{ taskSupport?: 'required' \| 'optional' \| 'forbidden' }` | MCP 2025-11-25 task-execution mode for `tools/list` items                                                                            |
| `authProviders`     | `ToolAuthProviderRef[]`                                     | Required auth providers for this tool (string name or `{ name, required?, scopes?, alias? }`)                                        |
| `annotations`       | `ToolAnnotations`                                           | MCP hints (`title`, `readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`)                                            |
| `examples`          | `ToolExample[]`                                             | Usage examples (`description`, `input`, optional `output`)                                                                           |
| `ui`                | `ToolUIConfig`                                              | UI widget configuration (`template`, `csp`, sanitization options)                                                                    |
| `hideFromDiscovery` | `boolean`                                                   | Deprecated alias for `visibility: 'hidden'`                                                                                          |

### Output Types

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
// Object output
@Tool({
  name: 'create_user',
  inputSchema: { name: z.string(), email: z.string().email() },
  outputSchema: z.object({
    id: z.string(),
    name: z.string(),
    email: z.string(),
  }),
})

// Primitive outputs
@Tool({
  name: 'calculate',
  inputSchema: { expression: z.string() },
  outputSchema: 'number', // Returns a number
})

// MCP content types
@Tool({
  name: 'generate_image',
  inputSchema: { prompt: z.string() },
  outputSchema: 'image', // Returns base64 image
})

@Tool({
  name: 'text_to_speech',
  inputSchema: { text: z.string() },
  outputSchema: 'audio', // Returns base64 audio
})

@Tool({
  name: 'fetch_document',
  inputSchema: { uri: z.string() },
  outputSchema: 'resource', // Returns MCP resource
})
```

### Authorization

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@Tool({
  name: 'sensitive_operation',
  inputSchema: { data: z.string() },
  authProviders: [
    { name: 'github', scopes: ['repo', 'user'] },
  ],
})
class SensitiveTool extends ToolContext {
  async execute(input) {
    // Access is validated before execute() is called
    const token = this.context.authInfo?.token;
    return await callApi(token, input.data);
  }
}
```

### Annotations

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@Tool({
  name: 'delete_file',
  inputSchema: { path: z.string() },
  annotations: {
    readOnlyHint: false,
    destructiveHint: true,
    idempotentHint: false,
    openWorldHint: false,
  },
})
```

### Examples

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@Tool({
  name: 'search_users',
  inputSchema: { query: z.string(), limit: z.number().optional() },
  examples: [
    {
      description: 'Search for users named "john"',
      input: { query: 'john', limit: 10 },
      output: { users: [{ id: '1', name: 'John Doe' }], total: 1 },
    },
  ],
})
```

### UI Configuration

The `ui.template` field accepts a builder function, an HTML/MDX string, a React component, or a `FileSource` (for client-side transpilation):

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@Tool({
  name: 'user_profile',
  inputSchema: { userId: z.string() },
  ui: {
    template: (ctx) => `<div>User ${ctx.input.userId}</div>`,
  },
})
```

See the [Tool UI guide](/frontmcp/servers/tools#tool-ui) for the full `ToolUIConfig` shape (CSP, sanitization, helpers, etc.).

## Function-Based Alternative

For simpler tools, use the `tool()` function:

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

const getWeather = tool({
  name: 'get_weather',
  description: 'Get current weather',
  inputSchema: { city: z.string() },
})((input, ctx) => {
  // ctx provides access to context methods
  const config = ctx.get(ConfigToken);
  return { temperature: 72, city: input.city };
});
```

## Context Methods

The `ToolContext` base class provides:

### Dependency Injection

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
async execute(input) {
  const service = this.get(ServiceToken);      // Required dependency
  const optional = this.tryGet(OptionalToken); // Optional dependency
}
```

### Notifications

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
async execute(input) {
  // Log message (MCP 2025-11-25 spec)
  await this.notify('Processing started', 'info');

  // Progress notification
  await this.progress(50, 100, 'Halfway done...');
}
```

### Elicitation (Interactive Input)

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
async execute(input) {
  const result = await this.elicit(
    'Please confirm this action',
    z.object({ confirmed: z.boolean() }),
    { mode: 'form', ttl: 300000 }
  );

  if (result.status === 'accept' && result.content.confirmed) {
    // Proceed with action
  }
}
```

### Platform Detection

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
async execute(input) {
  // Detect AI platform
  if (this.platform === 'claude') {
    // Claude-specific formatting
  }

  // Get client info
  const client = this.clientInfo; // { name: 'claude', version: '1.0' }
}
```

### Authentication

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
async execute(input) {
  const auth = this.context.authInfo;
  const token = auth?.token;
  const user = auth?.user;
}
```

### HTTP Requests

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
async execute(input) {
  // Context-aware fetch with auto-injected headers
  const response = await this.fetch('https://api.example.com/data', {
    method: 'POST',
    body: JSON.stringify(input),
  });
}
```

### Error Handling

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
async execute(input) {
  if (!input.valid) {
    this.fail(new Error('Invalid input'));
  }

  // Early return with output
  if (cached) {
    this.respond(cached); // Ends execution
  }
}
```

## Type Inference

FrontMCP provides helper types for extracting input/output types:

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

const metadata = {
  name: 'my_tool',
  inputSchema: { x: z.number(), y: z.number() },
  outputSchema: z.object({ result: z.number() }),
} as const;

type Input = ToolInputOf<typeof metadata>;   // { x: number; y: number }
type Output = ToolOutputOf<typeof metadata>; // { result: number }
```

## Full Example

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
import { Tool, ToolContext, App, FrontMcp } from '@frontmcp/sdk';
import { z } from '@frontmcp/sdk';

const inputSchema = {
  operation: z.enum(['add', 'subtract', 'multiply', 'divide']),
  a: z.number(),
  b: z.number(),
};

const outputSchema = z.object({
  result: z.number(),
  operation: z.string(),
});

@Tool({
  name: 'calculate',
  description: 'Perform arithmetic operations',
  inputSchema,
  outputSchema,
  annotations: {
    readOnlyHint: true,
    idempotentHint: true,
  },
  examples: [
    {
      description: 'Add two numbers',
      input: { operation: 'add', a: 2, b: 3 },
      output: { result: 5, operation: 'add' },
    },
  ],
})
class CalculateTool extends ToolContext {
  async execute(input) {
    await this.notify(`Calculating ${input.operation}(${input.a}, ${input.b})`, 'debug');

    let result: number;
    switch (input.operation) {
      case 'add': result = input.a + input.b; break;
      case 'subtract': result = input.a - input.b; break;
      case 'multiply': result = input.a * input.b; break;
      case 'divide':
        if (input.b === 0) this.fail(new Error('Division by zero'));
        result = input.a / input.b;
        break;
    }

    return { result, operation: input.operation };
  }
}

@App({ name: 'calculator', tools: [CalculateTool] })
class CalculatorApp {}

@FrontMcp({
  info: { name: 'Calculator', version: '1.0.0' },
  apps: [CalculatorApp],
})
export default class CalculatorServer {}
```

## Related

<CardGroup cols={2}>
  <Card title="ToolContext" icon="code" href="/frontmcp/sdk-reference/contexts/tool-context">
    Context class details
  </Card>

  <Card title="ToolRegistry" icon="database" href="/frontmcp/sdk-reference/registries/tool-registry">
    Tool registry API
  </Card>

  <Card title="Tool Errors" icon="triangle-exclamation" href="/frontmcp/sdk-reference/errors/tool-errors">
    Tool-related errors
  </Card>

  <Card title="@Resource" icon="file" href="/frontmcp/sdk-reference/decorators/resource">
    Define resources
  </Card>
</CardGroup>
