🚨 Shai-Hulud Strikes Again:834 Packages Compromised.Technical Analysis β†’
Socket
Book a DemoInstallSign in
Socket

@tscodex/mcp-sdk

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@tscodex/mcp-sdk

SDK for creating MCP servers with Extension support

latest
npmnpm
Version
0.0.6
Version published
Maintainers
1
Created
Source

@tscodex/mcp-sdk

TypeScript SDK for creating MCP (Model Context Protocol) servers with seamless Extension integration

npm version License: MIT

🎯 Overview

@tscodex/mcp-sdk is a production-ready TypeScript SDK for building MCP servers with built-in support for:

  • βœ… Type-safe APIs using TypeBox schemas
  • βœ… Configuration management with Extension integration
  • βœ… Authentication & Authorization with role-based access control
  • βœ… HTTP transport for MCP protocol
  • βœ… Security features (rate limiting, request validation, path sanitization)
  • βœ… Error handling middleware
  • βœ… Graceful shutdown handling
  • βœ… Extension endpoints (health checks, configuration)
  • βœ… AI Client for interacting with AI proxy (OpenAI, OpenRouter, Ollama, etc.)

πŸ“¦ Installation

npm install @tscodex/mcp-sdk

Requirements:

  • Node.js >= 18.0.0
  • TypeScript >= 5.0.0

πŸš€ Quick Start

Server Name Validation

Server name must:

  • Start with a Latin letter (a-z, A-Z)
  • Contain only Latin letters, numbers, hyphens (-), and underscores (_)
  • Not start with a number

Valid examples: my-server, mcp_images, server123, MyServer
Invalid examples: @tscodex/mcp-images (contains @ and /), 123server (starts with number), my server (contains space)

Minimal Server

import { McpServer, Type } from '@tscodex/mcp-sdk';

const server = new McpServer({
  name: 'hello-server',
  version: '1.0.0',
  description: 'Simple hello world MCP server'
});

// Define schema using TypeBox
const HelloSchema = Type.Object({
  name: Type.Optional(Type.String({ 
    description: 'Name to greet',
    default: 'World'
  }))
});

// Register tool with automatic type inference
server.addTool({
  name: 'hello-world',
  description: 'Greet someone with a personalized message',
  schema: HelloSchema,
  handler: async (params, context) => {
    // params is automatically typed as { name?: string }
    const name = params.name || 'World';
    return {
      content: [{
        type: 'text',
        text: `Hello, ${name}!`
      }]
    };
  }
});

// Initialize and start
await server.initialize();
await server.start();
console.log(`Server running on port ${server.serverPort}`);

πŸ“š Core Features

1. Type-Safe Configuration

import { McpServer, Type, Static } from '@tscodex/mcp-sdk';

const ConfigSchema = Type.Object({
  apiKey: Type.String({ minLength: 10 }),
  timeout: Type.Number({ default: 5000 }),
  enabled: Type.Boolean({ default: true })
});

type Config = Static<typeof ConfigSchema>;

const server = new McpServer<Config>({
  name: 'api-server',
  version: '1.0.0',
  description: 'API integration server',
  configSchema: ConfigSchema,
  loadConfig: async () => {
    // Extension config is passed via process.env.MCP_CONFIG
    const extensionConfig = process.env.MCP_CONFIG 
      ? JSON.parse(process.env.MCP_CONFIG) 
      : {};
    
    return {
      timeout: 5000,
      enabled: true,
      ...extensionConfig // Extension config takes priority
    };
  }
});

// Access configuration in handlers
server.addTool({
  name: 'api-call',
  schema: Type.Object({}),
  handler: async (params, context) => {
    // context.config contains full config including secrets
    const timeout = context.config.timeout;
    const apiKey = context.config.apiKey; // Full access to all config
    
    // If you need to return config in result, filter public parameters
    import { filterMcpPublicConfig } from '@tscodex/mcp-sdk';
    const publicConfig = filterMcpPublicConfig(context.config);
    // Only 'mcp_*' keys will be in publicConfig
    
    // ...
  }
});

