Skip to content

Tools Reference

defineTool(name, options)

typescript
import { defineTool } from '@airmcp-dev/core';

const tool = defineTool('search', {
  description: 'Search documents',
  params: { query: 'string', limit: 'number?' },
  handler: async ({ query, limit }) => doSearch(query, limit),
  layer: 4,
  tags: ['search', 'read'],
});

Signature

typescript
function defineTool(name: string, options: {
  description?: string;
  params?: AirToolParams;
  outputSchema?: AirToolParams;       // MCP 2025-06-18 Structured Output
  annotations?: AirToolAnnotations;   // MCP 2025-03-26 Tool Annotations
  handler: AirToolHandler;
  layer?: number;
  tags?: string[];
}): AirToolDef;

Tool Annotations 0.2.0

MCP 2025-03-26 spec. Hints for clients about tool behavior. All fields optional.

typescript
defineTool('delete_user', {
  description: 'Delete a user permanently',
  params: { userId: 'string' },
  annotations: {
    title: 'Delete User',
    readOnlyHint: false,
    destructiveHint: true,
    idempotentHint: false,
    openWorldHint: true,
  },
  handler: async ({ userId }) => { /* ... */ },
});

AirToolAnnotations

typescript
interface AirToolAnnotations {
  title?: string;            // Human-readable title
  readOnlyHint?: boolean;    // No data modification
  destructiveHint?: boolean; // Deletes, overwrites, etc.
  idempotentHint?: boolean;  // Same input → same result
  openWorldHint?: boolean;   // Interacts with external systems
}

Structured Output 0.2.0

MCP 2025-06-18 spec. Define output schema — handler result auto-converts to structuredContent.

typescript
defineTool('get_user', {
  params: { userId: 'string' },
  outputSchema: { name: 'string', email: 'string', age: 'number?' },
  handler: async ({ userId }) => ({
    name: 'Alice',
    email: 'alice@example.com',
    age: 30,
  }),
});

outputSchema uses the same AirToolParams format as params.

Parameter types

AirToolParams

Three formats, mixable:

typescript
type AirToolParams = Record<string, ParamShorthand | ParamObjectDef | z.ZodType>;

ParamShorthand

ShorthandZod
'string'z.string()
'string?'z.string().optional()
'number'z.number()
'number?'z.number().optional()
'boolean'z.boolean()
'boolean?'z.boolean().optional()
'object'z.record(z.any())
'object?'z.record(z.any()).optional()

ParamObjectDef

typescript
interface ParamObjectDef {
  type: 'string' | 'number' | 'boolean' | 'object';
  description?: string;      // → Zod .describe()
  optional?: boolean;         // → Zod .optional()
}

AirToolHandler

typescript
type AirToolHandler = (
  params: Record<string, any>,
  context: AirToolContext,
) => Promise<AirToolResponse> | AirToolResponse;

AirToolContext

typescript
interface AirToolContext {
  requestId: string;          // crypto.randomUUID()
  serverName: string;
  startedAt: number;
  state: Record<string, any>;
  signal?: AbortSignal;       // Request cancellation (0.2.0)
  elicit?: (message: string, schema: AirElicitSchema) => Promise<AirElicitResult>;  // (0.2.0)
}

Elicitation 0.2.0

MCP 2025-06-18 spec. Request user input mid-execution. Available when the client supports elicitation.

typescript
defineTool('deploy', {
  params: { env: 'string' },
  annotations: { destructiveHint: true },
  handler: async ({ env }, ctx) => {
    if (env === 'production' && ctx.elicit) {
      const confirm = await ctx.elicit('Deploy to production?', {
        confirmed: { type: 'boolean', description: 'Confirm deployment' },
      });
      if (confirm.action !== 'accept') return 'Deployment cancelled';
    }
    return `Deployed to ${env}`;
  },
});
typescript
interface AirElicitSchema {
  [key: string]: {
    type: 'string' | 'number' | 'boolean';
    description?: string;
    required?: boolean;
  };
}

interface AirElicitResult {
  action: 'accept' | 'decline' | 'cancel';
  content?: Record<string, any>;
}

paramsToZodSchema(params?)

Convert shorthand to Zod. Applies .passthrough(). Returns undefined if empty.

typescript
paramsToZodSchema({ query: 'string', limit: 'number?' });
// → z.object({ query: z.string(), limit: z.number().optional() }).passthrough()

paramsToJsonSchema(params?)

Convert to JSON Schema for MCP registration.

typescript
paramsToJsonSchema({ query: 'string', limit: 'number?' });
// → { type: 'object', properties: { ... }, required: ['query'] }

normalizeResult(value)

Convert handler return to MCP content array.

InputOutput
'hello'[{ type: 'text', text: 'hello' }]
42[{ type: 'text', text: '42' }]
null[{ type: 'text', text: '' }]
[1,2,3][{ type: 'text', text: '[\n 1,\n 2,\n 3\n]' }]
{ text: 'hi' }[{ type: 'text', text: 'hi' }]
{ image: 'b64', mimeType: '...' }[{ type: 'image', data: 'b64', mimeType: '...' }]
{ resource: { uri, name?, ... } }[{ type: 'resource_link', uri, name?, ... }]
{ content: [...] }Passthrough

MCP 2025-06-18 spec. Return a reference to a resource instead of inlining data.

typescript
defineTool('get_report', {
  params: { reportId: 'string' },
  handler: async ({ reportId }) => ({
    resource: {
      uri: `report://reports/${reportId}`,
      name: `Report ${reportId}`,
      description: 'Quarterly report',
      mimeType: 'application/pdf',
    },
  }),
});

McpContent

typescript
interface McpContent {
  type: 'text' | 'image' | 'resource' | 'resource_link';
  text?: string;
  data?: string;
  mimeType?: string;
  uri?: string;          // resource_link only
  name?: string;         // resource_link only
  description?: string;  // resource_link only
}

Released under the Apache-2.0 License.