# MCP Toolkit

[![npm version](https://img.shields.io/npm/v/mcp-toolkit.svg)](https://www.npmjs.com/package/mcp-toolkit)
[![Node.js CI](https://github.com/adamavenir/mcp-toolkit/actions/workflows/node.js.yml/badge.svg)](https://github.com/adamavenir/mcp-toolkit/actions/workflows/node.js.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)

A TypeScript implementation of the Model Context Protocol (MCP) toolkit for connecting to and interacting with MCP-compliant servers.

## Overview

The Model Context Protocol (MCP) is a standardized protocol that enables communication between LLM applications and external data sources or tools. The MCP Toolkit provides a robust, modular implementation for building MCP-compliant clients that can:

- Connect to MCP servers using different transport mechanisms (stdio, SSE)
- Discover and execute tools provided by servers
- Discover, read, and subscribe to resources
- Handle server lifecycle events and error conditions
- Manage connections to multiple servers

## Key Features

- **Simplified Client API**: High-level client for quick integration
- **Transport Layer Options**: Support for stdio (local processes) and SSE (remote servers)
- **Tool Execution**: Discover and execute tools with proper error handling
- **Resource Management**: List, read, and subscribe to resources
- **Error Handling**: Comprehensive error handling with recovery strategies
- **TypeScript Support**: Full TypeScript definitions for type safety

## Installation

```bash
npm install mcp-toolkit
```

## Quick Start

### Basic Usage

```typescript
import { McpClient } from 'mcp-toolkit';
import { StdioClientTransport } from 'mcp-toolkit';

async function main() {
  // Create client
  const client = new McpClient(
    {
      name: 'my-client',
      version: '1.0.0'
    },
    {
      capabilities: {
        resources: {},
        tools: {},
        prompts: {}
      },
      requestTimeout: 10000
    }
  );

  try {
    // Create transport to a local server process
    const transport = new StdioClientTransport({
      command: 'node',
      args: ['path/to/server.js'],
      inherit: false
    });

    // Connect to server
    await client.connect(transport);
    console.log('Connected to server');

    // Get server info
    const serverInfo = await client.getServerInfo();
    console.log('Server info:', serverInfo);

    // List available tools
    const tools = await client.listTools();
    console.log('Available tools:', tools);

    // Call a tool
    if (tools.length > 0) {
      const result = await client.callTool(tools[0].name, { 
        // parameters matching the tool's schema
      });
      console.log('Tool result:', result);
    }

    // List available resources
    const resources = await client.listResources();
    console.log('Available resources:', resources);

    // Read a resource
    if (resources.length > 0) {
      const resourceContent = await client.readResource(resources[0].uri);
      console.log('Resource content:', resourceContent);
    }
  } catch (error) {
    console.error('Error:', error);
  } finally {
    // Close the connection
    await client.close();
    console.log('Connection closed');
  }
}

main();
```

### Using SSE Transport (for remote servers)

```typescript
import { McpClient, SseTransport } from 'mcp-toolkit';

// Create an SSE transport for a remote server
const transport = new SseTransport({
  baseUrl: 'https://your-mcp-server-url.com',
  headers: {
    'Authorization': 'Bearer your-auth-token'
  }
});

// Use the transport with your client
const client = new McpClient({ /* config */ });
await client.connect(transport);
```

## Client Configuration

The McpClient constructor takes two optional configuration objects:

```typescript
const client = new McpClient(
  // Client info (optional)
  {
    name: 'my-client',         // Client name
    version: '1.0.0'           // Client version
  },
  // Client config (optional)
  {
    capabilities: {            // Client capabilities
      resources: {},           // Resource-specific capabilities
      tools: {},               // Tool-specific capabilities
      prompts: {}              // Prompt-specific capabilities
    },
    requestTimeout: 10000,     // Request timeout in milliseconds
  }
);
```

## Resource Management

Resources are data and content exposed by MCP servers that can be read by clients.

### Listing Resources

```typescript
// List all available resources
const resources = await client.listResources();
console.log('Available resources:', resources);
```

### Reading Resources

```typescript
// Read a resource by URI
const resourceContent = await client.readResource('mcp://server-name/path/to/resource');
console.log('Resource content:', resourceContent);
```

### Subscribing to Resource Updates

```typescript
// Subscribe to resource updates
await client.subscribeResource('mcp://server-name/path/to/resource');

// Listen for resource update events
client.onResourceUpdated((event) => {
  console.log('Resource updated:', event.uri);
  console.log('New content:', event.content);
});
```

## Tool Execution

Tools are executable functions exposed by MCP servers that clients can call.

### Listing Tools

```typescript
// List all available tools
const tools = await client.listTools();
console.log('Available tools:', tools);
```

### Calling Tools

```typescript
// Call a tool by name with parameters
const result = await client.callTool('tool-name', {
  param1: 'value1',
  param2: 'value2'
});
console.log('Tool result:', result);
```

## Error Handling

The toolkit provides comprehensive error handling with specific error types for different scenarios.

```typescript
import { ConnectionError, ToolError } from 'mcp-toolkit';

try {
  await client.callTool('non-existent-tool', {});
} catch (error) {
  if (error instanceof ToolError) {
    console.error('Tool execution error:', error.message);
    // Handle tool-specific errors
  } else if (error instanceof ConnectionError) {
    console.error('Connection error:', error.message);
    // Handle connection errors
  } else {
    console.error('Unexpected error:', error);
  }
}
```

## Transport Options

### StdioClientTransport

For connecting to local server processes using standard input/output.

```typescript
import { StdioClientTransport } from 'mcp-toolkit';

const transport = new StdioClientTransport({
  command: 'node',             // Command to start the server
  args: ['server.js'],         // Command arguments
  inherit: false,              // Whether to inherit stdio from parent process
  env: {                       // Optional environment variables
    NODE_ENV: 'production'
  },
  cwd: '/path/to/working/dir'  // Optional working directory
});
```

### SseTransport

For connecting to remote servers using Server-Sent Events (SSE).

```typescript
import { SseTransport } from 'mcp-toolkit';

const transport = new SseTransport({
  baseUrl: 'https://your-mcp-server.com',  // Server URL
  headers: {                               // Optional HTTP headers
    'Authorization': 'Bearer token'
  },
  retryInterval: 3000                      // Retry interval in milliseconds
});
```

## Advanced Usage

### Event Handling

The client provides event handlers for various lifecycle events.

```typescript
// Connection events
client.onConnect(() => {
  console.log('Connected to server');
});

client.onDisconnect(() => {
  console.log('Disconnected from server');
});

// Resource updates
client.onResourceUpdated((event) => {
  console.log('Resource updated:', event.uri);
});

// Server events
client.onServerEvent((event) => {
  console.log('Server event:', event.type, event.data);
});
```

### Multi-Server Management

For applications that need to connect to multiple MCP servers, the toolkit provides a higher-level API:

```typescript
import { container, registerServices } from 'mcp-toolkit';
import { IMCPToolkit } from 'mcp-toolkit';

// Register services with configuration
registerServices({
  servers: {
    'server1': {
      command: 'node',
      args: ['path/to/server1.js'],
      autoRestart: true
    },
    'server2': {
      command: 'node',
      args: ['path/to/server2.js'],
      autoRestart: true
    }
  },
  connections: {
    defaults: {
      transportType: 'stdio',
      requestTimeout: 5000
    }
  }
});

// Get the toolkit instance
const toolkit = container.resolve<IMCPToolkit>('IMCPToolkit');

// Start all servers and establish connections
await toolkit.start();

// Get tools from all servers
const allTools = await toolkit.getAvailableTools();

// Execute a tool call on any server
const result = await toolkit.executeToolCall({
  function_name: 'tool-name',
  parameters: { param1: 'value1' }
});

// Stop all servers and close connections
await toolkit.stop();
```

### Parallel Tool Execution

```typescript
// Execute multiple tool calls in parallel
const results = await toolkit.executeToolCalls([
  {
    function_name: 'tool1',
    parameters: { param1: 'value1' }
  },
  {
    function_name: 'tool2',
    parameters: { param2: 'value2' }
  }
]);
```

## Examples

Check out the examples directory for more comprehensive examples:

- [Basic Client](./examples/basic/simple-client.ts) - A simple client implementation
- [Multi-Server Management](./examples/advanced/multi-server.ts) - Managing multiple MCP servers

## Documentation

For more detailed documentation, see:

- [Introduction to MCP](./docs/introduction.md) - Learn about the Model Context Protocol
- [Creating an MCP Server](./docs/creating-an-mcp-server.md) - Build your own MCP server
- [Best Practices](./docs/best-practices.md) - Recommended patterns for using the toolkit
- [Publishing Guide](./docs/publishing.md) - How to publish the toolkit to npm

## Development

### Building from Source

```bash
# Clone the repository
git clone https://github.com/adamavenir/mcp-toolkit.git
cd mcp-toolkit

# Install dependencies
npm install

# Build the project
npm run build

# Run tests
npm test
```

## Contributing

Contributions are welcome! Please read our [Contributing Guide](./CONTRIBUTING.md) for details on how to submit pull requests, open issues, and more.

## Development and Versioning

### Versioning

We use semantic versioning. To bump the version and create a git tag, use one of the following npm scripts:

```bash
# Bump patch version (0.0.x)
npm run version:patch

# Bump minor version (0.x.0)
npm run version:minor

# Bump major version (x.0.0)
npm run version:major
```

After bumping the version, push the changes and tags:

```bash
git push && git push --tags
```

This will trigger the GitHub Action to automatically publish the new version to npm.

## License

MIT