2. Authentication & Authorization

import { McpServer, Type, Static } from '@tscodex/mcp-sdk';

enum Roles {
  ADMIN = 'admin',
  USER = 'user'
}

const SessionSchema = Type.Object({
  email: Type.String({ format: 'email' }),
  role: Type.Enum(Roles)
});

type Session = Static<typeof SessionSchema>;

const server = new McpServer<Config, Roles, Session>({
  name: 'secure-server',
  version: '1.0.0',
  description: 'Server with role-based access',
  auth: {
    roles: {
      admin: (session, context) => {
        // Access to loaded configuration
        const allowedAdmins = context.config.adminEmails || [];
        return session.role === Roles.ADMIN && 
               allowedAdmins.includes(session.email);
      },
      user: async (session, context) => {
        // Async checks supported
        return session.role === Roles.USER;
      }
    },
    sessionSchema: SessionSchema,
    requireSession: true,
    // Transform token from MCP_AUTH_TOKEN into full session
    loadSession: async (token, context) => {
      // Validate token and fetch user data
      const response = await fetch(`${context.config.apiUrl}/validate-token`, {
        method: 'POST',
        headers: { 'Authorization': `Bearer ${token}` }
      });
      return await response.json() as Session;
    }
  }
});

// Tools with access control
server.addTool({
  name: 'delete-file',
  description: 'Delete a file (admin only)',
  schema: Type.Object({ path: Type.String() }),
  access: [Roles.ADMIN], // Only admins can use this tool
  handler: async (params, context) => {
    // context.session is typed as Session
    console.log(`Admin ${context.session.email} deleted ${params.path}`);
    // ...
  }
});

3. Resources & Prompts

// Register resource (URI automatically prefixed with server ID)
server.addResource({
  uri: 'about', // Becomes: hello-server://about
  name: 'About',
  description: 'Server information',
  handler: async (uri, context) => {
    return {
      contents: [{
        uri, // Use normalized URI
        mimeType: 'text/plain',
        text: 'Server information...'
      }]
    };
  }
});

// Register prompt
server.addPrompt({
  name: 'explain-topic',
  description: 'Explain a topic',
  arguments: Type.Object({
    topic: Type.String({ description: 'Topic to explain' })
  }),
  handler: async (params, context) => {
    return {
      messages: [{
        role: 'user',
        content: {
          type: 'text',
          text: `Explain ${params.topic}`
        }
      }]
    };
  }
});

4. Error Handling

import type { ErrorHandler } from '@tscodex/mcp-sdk';

const errorHandler: ErrorHandler = (error, context) => {
  if (error instanceof FileNotFoundError) {
    return `File not found: ${error.path}. Please check the file path.`;
  }
  if (error instanceof PermissionError) {
    return `Access denied. Please contact administrator.`;
  }
  // Fallback to default message
  return `An error occurred while executing ${context.type} "${context.name}": ${error.message}`;
};

const server = new McpServer({
  name: 'my-server',
  version: '1.0.0',
  description: 'Server with custom error handling',
  errorHandler
});

5. Security Features

import { RateLimiter } from '@tscodex/mcp-sdk';

const server = new McpServer({
  name: 'secure-server',
  version: '1.0.0',
  description: 'Server with security features',
  securityOptions: {
    rateLimit: {
      maxRequests: 100,
      windowMs: 60000, // 1 minute
      message: 'Too many requests'
    },
    maxRequestBodySize: 10 * 1024 * 1024, // 10MB
    validateRequestSize: true
  },
  httpOptions: {
    requestTimeout: 30000,
    keepAliveTimeout: 5000
  }
});

6. Logging

const server = new McpServer({
  name: 'my-server',
  version: '1.0.0',
  description: 'Server with custom logger',
  logger: {
    info: (msg, ...args) => console.log(`[INFO] ${msg}`, ...args),
    error: (msg, ...args) => console.error(`[ERROR] ${msg}`, ...args),
    warn: (msg, ...args) => console.warn(`[WARN] ${msg}`, ...args),
    debug: (msg, ...args) => console.debug(`[DEBUG] ${msg}`, ...args)
  }
});

