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-advanced.mddocs/

Server Advanced Features

Advanced capabilities: sampling (LLM completions), elicitation (user input), dynamic capability management, and low-level protocol access.

Sampling (LLM Completions)

Servers can request LLM completions from clients, enabling tools to use AI capabilities.

async createMessage(
  params: {
    messages: SamplingMessage[];              // Messages for the LLM
    maxTokens?: number;                       // Maximum tokens to generate
    modelPreferences?: ModelPreferences;      // Model preferences
    systemPrompt?: string;                    // System prompt
    includeContext?: 'none' | 'thisServer' | 'allServers';
    temperature?: number;                     // Temperature (0-1)
    stopSequences?: string[];                 // Stop sequences
    metadata?: Record<string, unknown>;       // Request metadata
  },
  options?: RequestOptions
): Promise<CreateMessageResult>;

interface SamplingMessage {
  role: 'user' | 'assistant';
  content: TextContent | ImageContent | AudioContent;
}

interface ModelPreferences {
  hints?: ModelHint[];                        // Prioritized model hints
  costPriority?: number;                      // 0-1, higher = prefer cheaper
  speedPriority?: number;                     // 0-1, higher = prefer faster
  intelligencePriority?: number;              // 0-1, higher = prefer smarter
}

interface CreateMessageResult {
  role: 'assistant';
  content: TextContent | ImageContent | AudioContent;
  model: string;
  stopReason?: 'endTurn' | 'stopSequence' | 'maxTokens';
  _meta?: Record<string, unknown>;
}

Example

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

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

server.registerTool('summarize', {
  title: 'Summarize Text',
  description: 'Summarize text using an LLM',
  inputSchema: { text: z.string().describe('Text to summarize') },
  outputSchema: { summary: z.string() }
}, async ({ text }) => {
  const response = await server.server.createMessage({
    messages: [{
      role: 'user',
      content: { type: 'text', text: `Please summarize the following text concisely:\n\n${text}` }
    }],
    maxTokens: 500,
    temperature: 0.7
  });

  const summary = response.content.type === 'text' ? response.content.text : 'Unable to generate summary';
  const output = { summary };

  return {
    content: [{ type: 'text', text: JSON.stringify(output) }],
    structuredContent: output
  };
});

Elicitation (User Input)

Servers can request structured input from users through forms.

async elicitInput(
  params: {
    message: string;                          // Message to display to user
    requestedSchema: JSONSchema;              // JSON Schema describing requested input
  },
  options?: RequestOptions
): Promise<ElicitResult>;

interface ElicitResult {
  action: 'accept' | 'decline' | 'cancel';
  content?: Record<string, unknown>;          // User-provided content if action is 'accept'
  _meta?: Record<string, unknown>;
}

Examples

// User confirmation
server.registerTool('delete-file', {
  title: 'Delete File',
  description: 'Delete a file with user confirmation',
  inputSchema: { filePath: z.string() },
  outputSchema: { deleted: z.boolean() }
}, async ({ filePath }) => {
  const result = await server.server.elicitInput({
    message: `Are you sure you want to delete ${filePath}?`,
    requestedSchema: {
      type: 'object',
      properties: {
        confirm: { type: 'boolean', title: 'Confirm deletion', description: 'Check to confirm file deletion' }
      },
      required: ['confirm']
    }
  });

  let output;
  if (result.action === 'accept' && result.content?.confirm) {
    await fs.unlink(filePath);
    output = { deleted: true };
  } else {
    output = { deleted: false };
  }

  return {
    content: [{ type: 'text', text: JSON.stringify(output) }],
    structuredContent: output
  };
});

// Restaurant booking with alternatives
server.registerTool('book-restaurant', {
  title: 'Book Restaurant',
  description: 'Book a table at a restaurant',
  inputSchema: { restaurant: z.string(), date: z.string(), partySize: z.number() }
}, async ({ restaurant, date, partySize }) => {
  const available = await checkAvailability(restaurant, date, partySize);

  if (!available) {
    const result = await server.server.elicitInput({
      message: `No tables available at ${restaurant} on ${date}. Would you like to check alternative dates?`,
      requestedSchema: {
        type: 'object',
        properties: {
          checkAlternatives: { type: 'boolean', title: 'Check alternative dates' },
          flexibleDates: {
            type: 'string',
            title: 'Date flexibility',
            enum: ['next_day', 'same_week', 'next_week'],
            enumNames: ['Next day', 'Same week', 'Next week']
          }
        },
        required: ['checkAlternatives']
      }
    });

    if (result.action === 'accept' && result.content?.checkAlternatives) {
      const alternatives = await findAlternatives(restaurant, date, partySize, result.content.flexibleDates);
      return {
        content: [{ type: 'text', text: `Alternative dates: ${alternatives.join(', ')}` }],
        structuredContent: { alternatives }
      };
    }
  }

  await makeBooking(restaurant, date, partySize);
  return {
    content: [{ type: 'text', text: 'Booking confirmed' }],
    structuredContent: { success: true }
  };
});

Dynamic Capability Management

Tools, resources, and prompts can be controlled at runtime:

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

Example

// Register tools with different access levels
const readTool = server.registerTool('read-data', readConfig, readCallback);
const writeTool = server.registerTool('write-data', writeConfig, writeCallback);
const adminTool = server.registerTool('admin-action', adminConfig, adminCallback);

// Initially, only read access
writeTool.disable();
adminTool.disable();

// After authentication, enable based on role
function updatePermissions(userRole: string) {
  if (userRole === 'editor') {
    writeTool.enable();
  } else if (userRole === 'admin') {
    writeTool.enable();
    adminTool.enable();
  }
  server.sendToolListChanged();
}

// Dynamic tool update
readTool.update({ description: `Read data (authenticated as ${username})` });

Notification Debouncing

Reduce network traffic by coalescing rapid notifications:

const server = new McpServer(
  { name: 'efficient-server', version: '1.0.0' },
  {
    debouncedNotificationMethods: [
      'notifications/tools/list_changed',
      'notifications/resources/list_changed',
      'notifications/prompts/list_changed'
    ]
  }
);

// Bulk updates send only one notification per type
for (let i = 0; i < 100; i++) {
  server.registerTool(`tool${i}`, config, callback).disable();
}
// Only one notifications/tools/list_changed sent

Low-Level Server Access

const mcpServer = new McpServer({ name: 'my-server', version: '1.0.0' });
const lowLevelServer = mcpServer.server;  // Access underlying Server instance

// Set custom request handlers
lowLevelServer.setRequestHandler(CustomRequestSchema, async (request, extra) => {
  return customResult;
});

// Set custom notification handlers
lowLevelServer.setNotificationHandler(CustomNotificationSchema, async (notification) => {
  // Handle notification
});

// Send custom requests to client
const result = await lowLevelServer.request({ method: 'custom/method', params: {} }, CustomResultSchema);

// Send custom notifications to client
await lowLevelServer.notification({ method: 'custom/notification', params: {} });

Protocol Base Class

import { Protocol, ProtocolOptions, RequestOptions, NotificationOptions } from '@modelcontextprotocol/sdk/shared/protocol.js';

abstract class Protocol<RequestT, NotificationT, ResultT> {
  protected transport?: Transport;

  setRequestHandler<T extends ZodType<object>>(schema: T, handler: (request: z.infer<T>, extra: RequestHandlerExtra) => Promise<Result> | Result): void;
  setNotificationHandler<T extends ZodType<object>>(schema: T, handler: (notification: z.infer<T>) => Promise<void> | void): void;
  request<U extends ZodType<object>>(request: RequestT, resultSchema: U, options?: RequestOptions): Promise<z.infer<U>>;
  notification(notification: NotificationT, options?: NotificationOptions): Promise<void>;
  close(): Promise<void>;
  connect(transport: Transport): Promise<void>;
}

interface ProtocolOptions {
  enforceStrictCapabilities?: boolean;        // Default: true
  debouncedNotificationMethods?: string[];
}

Client Information

server.getClientCapabilities(): ClientCapabilities | undefined;
server.getClientVersion(): Implementation | undefined;

Logging

async sendLoggingMessage(
  params: {
    level: LoggingLevel;
    logger?: string;
    data: unknown;
  },
  sessionId?: string
): Promise<void>;

type LoggingLevel = 'debug' | 'info' | 'notice' | 'warning' | 'error' | 'critical' | 'alert' | 'emergency';

Example

await server.sendLoggingMessage({
  level: 'info',
  logger: 'my-tool',
  data: 'Operation completed successfully'
});

await server.sendLoggingMessage({
  level: 'error',
  logger: 'database',
  data: { error: 'Connection failed', code: 'ECONNREFUSED' }
});

Roots Management

async listRoots(params?: Record<string, unknown>, options?: RequestOptions): Promise<ListRootsResult>;

interface ListRootsResult {
  roots: Root[];
  _meta?: Record<string, unknown>;
}

interface Root {
  uri: string;
  name: string;
}

Connection Management

isConnected(): boolean;
async close(): Promise<void>;

Lifecycle Callbacks

server.server.oninitialized = () => { console.log('Client initialized'); };
server.server.onclose = () => { console.log('Connection closed'); };
server.server.onerror = (error) => { console.error('Server error:', error); };

Types Reference

interface RequestHandlerExtra {
  request: <U>(request: Request, resultSchema: U, options?: RequestOptions) => Promise<Result>;
  notification: (notification: Notification, options?: NotificationOptions) => Promise<void>;
  sessionId?: string;
  requestInfo?: RequestInfo;
  signal: AbortSignal;
}

interface RequestOptions {
  timeout?: number;
  onprogress?: (progress: Progress) => void;
  signal?: AbortSignal;
}

interface NotificationOptions {
  priority?: number;
}

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