Skip to content

Connect an MCP server

MCP servers are the data sources for your webmcp-auto-ui applications. This tutorial shows you how to connect an external MCP server, create tool layers, and integrate them into the agent loop so the LLM can query remote data.

Connect a remote MCP server, retrieve its tools, and use them in the agent loop so the LLM can query data in real time.

  • The boilerplate is installed (see Getting started)
  • An MCP server accessible via HTTP (public or local)

An application that connects to one or more MCP servers, with error handling, lazy loading, and full agent loop integration.

graph LR
App[Application] -->|HTTP| Proxy[MCP Proxy]
Proxy -->|stdio| Server[MCP Server]
Server -->|data| Proxy
Proxy -->|JSON-RPC| App
App -->|tools| Agent[Agent Loop]
Agent -->|queries| App

MCP (Model Context Protocol) is a standardized client-server protocol that allows an LLM to access remote tools. Each MCP server exposes:

  • Tools: atomic actions the LLM can call (e.g., query_sql, search, fetch_document)
  • Recipes (optional): composition guides that explain how to combine tools

Transport uses HTTP Streamable with JSON-RPC 2.0 messages.


The @webmcp-auto-ui/core package provides McpClient for connecting to an MCP server:

import { McpClient } from '@webmcp-auto-ui/core';
const mcpClient = new McpClient('http://localhost:3000/mcp', {
clientName: 'WebMCP Auto-UI',
clientVersion: '1.0.0',
timeout: 30000,
});
await mcpClient.initialize();
const tools = await mcpClient.listTools();
console.log('Available tools:', tools);

Initialization performs:

  1. An initialize request with client capabilities
  2. The server responds with its capabilities and info
  3. A tools/list request to retrieve available tools
sequenceDiagram
participant Client as McpClient
participant Server as MCP Server
Client->>Server: POST /mcp {method: "initialize"}
Server-->>Client: {serverInfo, capabilities}
Client->>Server: POST /mcp {method: "tools/list"}
Server-->>Client: {tools: [{name, description, inputSchema}]}
Note over Client: Ready!

Checkpoint: the tools variable contains an array of objects with name, description, and inputSchema.


A ToolLayer encapsulates an MCP server with its tools in the format expected by the agent:

import type { ToolLayer } from '@webmcp-auto-ui/agent';
async function createMcpLayer(url: string, name: string): Promise<ToolLayer> {
const client = new McpClient(url);
await client.initialize();
const tools = await client.listTools();
return {
protocol: 'mcp',
serverName: name,
description: `Tools from the ${name} server`,
serverUrl: url,
tools: tools.map(t => ({
name: t.name,
description: t.description ?? '',
inputSchema: t.inputSchema,
})),
};
}
const wikiLayer = await createMcpLayer(
'https://demos.hyperskills.net/mcp-wikipedia/mcp',
'wikipedia'
);

Pass the MCP client and tool layers to runAgentLoop():

import { runAgentLoop, RemoteLLMProvider } from '@webmcp-auto-ui/agent';
const provider = new RemoteLLMProvider({
proxyUrl: '/api/chat',
model: 'sonnet',
});
const result = await runAgentLoop('Search for information about Svelte', {
client: mcpClient,
provider,
layers: [wikiLayer],
maxIterations: 5,
callbacks: {
onToolCall: (call) => {
console.log(`Tool called: ${call.name}`);
if (call.result) console.log('Result:', call.result);
},
onText: (text) => {
console.log('Agent says:', text);
},
},
});

Checkpoint: the console shows tool calls and results returned by the MCP server.


McpMultiClient automatically manages multiple connections and call routing:

import { McpMultiClient } from '@webmcp-auto-ui/core';
import { fromMcpTools } from '@webmcp-auto-ui/agent';
import type { McpLayer, ToolLayer } from '@webmcp-auto-ui/agent';
const multi = new McpMultiClient();
await multi.addServer('https://demos.hyperskills.net/mcp-wikipedia/mcp');
await multi.addServer('https://demos.hyperskills.net/mcp-metmuseum/mcp');
await multi.addServer('https://demos.hyperskills.net/mcp-openmeteo/mcp');
const layers: ToolLayer[] = multi.listServers().map(server => ({
protocol: 'mcp' as const,
serverName: server.name,
tools: fromMcpTools(server.tools),
}));
const result = await runAgentLoop('What is the weather in Paris?', {
client: multi,
provider,
layers,
maxIterations: 5,
});
graph TD
Agent[Agent Loop] -->|prefix data-api_mcp_*| Data[Data API Server]
Agent -->|prefix search-engine_mcp_*| Search[Search Server]
Agent -->|prefix autoui_webmcp_*| WebMCP[Local WebMCP]

Wrap the connection in a try/catch:

async function safeConnectMcp(url: string) {
try {
const client = new McpClient(url, { timeout: 10000 });
await client.initialize();
return client;
} catch (error) {
if (error instanceof Error) {
if (error.message.includes('timeout')) {
console.error('MCP server unreachable (timeout)');
} else if (error.message.includes('404')) {
console.error('MCP endpoint not found');
} else {
console.error('Connection error:', error.message);
}
}
return null;
}
}

For servers with many tools, lazy loading avoids overloading the LLM’s prompt. The agent loop handles this automatically:

const result = await runAgentLoop('...', {
client: mcpClient,
provider,
layers: [mcpLayer],
// Lazy loading is active by default
});

With 4 servers and 50 tools total, discovery mode exposes about 20 tools instead of 50, saving 3,000—5,000 tokens in the initial prompt.


ServerProduction URLMain tools
Wikipedia/mcp-wikipedia/mcpsearch, readArticle
Met Museum/mcp-metmuseum/mcpsearch-museum-objects, get-museum-object
Open Meteo/mcp-openmeteo/mcpweather_forecast, geocoding
HackerNews/mcp-hackernews/mcpget-front-page, search-posts
iNaturalist/mcp-inaturalist/mcpsearch_observations
NASA/mcp-nasa/mcpnasa_apod, nasa_mars_rover

URLs are prefixed with https://demos.hyperskills.net.


ProblemLikely causeSolution
”Failed to initialize”Server down or wrong URLTest with curl -X POST <url>
”Timeout”Slow server startupIncrease timeout to 30s
Empty toolsServer doesn’t expose tools/listCheck MCP server version
”CORS error”Cross-origin blockedUse an nginx proxy (see Deploy MCP proxies)