7. AI Client

The SDK provides an AI client for interacting with the AI proxy provided by MCP Manager. This allows your MCP server to use AI capabilities (like OpenAI, OpenRouter, Ollama) without managing API keys directly.

How it works:

When MCP Manager starts your server, it automatically injects:

  • MCP_AI_PROXY_URL - The URL of the AI proxy endpoint
  • MCP_AI_PROXY_TOKEN - A unique token for authentication

The proxy acts as a secure intermediary, providing:

  • Security - Your server never sees the real API key
  • Rate limiting - MCP Manager can limit requests per server
  • Model restrictions - Admins can control which models each server can use
  • Usage tracking - All requests are logged for monitoring
  • Centralized config - One API key configuration for all servers

Basic usage:

import { getAIClient } from '@tscodex/mcp-sdk';

const ai = getAIClient();

// Always check availability before using AI
if (await ai.isAvailable()) {
  // Simple completion
  const result = await ai.complete('Summarize this text...');

  // Or full chat API
  const response = await ai.chat({
    messages: [{ role: 'user', content: 'Hello!' }],
    temperature: 0.7,
  });
}

Using with tools:

import { McpServer, Type, getAIClient } from '@tscodex/mcp-sdk';

const server = new McpServer({ name: 'my-server' });
const ai = getAIClient();

server.addTool({
  name: 'summarize',
  description: 'Summarize text using AI',
  schema: Type.Object({
    text: Type.String({ description: 'Text to summarize' }),
  }),
  handler: async ({ text }) => {
    // Graceful degradation when AI is not available
    if (!await ai.isAvailable()) {
      return {
        content: [{ type: 'text', text: 'AI summarization is not available' }],
        isError: true,
      };
    }

    const summary = await ai.completeWithSystem(
      'You are a helpful assistant that creates concise summaries.',
      `Please summarize the following text:\n\n${text}`
    );

    return {
      content: [{ type: 'text', text: summary }],
    };
  },
});

Error handling:

import { getAIClient, AIClientError } from '@tscodex/mcp-sdk';

const ai = getAIClient();

try {
  const result = await ai.complete('Hello');
} catch (error) {
  if (error instanceof AIClientError) {
    switch (error.code) {
      case 'NOT_CONFIGURED':
        // AI proxy URL or token not set
        break;
      case 'UNAUTHORIZED':
        // Token invalid or expired (server restarted?)
        break;
      case 'RATE_LIMITED':
        // Too many requests, try again later
        break;
      case 'API_ERROR':
        // Upstream API error
        break;
      case 'TIMEOUT':
        // Request timed out
        break;
      case 'NETWORK_ERROR':
        // Network connectivity issue
        break;
    }
  }
}

Custom configuration:

import { createAIClient } from '@tscodex/mcp-sdk';

// Create client with custom options
const ai = createAIClient({
  defaultModel: 'gpt-4',      // Default model for all requests
  timeout: 60000,              // 60 second timeout
  // baseUrl and token are still read from env by default
});

Available methods:

// Check if AI is configured (synchronous)
ai.isConfigured(): boolean;

// Check if AI proxy is available (async, cached)
await ai.isAvailable(forceCheck?: boolean): Promise<boolean>;

// Get available models
await ai.getModels(): Promise<ModelsResponse>;

// Full chat completion API
await ai.chat(options: ChatCompletionOptions): Promise<ChatCompletion>;

// Simple single-prompt completion
await ai.complete(prompt: string, options?: ChatCompletionOptions): Promise<string>;

// System + user prompt pattern
await ai.completeWithSystem(
  systemPrompt: string,
  userPrompt: string,
  options?: ChatCompletionOptions
): Promise<string>;

// Reset availability cache (useful after config changes)
ai.resetAvailabilityCache(): void;

πŸ“– API Reference

McpServer<TConfig, TRoles, TSession>

Main server class.

Constructor

