npm package: @meldscience/mcp-tool-manager
Description: Tool manager for MCP servers
Author: Adam Avenir
Latest version: 0.3.2

# mcp-tool-manager

Tool server manager for Model Context Protocol (MCP). This package provides a TypeScript implementation for managing MCP tool servers, handling server lifecycle, service discovery, and executing tool requests.

## Features

- Manage multiple MCP tool servers
- File-based service discovery
- Hot-reload server configurations
- Server health monitoring
- Comprehensive logging with secret masking
- Compatible with Claude Desktop App configurations
- Built on the MCP TypeScript SDK
- Real-time server status events
- Tool access control and usage tracking
- Rate limiting support
- CommonJS and ESM compatibility

## Installation

```bash
npm install @meldscience/mcp-tool-manager
```

## Usage

You can provide server configurations either directly in code or via a JSON configuration file.

### Module System Compatibility

This package is built as a CommonJS module but is fully compatible with both CommonJS and ESM projects through TypeScript's module resolution. You can use either `require()` or `import` syntax in your code:

```typescript
// ESM
import { ToolManager } from '@meldscience/mcp-tool-manager';

// CommonJS
const { ToolManager } = require('@meldscience/mcp-tool-manager');
```

### Configuration in Code

```typescript
import { ToolManager } from '@meldscience/mcp-tool-manager';

// Provide server configurations directly
const manager = new ToolManager({
  mcpServers: {
    // Each key is a unique identifier for the server
    'my-server': {
      command: 'npx',  // Command to run
      args: ['my-mcp-server'],  // Arguments for the command (optional)
      env: {  // Optional environment variables
        API_KEY: process.env.MY_API_KEY
      },
      cwd: '/path/to/working/dir'  // Optional working directory
    },
    'another-server': {
      command: 'uvx',
      args: ['run', 'another-server', '--port', '3000']
    }
  },
  // Optional service discovery configuration
  discovery: {
    type: 'file',  // 'file' | 'redis' | 'http'
    options: {
      path: '/path/to/discovery.json'  // Required for file-based discovery
    }
  },
  // Optional Winston logger configuration
  loggerOptions: {
    level: 'info',
    // Other Winston options...
  }
});
```

### Configuration from File

```typescript
import { ToolManager } from '@meldscience/mcp-tool-manager';
import { readFileSync } from 'fs';

// Load configuration from a JSON file
const config = JSON.parse(readFileSync('./mcp-server-config.json', 'utf-8'));
const manager = new ToolManager(config);
```

Example `mcp-server-config.json`:
```json
{
  "mcpServers": {
    "my-server": {
      "command": "npx",
      "args": ["my-mcp-server"],
      "env": {
        "API_KEY": "your-api-key"
      },
      "cwd": "/path/to/working/dir"
    }
  },
  "discovery": {
    "type": "file",
    "options": {
      "path": "/path/to/discovery.json"
    }
  },
  "loggerOptions": {
    "level": "info"
  }
}
```

### Configuration Types

```typescript
// Server configuration
interface MCPServerConfig {
  // Command to run (e.g., 'npx', 'uvx', './run.sh')
  command: string;
  
  // Optional arguments for the command
  args?: string[];
  
  // Optional environment variables
  env?: Record<string, string>;
  
  // Optional working directory
  cwd?: string;

  // Optional startup timeout in milliseconds
  // Default: 30000 (30 seconds)
  // Increase this when using npx to install packages
  startupTimeout?: number;
}

// Service discovery configuration
interface DiscoveryConfig {
  // Discovery provider type
  type: 'file' | 'redis' | 'http';
  
  // Provider-specific options
  options: {
    path?: string;      // Required for file-based discovery
    url?: string;       // Required for HTTP discovery
    redisUrl?: string;  // Required for Redis discovery
  };
}

// Tool manager configuration
interface ToolManagerConfig {
  // Map of server names to their configurations
  mcpServers: Record<string, MCPServerConfig>;
  
  // Optional service discovery configuration
  discovery?: DiscoveryConfig;
  
  // Optional Winston logger configuration
  loggerOptions?: LoggerOptions;
}

// Claude Desktop App configuration (extends ToolManagerConfig)
interface ClaudeDesktopConfig extends ToolManagerConfig {
  globalShortcut?: string;
  [key: string]: unknown;  // Other Claude Desktop fields
}
```

### Server Events

The ToolManager emits events when server status changes:

```typescript
// Listen for server status updates
manager.on('serverUpdate', (event) => {
  console.log(`Server ${event.name} status: ${event.status}`);
  console.log('Available tools:', event.capabilities.tools);
  console.log('Server URL:', event.url);
});

// Event type definition
interface ServerUpdateEvent {
  name: string;
  status: 'starting' | 'running' | 'stopped' | 'error';
  url: string;
  capabilities: {
    tools: string[];
  };
}

### Managing Servers

Once configured, you can manage your servers:

```typescript
// Start all servers
await manager.start();

