Documentation/MCP (Model Context Protocol)
v2.0.0-beta.1Snapshot bebdcd1Markdown ↗

MCP (Model Context Protocol)

MCP (Model Context Protocol)

The Model Context Protocol (MCP) is an open standard that enables seamless integration between AI applications and external data sources and tools. The library provides a complete MCP client implementation with support for both local (stdio) and remote (HTTP/HTTPS) servers.

Overview

MCP allows you to:

  • Discover tools automatically from MCP servers
  • Connect to local servers via stdio (process spawning)
  • Connect to remote servers via HTTP/HTTPS (StreamableHTTP)
  • Manage multiple servers simultaneously
  • Auto-reconnect with exponential backoff
  • Namespace tools to prevent conflicts
  • Serializable client metadata for diagnostics and host-managed restart flows

Quick Start

1. Install or provision an MCP server

bash
npm install oneringai
# Client dependencies are included. Provision the selected MCP server separately.

2. Connect to a Local MCP Server

typescript
import { MCPRegistry, Agent, Connector, Vendor } from 'oneringai';

// Setup connector for LLM
Connector.create({
  name: 'openai',
  vendor: Vendor.OpenAI,
  auth: { type: 'api_key', apiKey: process.env.OPENAI_API_KEY! },
});

// Create MCP client for filesystem server
const client = MCPRegistry.create({
  name: 'filesystem',
  transport: 'stdio',
  transportConfig: {
    command: 'npx',
    args: ['-y', '@modelcontextprotocol/server-filesystem', process.cwd()],
  },
});

// Connect to the server
await client.connect();
console.log(`Connected! Available tools: ${client.tools.length}`);

// Create agent and register MCP tools
const agent = Agent.create({ connector: 'openai', model: 'gpt-4.1' });
client.registerTools(agent.tools);

// Agent can now use MCP tools
const response = await agent.run('List all TypeScript files in the current directory');
console.log(response.output_text);

3. Connect to a Remote MCP Server

typescript
// Create HTTP/HTTPS MCP client
const remoteClient = MCPRegistry.create({
  name: 'remote-api',
  transport: 'https',
  transportConfig: {
    url: 'https://mcp.example.com/api',
    token: process.env.MCP_TOKEN,
    headers: {
      'X-Client-Version': '1.0.0',
    },
    reconnection: {
      maxRetries: 5,
      initialReconnectionDelay: 1000,
      maxReconnectionDelay: 30000,
    },
  },
});

await remoteClient.connect();
remoteClient.registerTools(agent.tools);

Configuration File

Create oneringai.config.json to declare MCP servers:

json
{
  "version": "1.0",
  "mcp": {
    "servers": [
      {
        "name": "filesystem",
        "displayName": "Filesystem Server",
        "transport": "stdio",
        "transportConfig": {
          "command": "npx",
          "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"]
        },
        "autoConnect": true,
        "toolNamespace": "mcp:fs",
        "permissions": {
          "defaultScope": "session",
          "defaultRiskLevel": "medium"
        }
      },
      {
        "name": "github",
        "displayName": "GitHub API",
        "transport": "https",
        "transportConfig": {
          "url": "https://mcp.example.com/github",
          "token": "${GITHUB_TOKEN}"
        },
        "autoConnect": false,
        "toolNamespace": "mcp:github"
      }
    ]
  }
}

Load and use the configuration:

typescript
import { MCPRegistry, Agent } from 'oneringai';

// Reads JSON, interpolates ${ENV_VAR} values, and creates all declared clients.
const clients = await MCPRegistry.loadFromConfigFile('./oneringai.config.json');

// Connect every currently disconnected client in the registry.
await MCPRegistry.connectAll();

// Create agent and register tools
const agent = Agent.create({ connector: 'openai', model: 'gpt-4.1' });
for (const client of clients) {
  if (client.isConnected()) {
    client.registerTools(agent.tools);
  }
}

loadFromConfigFile() creates clients but does not connect them. Currently, MCPRegistry.connectAll() connects all disconnected clients; it does not filter on the declared autoConnect flag. Connect selected clients individually if your application needs that distinction. Also use transportConfig.timeoutMs for the HTTP abort timeout—the top-level requestTimeoutMs field is currently not applied by MCPClient.

MCPRegistry API

The static registry manages all MCP client connections:

typescript
// Create a client
const client = MCPRegistry.create({
  name: 'my-server',
  transport: 'stdio',
  transportConfig: { /* ... */ },
});

// Get a client
const clientVariant2 = MCPRegistry.get('my-server');

// Check if exists
if (MCPRegistry.has('my-server')) {
  // ...
}

// List all servers
const serverNames = MCPRegistry.list();

// Get server info
const info = MCPRegistry.getInfo('my-server');
// { name, state, connected, toolCount }

// Get all server info
const allInfo = MCPRegistry.getAllInfo();

// Lifecycle management
await MCPRegistry.connectAll();
await MCPRegistry.disconnectAll();
MCPRegistry.destroyAll();

MCPClient API

Each client manages a connection to one MCP server:

Connection Management