interface McpServerOptions<TConfig, TRoles, TSession> {
  // REQUIRED
  name: string;                    // Unique server name (must start with Latin letter, 
                                   // contain only letters, numbers, hyphens, underscores)
  version: string;                 // Version (semver)
  description: string;             // Server description
  
  // OPTIONAL
  id?: string;                     // Server ID for resource prefix (auto-generated from name)
  configSchema?: TSchema;          // TypeBox schema for configuration
  loadConfig?: () => Promise<TConfig>; // Load local configuration
  auth?: AuthConfig<TRoles, TSession, TConfig>; // Authentication config
  mcpPath?: string;                // MCP endpoint path (default: '/mcp')
  corsOptions?: CorsOptions;       // CORS configuration
  httpOptions?: ServerHttpOptions;  // HTTP server options
  securityOptions?: ServerSecurityOptions; // Security options
  handlerOptions?: ServerHandlerOptions; // Handler timeout options
  errorHandler?: ErrorHandler<TConfig, TSession>; // Error handler
  logger?: Logger;                 // Custom logger
}

Methods

// Initialization
await server.initialize(): Promise<void>;
await server.start(): Promise<void>;
await server.stop(): Promise<void>;

// Tool registration
server.addTool<TSchemaType>(config: ToolConfig<TSchemaType, TConfig, TRoles, TSession>): void;

// Resource registration
server.addResource(config: ResourceConfig<TConfig, TRoles, TSession>): void;

// Prompt registration
server.addPrompt<TSchemaType>(config: PromptConfig<TSchemaType, TConfig, TRoles, TSession>): void;

// Access methods
server.getConfig(): TConfig;
server.getProjectRoot(): string | undefined;
server.getSession(): TSession | undefined;
server.getTools(): string[];
server.getResources(): string[];
server.getPrompts(): string[];
server.getMetadata(): ServerMetadata;  // Get server metadata (tools, resources, prompts, config schema)

// Properties
server.serverId: string;           // Server ID (resource prefix)
server.serverPort: number;         // Server port
server.serverHost: string;         // Server host
server.running: boolean;             // Is server running

Events

server.on('initialized', () => {});
server.on('started', (port: number, host: string) => {});
server.on('stopped', () => {});
server.on('error', (error: Error) => {});
server.on('toolRegistered', (name: string) => {});
server.on('toolCalled', (name: string, params: any, result: any) => {});
server.on('toolError', (name: string, params: any, error: Error) => {});
server.on('resourceRegistered', (uri: string) => {});
server.on('resourceRead', (uri: string, result: any) => {});
server.on('resourceError', (uri: string, error: Error) => {});
server.on('promptRegistered', (name: string) => {});
server.on('promptCalled', (name: string, params: any, result: any) => {});
server.on('promptError', (name: string, params: any, error: Error) => {});
server.on('projectRootChanged', (newRoot: string, previousRoot: string) => {});

🌐 Extension Integration

The SDK is designed to work seamlessly with Cursor/VSCode Extensions.

Metadata Mode (--meta flag)