// Get available tools from a server
const tools = await manager.getTools('my-server');

// Execute a tool
const result = await manager.executeRequest('my-server', 'some-tool', {
  // tool parameters
});

// Update configuration
await manager.updateConfig({
  mcpServers: {
    // Updated server configs
  }
});

// Stop all servers
await manager.stop();
```

### Server Status

Monitor the status of your servers:

```typescript
// Get status of all servers
const allStatus = manager.getStatus();

// Get status of a specific server
const serverStatus = manager.getServerStatus('my-server');

// Server status interface
interface ServerStatus {
  state: 'starting' | 'running' | 'stopped' | 'error';
  lastCheck: Date;
  error?: Error;
  pid?: number;
}
```

### Service Discovery

The tool manager supports file-based service discovery out of the box:

```typescript
// Configure file-based discovery
const manager = new ToolManager({
  mcpServers: {
    // server configs...
  },
  discovery: {
    type: 'file',
    options: {
      path: '/path/to/discovery.json'  // Path where server info will be published
    }
  }
});
```

The discovery file is automatically:
- Created when the first server starts
- Updated when server status changes
- Updated when available tools change
- Updated periodically with health check information
- Cleaned up when servers stop

Other applications can monitor this file to discover available MCP servers and their capabilities.

### Tool Management

The tool manager provides features for managing tool access and tracking usage:

```typescript
// Register a tool with metadata
await manager.registerTool('my-server', {
  name: 'my-tool',
  description: 'A useful tool',
  parameters: {
    type: 'object',
    properties: {
      // ...
    }
  }
}, {
  // Optional metadata
  enabled: true,
  allowedInstances: ['instance-1', 'instance-2'],
  requiredCapabilities: ['file_access'],
  rateLimit: {
    maxRequests: 100,
    windowMs: 60000 // 1 minute
  }
});

// Get tools available to a specific instance
const tools = await manager.getToolsForInstance('instance-1');

// Check if a tool is available
const canUse = await manager.canUseToolForInstance('instance-1', 'my-tool');

// Get tool usage statistics
const stats = await manager.getToolUsageStats('my-server', 'my-tool');
console.log(stats.totalUsage);    // Total uses
console.log(stats.recentUsage);   // Uses in last hour
console.log(stats.instanceUsage); // Uses per instance
```

Tool metadata supports:
- Enabling/disabling tools
- Restricting tools to specific instances
- Required capabilities
- Rate limiting
- Usage tracking per instance

## API Reference

### `ToolManager`

Main class for managing MCP tool servers.

#### Constructor

```typescript
constructor(config: ToolManagerConfig | ClaudeDesktopConfig)
```

#### Methods

- `start(): Promise<void>` - Start all configured servers
- `stop(): Promise<void>` - Stop all servers
- `updateConfig(config: ToolManagerConfig | ClaudeDesktopConfig): Promise<void>` - Update server configurations
- `getStatus(): Record<string, ServerStatus>` - Get status of all servers
- `getServerStatus(serverName: string): ServerStatus | undefined` - Get status of a specific server
- `getTools(serverName: string): Promise<Tool[]>` - Get available tools from a server
- `executeRequest(serverName: string, tool: string, params: unknown): Promise<ToolResult>` - Execute a tool request

#### Events

- `serverUpdate` - Emitted when a server's status changes
  ```typescript
  interface ServerUpdateEvent {
    name: string;
    status: 'starting' | 'running' | 'stopped' | 'error';
    url: string;
    capabilities: {
      tools: string[];
    };
  }
  ```

## Development

```bash
# Install dependencies
npm install

# Build
npm run build

# Run tests
npm test

# Lint
npm run lint
```

## License

MIT

### Server Configuration

```typescript
interface MCPServerConfig {
  // Command to run (e.g., 'npx', 'uvx', './run.sh')
  command: string;
  
  // Optional arguments for the command
  args?: string[];
  
  // Optional environment variables
  env?: Record<string, string>;
  
  // Optional working directory
  cwd?: string;

  // Optional startup timeout in milliseconds
  // Default: 30000 (30 seconds)
  // Increase this when using npx to install packages
  startupTimeout?: number;
}
```

#### Startup Timeouts

The default startup timeout is 30 seconds (30000ms). This should be sufficient for most cases, including when using `npx` to install packages. However, in some environments (like Docker containers or slow networks), you might need to increase this timeout:

```typescript
const manager = new ToolManager({
  mcpServers: {
    'my-server': {
      command: 'npx',
      args: ['-y', 'my-mcp-server'],
      startupTimeout: 300000  // 5 minutes for slower environments
    }
  }
});
```

For better performance in production environments, consider:
1. Pre-installing packages in your Docker image instead of using `npx`
2. Using a package lock file to speed up installations
3. Using a private npm registry or npm cache if available