typescript
// Connect to server
await client.connect();

// Disconnect
await client.disconnect();

// Reconnect
await client.reconnect();

// Check connection status
const isConnected = client.isConnected();

// Ping server (health check)
const alive = await client.ping();

Tool Operations

typescript
// List available tools
const tools = await client.listTools();
console.log(tools.map(t => `${t.name}: ${t.description}`));

// Call a tool directly
const result = await client.callTool('read_file', {
  path: './README.md'
});
console.log(result.content);

// Register tools with agent
client.registerTools(agent.tools);

// Unregister tools
client.unregisterTools(agent.tools);

Resource Operations

typescript
// List available resources
const resources = await client.listResources();

// Read a resource
const content = await client.readResource('file:///path/to/file');
console.log(content.text);

Current limitation: the client currently initializes negotiated capabilities as an empty object and does not register an SDK notification handler for resource updates. As a result, subscribeResource() cannot pass its capability check and no resource:updated event is emitted. Treat resource subscriptions as unavailable until that wiring is implemented; listResources() and readResource() remain usable.

Prompt Operations

typescript
// List available prompts
const prompts = await client.listPrompts();

// Get a prompt
const promptResult = await client.getPrompt('summarize', {
  length: 'short',
});

// Use prompt messages
for (const msg of promptResult.messages) {
  console.log(`${msg.role}: ${msg.content.text}`);
}

Event Monitoring

Listen to connection and execution events:

typescript
// Connection events
client.on('connected', () => {
  console.log('Connected to MCP server');
});

client.on('disconnected', () => {
  console.log('Disconnected from MCP server');
});

client.on('reconnecting', (attempt) => {
  console.log(`Reconnecting... attempt ${attempt}`);
});

client.on('failed', (error) => {
  console.error('Connection failed:', error);
});

// Tool execution events
client.on('tool:called', (name, args) => {
  console.log(`Tool called: ${name}`, args);
});

client.on('tool:result', (name, result) => {
  console.log(`Tool result: ${name}`, result);
});

// Error events
client.on('error', (error) => {
  console.error('MCP error:', error);
});

Transport Types

Stdio Transport

For local MCP servers (spawns a process):

typescript
const client = MCPRegistry.create({
  name: 'local-server',
  transport: 'stdio',
  transportConfig: {
    command: 'npx',                                    // or 'node', 'python', etc.
    args: ['-y', '@modelcontextprotocol/server-filesystem', '/path'],
    env: {
      NODE_ENV: 'production',
      CUSTOM_VAR: 'value',
    },
    cwd: '/working/directory',                        // Optional working directory
  },
});

Best for:

  • Local file system access
  • Database connections (PostgreSQL, SQLite)
  • Development and testing

HTTP/HTTPS Transport

For remote MCP servers (StreamableHTTP with SSE):

typescript
const client = MCPRegistry.create({
  name: 'remote-server',
  transport: 'https',
  transportConfig: {
    url: 'https://mcp.example.com/api',
    token: process.env.MCP_TOKEN,                      // Bearer token
    headers: {
      'X-Client-Version': '1.0.0',
      'X-Custom-Header': 'value',
    },
    timeoutMs: 30000,                                  // Request timeout (no default — omitting this applies no timeout at all)
    sessionId: 'optional-session-id',                  // For reconnection
    reconnection: {
      maxReconnectionDelay: 30000,                     // Max delay between retries (default: 30000)
      initialReconnectionDelay: 1000,                  // Initial delay (default: 1000)
      reconnectionDelayGrowFactor: 1.5,                // Backoff factor (default: 1.5)
      maxRetries: 5,                                   // Max attempts (default: 2)
    },
  },
});

Best for:

  • Cloud-hosted services
  • Production deployments
  • Team collaboration
  • Remote API access

Tool Namespacing

MCP tools are automatically namespaced to prevent conflicts:

typescript
// Default namespace: mcp:{server-name}:{tool-name} (sanitized to mcp_{server-name}_{tool-name}
// for tool-name compatibility — colons are not allowed in tool names)
// Example: mcp_filesystem_read_file, mcp_github_create_issue

// Custom namespace
const client = MCPRegistry.create({
  name: 'fs',
  toolNamespace: 'files',
  // ...
});
// Tools: files_read_file, files_write_file, etc.

// Check registered tools
const toolNames = agent.listTools();
console.log(toolNames.filter(name => name.startsWith('mcp_')));

Multi-Server Example

Connect to multiple MCP servers simultaneously:

typescript
import { MCPRegistry, Agent, Connector, Vendor } from 'oneringai';

// Setup connector
Connector.create({
  name: 'openai',
  vendor: Vendor.OpenAI,
  auth: { type: 'api_key', apiKey: process.env.OPENAI_API_KEY! },
});

// Create multiple clients
const fsClient = MCPRegistry.create({
  name: 'filesystem',
  transport: 'stdio',
  transportConfig: {
    command: 'npx',
    args: ['-y', '@modelcontextprotocol/server-filesystem', process.cwd()],
  },
});