SDK supports metadata mode for Extension integration. When started with --meta or --metadata flag:

  • Server outputs only JSON metadata to stdout (no logs)
  • All logs are redirected to stderr
  • Server exits after outputting metadata (doesn't start HTTP server)
  • Useful for Extension to discover server capabilities without starting the server

Usage:

node dist/index.js --meta
# or
node dist/index.js --metadata

Programmatic usage:

await server.initialize();
const metadata = server.getMetadata();
console.log(JSON.stringify(metadata, null, 2));

Environment Variables

Extension automatically passes configuration via environment variables:

  • MCP_PORT - Server port (default: 3848)
  • MCP_HOST - Server host (default: '0.0.0.0')
  • MCP_PROJECT_ROOT - Workspace root directory
  • MCP_CONFIG - Configuration as JSON string
  • MCP_AUTH_TOKEN - Authentication token/key (for auth-enabled servers)
  • MCP_PATH - MCP endpoint path (default: '/mcp')
  • MCP_AI_PROXY_URL - AI proxy endpoint URL (for AI Client)
  • MCP_AI_PROXY_TOKEN - AI proxy authentication token (for AI Client)

Fallback Support: SDK supports fallback to non-prefixed environment variables for server settings:

  • MCP_HOST β†’ HOST (if MCP_HOST is not set)
  • MCP_PORT β†’ PORT (if MCP_PORT is not set)
  • MCP_PROJECT_ROOT β†’ CURSOR_WORKSPACE β†’ PROJECT_ROOT (if MCP_PROJECT_ROOT is not set)

Priority order: MCP_* env vars β†’ non-prefixed env vars β†’ CLI arguments β†’ defaults

Important: Only environment variables with MCP_ prefix are loaded into application configuration (via loadConfig). This prevents accidental exposure of system environment variables. For example, use MCP_TIMEOUT=5000 instead of TIMEOUT=5000. However, server settings (host, port, project root) support fallback to non-prefixed variables for convenience.

Extension Endpoints

SDK automatically creates endpoints for Extension:

  • GET /health - Health check with server information
  • GET /gateway/metadata - Get server metadata (tools, resources, prompts, config schema)
  • POST /gateway/config/project-root - Update project root
  • GET /gateway/config/current - Get current configuration (public config only)
  • POST /gateway/config - Update configuration dynamically (deep merge)

Configuration Management

Important: Extension configuration is updated only by restarting the process with new environment variables. The SDK provides read-only access via getConfig().

Public MCP Configuration Parameters

Handlers receive full configuration (including secrets) in context.config for use in code. SDK provides filterMcpPublicConfig() utility to help handlers return only public MCP parameters (mcp_* keys) in their results.

Example:

import { filterMcpPublicConfig } from '@tscodex/mcp-sdk';

// In your config schema, define public MCP parameters with 'mcp_' prefix
const ConfigSchema = Type.Object({
  // Public MCP parameters (safe to return in results)
  mcp_timeout: Type.Number({ default: 5000 }),
  mcp_api_url: Type.String({ default: 'https://api.example.com' }),
  
  // Private parameters (secrets - use in code but don't return in results)
  apiKey: Type.String(), // Secret - use in code, filter before returning
  databasePassword: Type.String() // Secret - use in code, filter before returning
});

// In handler
handler: async (params, context) => {
  // Full config access (including secrets) - use freely in code
  const timeout = context.config.timeout; // βœ… Available
  const apiKey = context.config.apiKey; // βœ… Available - use for API calls
  
  // If returning config in result, filter to public parameters only
  const publicConfig = filterMcpPublicConfig(context.config);
  // publicConfig contains only 'mcp_*' keys
  
  return {
    content: [{
      type: 'text',
      text: JSON.stringify(publicConfig) // Safe - only public params
    }]
  };
}

This design ensures that:

  • Full config (including secrets) is accessible in handlers for use in code
  • Public parameters (mcp_* keys) can be safely returned in results using filterMcpPublicConfig()
  • Responsibility for not leaking secrets in MCP responses lies with handlers

πŸ”€ Per-Request Context (Multi-Workspace Support)

When multiple workspaces share a single MCP server process, the SDK supports per-request context via HTTP headers. This allows each request to have its own projectRoot and workspaceId.

How It Works

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Workspace A   β”‚     β”‚   MCP Gateway   β”‚     β”‚   MCP Server    β”‚
β”‚  /projects/foo  │────▢│  (Proxy Layer)  │────▢│   (Shared)      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β”‚                 β”‚     β”‚                 β”‚
                        β”‚  Adds headers:  β”‚     β”‚  Reads headers  β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”‚  X-MCP-Project- β”‚     β”‚  via AsyncLocal β”‚
β”‚   Workspace B   │────▢│    Root         β”‚     β”‚  Storage        β”‚
β”‚  /projects/bar  β”‚     β”‚  X-MCP-Workspaceβ”‚     β”‚                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β”‚    -Id          β”‚     β”‚                 β”‚
                        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

HTTP Headers

The SDK recognizes these headers for per-request context:

HeaderDescriptionPriority
X-MCP-Project-RootWorkspace project root pathOverrides MCP_PROJECT_ROOT env
X-MCP-Workspace-IdWorkspace identifier (optional)Informational

Context Priority

projectRoot is resolved with the following priority:

  • Per-request header (X-MCP-Project-Root) - highest priority
  • Server-level environment (MCP_PROJECT_ROOT)
  • undefined if neither is set

Usage in Handlers

server.addTool({
  name: 'list-files',
  schema: Type.Object({}),
  handler: async (params, context) => {
    // projectRoot automatically reflects per-request header
    // or falls back to server-level MCP_PROJECT_ROOT
    const root = context.projectRoot;

    // workspaceId is available for logging/caching (optional)
    const wsId = context.workspaceId;

    if (!root) {
      return { content: [{ type: 'text', text: 'No project root configured' }] };
    }

    // Files are resolved relative to the correct workspace
    const files = await fs.readdir(root);
    return {
      content: [{ type: 'text', text: files.join('\n') }]
    };
  }
});

Implementation Details

The SDK uses Node.js AsyncLocalStorage to propagate request context through the async call stack. This allows handlers to access per-request headers even though the official MCP SDK doesn't support custom context in handlers.

// Available exports for advanced usage
import {
  getRequestContext,      // Get current request context
  extractRequestContext,  // Extract context from HTTP request
  requestContextStorage   // AsyncLocalStorage instance
} from '@tscodex/mcp-sdk';

// In custom middleware or transport
const reqContext = extractRequestContext(httpRequest);
// reqContext = { projectRoot?: string, workspaceId?: string }

Backward Compatibility

  • Servers that don't receive these headers continue to work normally
  • projectRoot falls back to MCP_PROJECT_ROOT environment variable
  • workspaceId is undefined when not provided
  • Existing plugins don't need any changes

🏷️ Custom Context Headers

SDK allows servers to declare custom context headers that are passed with each request. This enables workspace-specific data (like project IDs, API keys, etc.) to be sent dynamically without modifying the server configuration.

Declaring Context Headers

const server = new McpServer({
  name: 'my-server',
  version: '1.0.0',
  description: 'Server with custom context headers',
  // Declare which headers this server accepts
  contextHeaders: ['project-id', 'api-key', 'region']
});

How It Works

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   MCP Manager   β”‚     β”‚   MCP Gateway   β”‚     β”‚   MCP Server    β”‚
β”‚  UI shows form  β”‚     β”‚                 β”‚     β”‚                 β”‚
β”‚  for headers:   │────▢│  Adds headers:  │────▢│  Receives in    β”‚
β”‚  [project-id]   β”‚     β”‚  X-MCP-CTX-     β”‚     β”‚  context.       β”‚
β”‚  [api-key]      β”‚     β”‚    project-id   β”‚     β”‚  contextHeaders β”‚
β”‚  [region]       β”‚     β”‚  X-MCP-CTX-     β”‚     β”‚                 β”‚
β”‚                 β”‚     β”‚    api-key      β”‚     β”‚                 β”‚
β”‚                 β”‚     β”‚  X-MCP-CTX-     β”‚     β”‚                 β”‚
β”‚                 β”‚     β”‚    region       β”‚     β”‚                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Using in Handlers

server.addTool({
  name: 'get-project-data',
  description: 'Fetch data for specific project',
  schema: Type.Object({}),
  handler: async (params, context) => {
    // Access context headers from the request
    const projectId = context.contextHeaders?.['project-id'];
    const apiKey = context.contextHeaders?.['api-key'];
    const region = context.contextHeaders?.['region'] || 'us-east-1';

    if (!projectId) {
      return {
        content: [{
          type: 'text',
          text: 'Error: project-id header is required. Configure it in workspace settings.'
        }]
      };
    }

    // Use the values for your business logic
    const data = await fetchProjectData(projectId, apiKey, region);

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

HTTP Headers Format

Context headers are passed with the X-MCP-CTX- prefix:

Declared HeaderHTTP Headercontext.contextHeaders Key
project-idX-MCP-CTX-project-idproject-id
api-keyX-MCP-CTX-api-keyapi-key
RegionX-MCP-CTX-Regionregion (lowercase)

Metadata Exposure

Declared context headers are included in server metadata (getMetadata()), allowing MCP Manager to automatically show configuration UI for each workspace:

const metadata = server.getMetadata();
// metadata.contextHeaders = ['project-id', 'api-key', 'region']

Use Cases

  • Multi-tenant applications: Pass tenant ID per workspace
  • External service integration: Pass project/account IDs to map workspaces to external services
  • Region selection: Allow different regions per workspace
  • API key override: Different API keys for different workspaces

πŸ”§ Utilities

Security Utilities

import { safePath, isPathSafe, sanitizeFilename, RateLimiter } from '@tscodex/mcp-sdk';

// Safe path resolution
const safe = safePath('/workspace', userPath); // Prevents path traversal

// Path validation
if (isPathSafe(userPath)) {
  // Safe to use
}

// Filename sanitization
const safe = sanitizeFilename(userFilename);

// Rate limiter
const limiter = new RateLimiter({
  maxRequests: 100,
  windowMs: 60000
});

Configuration Utilities

import { validateConfig, updateConfig } from '@tscodex/mcp-sdk';

// Validate configuration against schema
const config = validateConfig<Config>(data, schema);

// Deep merge configurations
const merged = updateConfig(defaultConfig, extensionConfig);

πŸ“š Documentation

πŸ“ Examples

Check the examples/ directory for complete examples:

  • basic-server.ts - Minimal server setup
  • with-config.ts - Configuration management
  • with-auth.ts - Authentication & authorization
  • with-error-handler.ts - Custom error handling
  • file-server.ts - File operations example
  • with-ai-client.ts - AI Client integration example

Run examples:

tsx examples/basic-server.ts
tsx examples/with-config.ts
tsx examples/with-auth.ts

πŸ—οΈ Architecture

Initialization Flow

1. Extension starts process
   ↓ (env vars: MCP_PORT, MCP_HOST, MCP_PROJECT_ROOT, MCP_CONFIG, MCP_AUTH_TOKEN)
   
2. new McpServer(options)
   - Reads port/host/projectRoot from env vars
   - Creates HTTP Server
   - Creates MCP Server instance
   
3. server.initialize()
   - Loads configuration from MCP_CONFIG
   - Calls loadConfig() for local settings
   - Merges configurations (Extension takes priority)
   - Validates via configSchema
   - Loads session if auth is configured
   - Filters tools/resources/prompts by access
   - Sets up Extension endpoints
   - Registers MCP handlers
   
4. server.addTool/addResource/addPrompt
   - Register functionality
   
5. server.start()
   - Starts HTTP Server
   - Sets up graceful shutdown handlers
   
6. Server running
   - Handles MCP requests
   - Provides Extension endpoints
   - Configuration and session available via context

Project Structure

@tscodex/mcp-sdk/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ server.ts          # McpServer class
β”‚   β”œβ”€β”€ types.ts           # TypeScript types
β”‚   β”œβ”€β”€ config.ts          # Configuration management
β”‚   β”œβ”€β”€ transport.ts        # HTTP transport
β”‚   β”œβ”€β”€ security.ts         # Security utilities
β”‚   β”œβ”€β”€ extension.ts        # Extension types
β”‚   └── index.ts           # Main exports
β”œβ”€β”€ examples/              # Example servers
└── dist/                 # Compiled output

πŸ” Security Best Practices

  • Always validate user input using TypeBox schemas
  • Use safePath() for file operations to prevent path traversal
  • Enable rate limiting for production servers
  • Sanitize filenames using sanitizeFilename()
  • Validate request sizes to prevent DoS attacks
  • Use HTTPS in production (configured at transport level)
  • Implement proper authentication for sensitive operations

πŸ“„ License

MIT Β© unbywyd

🀝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Version: 0.2.0
Status: Production Ready

Keywords

mcp

FAQs

Package last updated on 06 Dec 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts