Skip to main content

Class Definition

export abstract class ToolContext<
  InSchema extends ToolInputType = ToolInputType,
  OutSchema extends ToolOutputType = ToolOutputType,
  In = ToolInputOf<{ inputSchema: InSchema }>,
  Out = ToolOutputOf<{ outputSchema: OutSchema }>,
> extends ExecutionContextBase<Out>

Type Parameters

ParameterDescription
InSchemaInput schema type (Zod shape)
OutSchemaOutput schema type
InInferred input type
OutInferred output type

Properties

PropertyTypeDescription
metadataToolMetadataTool metadata (name, description, etc.)
inputInParsed and validated input
outputOut | undefinedTool output (after execution)
toolNamestringTool name
toolIdstringTool ID (or name if not specified)
platformAIPlatformTypeDetected AI platform
clientInfoClientInfo | undefinedClient information

Abstract Method

execute(input)

The main execution method that must be implemented.
abstract execute(input: In): Promise<Out>
@Tool({
  name: 'my_tool',
  inputSchema: { value: z.number() },
  outputSchema: z.object({ result: z.number() }),
})
class MyTool extends ToolContext {
  async execute(input: { value: number }) {
    return { result: input.value * 2 };
  }
}

Methods

Response Methods

respond(value)

Set output and end execution immediately.
respond(value: Out): never
async execute(input) {
  if (cached) {
    this.respond(cached); // Ends execution here
  }
  // This code won't run if cached
  return processInput(input);
}

Notification Methods (MCP 2025-11-25)

notify(message, level?)

Send a log message notification to the client.
protected notify(
  message: string | Record<string, unknown>,
  level?: 'debug' | 'info' | 'warning' | 'error'
): Promise<boolean>
await this.notify('Processing started', 'info');
await this.notify({ step: 1, status: 'validating' }, 'debug');

progress(progress, total?, message?)

Send a progress notification to the client.
protected progress(
  progress: number,
  total?: number,
  message?: string
): Promise<boolean>
await this.progress(0, 100, 'Starting...');
await this.progress(50, 100, 'Halfway done');
await this.progress(100, 100, 'Complete');

Elicitation Methods

elicit<S>(message, requestedSchema, options?)

Request interactive user input during execution.
protected elicit<S extends ZodType>(
  message: string,
  requestedSchema: S,
  options?: ElicitOptions
): Promise<ElicitResult<z.infer<S>>>
Options:
OptionTypeDescription
mode'form' | 'url'Input mode
ttlnumberTimeout in milliseconds
Result:
interface ElicitResult<T> {
  status: 'accept' | 'reject' | 'timeout';
  content?: T;
  validationErrors?: string[];
}
Usage:
async execute(input) {
  const result = await this.elicit(
    'Please confirm this action',
    z.object({
      confirmed: z.boolean(),
      reason: z.string().optional(),
    }),
    { mode: 'form', ttl: 300000 }
  );

  if (result.status !== 'accept' || !result.content?.confirmed) {
    return { cancelled: true };
  }

  // Proceed with action
  return { success: true };
}

Platform Detection

get platform

Detect the AI platform making the request.
get platform(): AIPlatformType
Values: 'openai' | 'claude' | 'gemini' | 'langchain' | 'vercel-ai' | 'unknown'
if (this.platform === 'claude') {
  // Claude-specific formatting
}

get clientInfo

Get client information.
get clientInfo(): ClientInfo | undefined
const client = this.clientInfo;
if (client) {
  console.log(client.name);    // 'claude'
  console.log(client.version); // '1.0.0'
}

History

inputHistory

History of input changes during execution.
get inputHistory(): ReadonlyArray<In>

outputHistory

History of output changes during execution.
get outputHistory(): ReadonlyArray<Out>

Full Example

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

const inputSchema = {
  action: z.enum(['create', 'update', 'delete']),
  resourceId: z.string(),
  data: z.any().optional(),
};

const outputSchema = z.object({
  success: z.boolean(),
  resourceId: z.string(),
  message: z.string(),
});

@Tool({
  name: 'manage_resource',
  description: 'Create, update, or delete a resource',
  inputSchema,
  outputSchema,
  annotations: {
    destructiveHint: true,
  },
})
class ManageResourceTool extends ToolContext<
  typeof inputSchema,
  typeof outputSchema
> {
  async execute(input) {
    // Log start
    await this.notify(`Starting ${input.action} operation`, 'info');
    await this.progress(0, 100, 'Initializing...');

    // For destructive actions, confirm with user
    if (input.action === 'delete') {
      const confirmation = await this.elicit(
        `Are you sure you want to delete resource ${input.resourceId}?`,
        z.object({ confirmed: z.boolean() }),
        { mode: 'form', ttl: 60000 }
      );

      if (confirmation.status !== 'accept' || !confirmation.content?.confirmed) {
        return {
          success: false,
          resourceId: input.resourceId,
          message: 'Delete cancelled by user',
        };
      }
    }

    await this.progress(25, 100, 'Validating...');

    // Get service
    const resourceService = this.get(ResourceServiceToken);

    // Perform action
    await this.progress(50, 100, `Executing ${input.action}...`);

    let message: string;
    switch (input.action) {
      case 'create':
        await resourceService.create(input.resourceId, input.data);
        message = 'Resource created successfully';
        break;
      case 'update':
        await resourceService.update(input.resourceId, input.data);
        message = 'Resource updated successfully';
        break;
      case 'delete':
        await resourceService.delete(input.resourceId);
        message = 'Resource deleted successfully';
        break;
    }

    await this.progress(100, 100, 'Complete');
    await this.notify({ action: input.action, resourceId: input.resourceId }, 'info');

    return {
      success: true,
      resourceId: input.resourceId,
      message,
    };
  }
}

@Tool

Tool decorator

ExecutionContextBase

Base class

ToolRegistry

Tool registry

Tool Errors

Tool errors