const githubClient = MCPRegistry.create({
  name: 'github',
  transport: 'https',
  transportConfig: {
    url: 'https://mcp.example.com/github',
    token: process.env.GITHUB_TOKEN,
  },
});

const dbClient = MCPRegistry.create({
  name: 'postgres',
  transport: 'stdio',
  transportConfig: {
    command: 'npx',
    args: ['-y', '@modelcontextprotocol/server-postgres'],
    env: {
      DATABASE_URL: process.env.DATABASE_URL!,
    },
  },
});

// Connect all
await Promise.all([
  fsClient.connect(),
  githubClient.connect(),
  dbClient.connect(),
]);

// Create agent and register all tools
const agent = Agent.create({ connector: 'openai', model: 'gpt-4.1' });
fsClient.registerTools(agent.tools);
githubClient.registerTools(agent.tools);
dbClient.registerTools(agent.tools);

console.log(`Total tools: ${agent.listTools().length}`);

// Agent can now use tools from all servers
await agent.run('Query the database, analyze files, and create a GitHub issue with the results');

Finding MCP Servers

The MCP steering group's reference-server repository currently maintains a small educational set: Everything, Fetch, Filesystem, Git, Memory, Sequential Thinking, and Time. These demonstrate protocol and SDK features and are not presented as production-ready services. Browse published packages in the official MCP Registry and evaluate each server's permissions, maintenance, and threat model. Older GitHub, Google Drive, Slack, PostgreSQL, SQLite, and Brave Search reference servers should not be described as current official packages.

Error Handling

typescript
import {
  MCPError,
  MCPConnectionError,
  MCPToolError,
} from 'oneringai';

try {
  await client.connect();
} catch (error) {
  if (error instanceof MCPConnectionError) {
    console.error('Failed to connect:', error.message);
    // Retry or use fallback
  } else if (error instanceof MCPToolError) {
    console.error('Tool execution failed:', error.toolName);
  } else if (error instanceof MCPError) {
    console.error('Protocol error:', error.message);
  }
}

State Persistence

Save and restore MCP client state:

typescript
// Get current state
const state = client.getState();
console.log(state);
// {
//   name: 'filesystem',
//   state: 'connected',
//   capabilities: {},
//   subscribedResources: [],
//   lastConnectedAt: 1234567890,
//   connectionAttempts: 0
// }

// Save to storage
await storage.save('mcp-state', state);

// Load diagnostic/subscription metadata
const savedState = await storage.load('mcp-state');
const newClient = MCPRegistry.create(config);
newClient.loadState(savedState);
await newClient.connect();

loadState() restores only the local subscription URI set and reconnect-attempt counter. It does not restore a live transport, capabilities, tools, or server session, and connect() does not replay resources/subscribe requests. If a future client version exposes working subscriptions, the host must still re-subscribe explicitly after connecting.

Best Practices

  1. Use Configuration Files - Declare servers in oneringai.config.json for easier management; remember that autoConnect is not currently enforced by connectAll()
  2. Handle Reconnection - Enable autoReconnect for production deployments
  3. Monitor Events - Listen to connection events for observability
  4. Use Namespaces - Set custom toolNamespace to organize tools clearly
  5. Error Handling - Always wrap MCP operations in try/catch
  6. Clean Up - Call client.disconnect() when done
  7. Health Checks - Use client.ping() for monitoring
  8. Permission Control - Set appropriate defaultScope for security
  9. Set HTTP Timeouts at the transport - Use transportConfig.timeoutMs; requestTimeoutMs is not currently wired into requests

Troubleshooting

Connection Issues

typescript
// Enable detailed error logging
client.on('error', (error) => {
  console.error('MCP Error:', error);
  console.error('Stack:', error.stack);
});

// Check connection state
console.log('State:', client.state);
console.log('Connected:', client.isConnected());

// Manual reconnect
if (!client.isConnected()) {
  await client.reconnect();
}

Tool Discovery

typescript
// List all discovered tools
const tools = await client.listTools();
console.log('Available tools:');
tools.forEach(tool => {
  console.log(`  ${tool.name}: ${tool.description}`);
  console.log('  Input schema:', JSON.stringify(tool.inputSchema, null, 2));
});

// Check server capabilities
console.log('Capabilities:', client.capabilities);

Debug Mode

typescript
// Log all tool calls
client.on('tool:called', (name, args) => {
  console.log(`[DEBUG] Tool called: ${name}`);
  console.log('[DEBUG] Args:', JSON.stringify(args, null, 2));
});

client.on('tool:result', (name, result) => {
  console.log(`[DEBUG] Tool result: ${name}`);
  console.log('[DEBUG] Result:', JSON.stringify(result, null, 2));
});

Transport Extension Boundary

The public MCPServerConfig currently accepts only stdio, http, and https; MCPClient constructs the corresponding SDK transport internally. Implementing the SDK's Transport interface alone is therefore not enough to inject a custom transport into this integration. Add an adapter in the library or use the MCP SDK client directly when another transport is required.


Documentation snapshot · 2026-11-02Suggest a correction ↗

Search documentation

Type to search the v2 documentation.