CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-modelcontextprotocol--sdk

TypeScript SDK for implementing the Model Context Protocol, enabling developers to build MCP servers and clients with support for multiple transports, tools, resources, prompts, and authentication

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

server-tools.mddocs/

Server Tools API

Tools enable LLMs to take actions through your MCP server. Tools are model-controlled - AI decides which tools to call and with what arguments.

Registration

Modern API (Recommended)

server.registerTool<InputArgs extends ZodRawShape, OutputArgs extends ZodRawShape>(
  name: string,
  config: {
    title?: string;
    description?: string;
    inputSchema?: InputArgs;
    outputSchema?: OutputArgs;
    annotations?: ToolAnnotations;
    _meta?: Record<string, unknown>;
  },
  callback: (args: z.infer<ZodObject<InputArgs>>, extra: RequestHandlerExtra) => Promise<CallToolResult> | CallToolResult
): RegisteredTool;

Examples

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';

const server = new McpServer({ name: 'tools-server', version: '1.0.0' });

// Simple tool
server.registerTool('add', {
  title: 'Addition Tool',
  description: 'Add two numbers together',
  inputSchema: {
    a: z.number().describe('First number'),
    b: z.number().describe('Second number')
  },
  outputSchema: {
    result: z.number().describe('Sum of the two numbers')
  }
}, async ({ a, b }) => {
  const output = { result: a + b };
  return {
    content: [{ type: 'text', text: JSON.stringify(output) }],
    structuredContent: output
  };
});

// Tool with annotations
server.registerTool('fetch-data', {
  title: 'Fetch External Data',
  description: 'Fetch data from an external API',
  inputSchema: {
    url: z.string().url(),
    method: z.enum(['GET', 'POST']).optional()
  },
  outputSchema: {
    data: z.unknown(),
    status: z.number()
  },
  annotations: {
    audience: ['user']
  }
}, async ({ url, method = 'GET' }) => {
  const response = await fetch(url, { method });
  const data = await response.json();
  const output = { data, status: response.status };
  return {
    content: [{ type: 'text', text: JSON.stringify(output) }],
    structuredContent: output
  };
});

Return Values

interface CallToolResult {
  content: ContentBlock[];        // Required: Content to return
  isError?: boolean;              // Optional: Indicates error
  structuredContent?: Record<string, unknown>;  // Required if outputSchema defined
  _meta?: Record<string, unknown>;
}

Content Block Types

interface TextContent {
  type: 'text';
  text: string;
  annotations?: { audience?: ('user' | 'assistant')[]; priority?: number; };
}

interface ImageContent { type: 'image'; data: string; mimeType: string; }
interface AudioContent { type: 'audio'; data: string; mimeType: string; }
interface EmbeddedResource {
  type: 'resource';
  resource: { uri: string; mimeType?: string; text?: string; blob?: string; };
}
interface ResourceLink {
  type: 'resource_link';
  uri: string;
  name?: string;
  mimeType?: string;
  description?: string;
}

type ContentBlock = TextContent | ImageContent | AudioContent | EmbeddedResource | ResourceLink;

Return Examples

// Simple text
return {
  content: [{ type: 'text', text: 'Operation completed successfully' }]
};

// With structured content (required if outputSchema defined)
return {
  content: [{ type: 'text', text: JSON.stringify({ count: 42 }) }],
  structuredContent: { count: 42 }
};

// Error result
return {
  content: [{ type: 'text', text: 'Failed to process request' }],
  isError: true
};

// Multiple content blocks with resource links
return {
  content: [
    { type: 'text', text: 'Found 2 files' },
    { type: 'resource_link', uri: 'file:///project/README.md', name: 'README.md', mimeType: 'text/markdown' },
    { type: 'resource_link', uri: 'file:///project/index.ts', name: 'index.ts', mimeType: 'text/typescript' }
  ],
  structuredContent: { count: 2, files: ['README.md', 'index.ts'] }
};

Callback Signature

type ToolCallback<Args extends ZodRawShape = undefined> = Args extends undefined
  ? (extra: RequestHandlerExtra) => Promise<CallToolResult> | CallToolResult
  : (args: z.infer<ZodObject<Args>>, extra: RequestHandlerExtra) => Promise<CallToolResult> | CallToolResult;

interface RequestHandlerExtra {
  signal: AbortSignal;                    // Cancellation signal
  authInfo?: AuthInfo;                     // Validated access token info
  sessionId?: string;                      // Session ID from transport
  _meta?: RequestMeta;                     // Request metadata
  requestId: RequestId;                    // JSON-RPC request ID
  requestInfo?: RequestInfo;               // HTTP request details
  sendNotification: (notification: SendNotificationT) => Promise<void>;
  sendRequest: <U extends ZodType<object>>(request: SendRequestT, resultSchema: U, options?: RequestOptions) => Promise<z.infer<U>>;
}

Managing Tools

interface RegisteredTool {
  enable(): void;                          // Make visible in listTools
  disable(): void;                         // Hide from listTools
  update(updates: Partial<ToolConfig>): void;  // Update configuration
  remove(): void;                          // Remove entirely
}

Dynamic Control Example

const tool = server.registerTool('admin-action', config, callback);

tool.disable();  // Initially disabled

// After authentication
tool.enable();

// Update description
tool.update({
  description: 'Admin action (authenticated as user@example.com)'
});

// Remove when no longer needed
tool.remove();

Error Handling

server.registerTool('risky-operation', {
  title: 'Risky Operation',
  description: 'An operation that might fail',
  inputSchema: { id: z.string() }
}, async ({ id }) => {
  try {
    const result = await performRiskyOperation(id);
    return {
      content: [{ type: 'text', text: `Success: ${result}` }],
      structuredContent: { success: true, result }
    };
  } catch (error) {
    return {
      content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}` }],
      isError: true
    };
  }
});

Validation

  • Input arguments are automatically validated before callback invocation
  • Invalid inputs result in ErrorCode.InvalidParams error
  • Output structuredContent is validated against outputSchema if provided
  • Missing structuredContent when outputSchema is defined results in error

Notifications

server.sendToolListChanged(): void;  // Notify clients of tool list changes

Tools are automatically notified when added, removed, enabled, or disabled (if client supports capability).

Types Reference

interface Tool {
  name: string;
  title?: string;
  description?: string;
  inputSchema: JSONSchema;
  outputSchema?: JSONSchema;
  annotations?: ToolAnnotations;
  _meta?: Record<string, unknown>;
}

interface ToolAnnotations {
  audience?: ('user' | 'assistant')[];
  [key: string]: unknown;
}

interface ListToolsResult {
  tools: Tool[];
  nextCursor?: string;
}

Legacy API

server.tool(name: string, callback: ToolCallback): RegisteredTool;
server.tool(name: string, description: string, callback: ToolCallback): RegisteredTool;
server.tool<Args extends ZodRawShape>(name: string, paramsSchema: Args, callback: ToolCallback<Args>): RegisteredTool;
server.tool<Args extends ZodRawShape>(name: string, description: string, paramsSchema: Args, callback: ToolCallback<Args>): RegisteredTool;

Use registerTool for new code.

docs

authentication.md

client.md

index.md

server-advanced.md

server-prompts.md

server-resources.md

server-tools.md

transports.md

types.md

tile.json