MCP-USE: How a Full-Stack Framework Turns MCP Servers into Deployable Agent Applications
DEV Community

MCP-USE: How a Full-Stack Framework Turns MCP Servers into Deployable Agent Applications

MCP-USE: How a Full-Stack Framework Turns MCP Servers into Deployable Agent Applications

Most MCP tutorials stop at the server. You write a Python or TypeScript MCP server that exposes tools, test it with the CLI inspector, and call it done. But production agent applications need client orchestration, multi-server coordination, authentication boundaries, structured output parsing, and often a UI for humans to supervise tool calls. MCP-USE is a framework that addresses the deployment gap by providing both sides of the protocol boundary in a single stack. What MCP-USE Actually Solves

MCP defines a protocol for tool discovery and execution. It does not define how to build an agent loop, manage state across multiple servers, handle authentication, or render interactive UIs. MCP-USE fills those gaps with:

  • Client orchestration - Connects to multiple MCP servers, discovers tools, and routes LLM tool calls to the correct server.
  • Agent harness - Provides a loop that sends LLM responses to MCP servers, collects results, and feeds them back to the model.
  • Dual-language runtime - TypeScript for client orchestration and UI, Python for server-side tool execution.
  • Interactive MCP Apps - A TypeScript-specific feature that renders React components driven by MCP server state.

The framework is opinionated. It assumes you want to run TypeScript clients that talk to one or more MCP servers (Python or TypeScript), and it provides batteries-included patterns for authentication, schema validation, and structured output.

Architecture

MCP-USE splits responsibilities across three layers:

MCP Server

Exposes tools and resources via the MCP protocol. Written in Python or TypeScript. Runs as a separate process.

MCP Client

Discovers servers, fetches tool schemas, and executes tool calls. Written in TypeScript. Runs in Node.js or the browser.

Agent Orchestrator

Manages the LLM loop, decides which tools to call, and handles retries. Sits above the client layer. The protocol boundary is stdio or HTTP. MCP servers communicate over JSON-RPC, so the client and server can run in different languages, different processes, or different machines. MCP-USE provides SDKs for both sides but does not require you to use both. You can write a Python server and connect it to a custom TypeScript client, or vice versa.

State Synchronization Across the Boundary

MCP is stateless at the protocol level. Each tool call is independent. But agent applications often need session state (conversation history, user context, intermediate results). MCP-USE handles this in two ways:

  • Client-side state - The TypeScript client maintains conversation history and passes it to the LLM on each turn. The server never sees it.
  • Server-side state - MCP servers can expose resources (read-only data) that the client fetches on demand. Resources are stateful but read-only from the client's perspective. This separation prevents race conditions. The client owns orchestration state. The server owns tool execution state. Neither tries to synchronize mutable state across the boundary.

Dual-Language Support

MCP-USE supports both TypeScript and Python for server development, but the client is TypeScript-only. This asymmetry reflects real-world deployment patterns:

  • Python servers - Most ML tooling, data pipelines, and scientific libraries are Python. If your tools call Pandas, Hugging Face, or SQLAlchemy, you write a Python server.
  • TypeScript clients - Web UIs, Node.js orchestration, and browser-based agents are TypeScript. If you need a React frontend or want to deploy to Vercel, you write a TypeScript client.

The protocol boundary makes this work. The client does not care what language the server is written in. It only cares about the JSON-RPC interface.

Deployment Trade-Offs

Running TypeScript and Python in the same application stack introduces operational complexity:

Aspect Details
Stack Composition TypeScript-Only / Python-Only / Mixed Stack
Dependency Management npm/pip/poetry (npm + pip)
Runtime Requirements Node.js / Python 3.9+
Deployment Surface Single container / Multi-container or monorepo
Debugging Complexity Low / Low / Medium (cross-language traces)
Library Ecosystem Limited ML tooling / Limited web tooling

For prototypes, a mixed stack is fine. For production, consider whether you can consolidate. If your tools are simple HTTP calls or file operations, a TypeScript-only stack reduces moving parts. If your tools require heavy Python libraries, run the client and server in separate containers and use HTTP transport instead of stdio.

Tools vs Resources

The MCP Data Model distinguishes between tools and resources:

  • Tools - Functions the LLM can call. They take arguments, perform side effects, and return results. Example: send_email(to, subject, body).
  • Resources - Read-only data the client can fetch. They do not take arguments and do not perform side effects. Example: user_profile, recent_transactions.

MCP-USE exposes both through the client SDK. Tools are called during the agent loop. Resources are fetched before the loop starts or on-demand when the LLM requests context. This distinction matters for caching and observability. Resources can be cached aggressively because they are read-only. Tools cannot be cached because they have side effects. MCP-USE logs tool calls but not resource fetches, which keeps traces focused on actions rather than data access.

Agent Orchestration: The Loop

MCP-USE provides a simple agent loop in TypeScript:

import { MCPClient } from 'mcp-use';
import { OpenAI } from 'openai';

const client = new MCPClient();
await client.connectToServer('python-server', {
  command: 'python',
  args: ['server.py'],
  transport: 'stdio'
});

const tools = await client.listTools();

const openai = new OpenAI();
let messages = [{
  role: 'user',
  content: 'Send an email to a****@example.com'
}];

while (true) {
  const response = await openai.chat.completions.create({
    model: 'gpt-4',
    messages,
    tools: tools.map(t => t.schema)
  });

  const choice = response.choices[0];
  if (choice.finish_reason === 'stop') break;

  const toolCall = choice.message.tool_calls[0];
  const result = await client.callTool(toolCall.function.name, JSON.parse(toolCall.function.arguments));

  messages.push(choice.message);
  messages.push({
    role: 'tool',
    tool_call_id: toolCall.id,
    content: JSON.stringify(result)
  });
}

This loop is not production-ready. It has no error handling, no retry logic, no timeout, and no observability. But it shows the plumbing: Connect to one or more MCP servers. Fetch tool schemas and pass them to the LLM. When the LLM returns a tool call, route it to the correct server. Append the result to the message history and continue. MCP-USE does not enforce a specific agent framework. You can plug this loop into LangChain, LlamaIndex, or a custom orchestrator. The framework only handles the MCP protocol boundary.

Multi-Server Coordination

Production agents often need tools from multiple domains: database queries, API calls, file operations, email, Slack. Each domain can be a separate MCP server. MCP-USE clients can connect to multiple servers and merge their tool schemas:

await client.connectToServer('db-server', {
  command: 'python',
  args: ['db_server.py']
});
await client.connectToServer('email-server', {
  command: 'python',
  args: ['email_server.py']
});

const allTools = await client.listTools();
// Merged from both servers

The client routes tool calls by name. If two servers expose tools with the same name, the client throws an error. You must namespace tool names to avoid collisions (e.g., db.query, email.send). This introduces a coordination problem: how do you manage dependencies between tools? If the LLM calls db.query and then email.send, and the email tool needs data from the query result, the orchestrator must pass that data through the message history. MCP servers cannot call each other directly. All coordination happens in the client.

Interactive MCP Apps: UI on Top of MCP

MCP-Use's TypeScript SDK includes a feature called MCP Apps: React components that render UI driven by MCP server state. This is not part of the MCP spec. It is a framework-specific extension.

An MCP App is a TypeScript client that:

  • Connects to one or more MCP servers.
  • Fetches resources and tool schemas.
  • Renders a React UI that lets users trigger tool calls manually or via an LLM.
  • Updates the UI when tool calls complete.

This is useful for supervised agents. Instead of running the agent loop autonomously, you render a UI where a human approves each tool call before execution. The UI shows the tool name, arguments, and expected result schema. The human clicks "approve" or "reject." Why UI + MCP Is Interesting

Most agent frameworks treat the UI as an afterthought. You build the agent, then bolt on a chat interface. MCP Apps invert this: the UI is a first-class client of the MCP protocol. The same server that powers an autonomous agent can also power a supervised UI. This matters for compliance and debugging. If your agent makes financial transactions or modifies production data, you want a human in the loop. MCP Apps provide that without requiring you to rewrite the server.

Authentication and Security Boundaries

MCP servers run in separate processes. If the server needs to access authenticated APIs (Stripe, GitHub, AWS), it must handle credentials. MCP-Use does not provide a built-in auth system. You have three options:

  1. Environment variables - Pass credentials to the server process via env vars. Simple but insecure for multi-tenant deployments.
  2. OAuth flow in the client - The client obtains an OAuth token and passes it to the server on each tool call. Requires the server to accept a token argument on every tool.
  3. Server-side session store - The server maintains a session store (Redis, database) and the client passes a session ID. The server looks up credentials from the store. Option 3 is the most secure for production. The client never sees credentials. The server validates the session ID before executing tools.

MCP-Use does not enforce any of these patterns. It only provides the transport layer. You must implement auth yourself.

Observability

MCP-Use logs tool calls and results to stdout by default. For production, you need structured logging and distributed tracing. The framework provides hooks for custom loggers:

client.onToolCall((toolName, args) => {
  console.log(`[TOOL CALL] ${toolName}`, args);
});

client.onToolResult((toolName, result) => {
  console.log(`[TOOL RESULT] ${toolName}`, result);
});

These hooks let you integrate with OpenTelemetry, Datadog, or a custom tracing backend. The client does not emit traces by default because it does not know your tracing setup. For debugging, MCP-Use includes a web-based inspector that shows connected servers and their tool schemas, recent tool calls and results, resource fetches, and protocol-level JSON-RPC messages. The inspector runs as a separate web server. You point it at your client, and it proxies all MCP traffic through a UI. This is useful for debugging protocol issues or understanding what the LLM is requesting.

Testing MCP Servers Without a Client

MCP-Use includes a CLI inspector that lets you test servers without writing client code:

npx mcp-use inspect python server.py

This starts an interactive REPL where you can:

  • List available tools.
  • Call tools with JSON arguments.
  • Fetch resources.
  • Inspect tool schemas.

The CLI inspector is useful for unit testing servers. You can write shell scripts that call tools and assert on the results.

Structured Output and Schema Validation

MCP tools declare input and output schemas using JSON Schema. MCP-Use validates arguments before sending them to the server and validates results after receiving them. If validation fails, the client throws an error instead of passing invalid data to the LLM. This prevents a class of bugs where the LLM generates malformed tool calls and the server crashes. The client catches the error and can retry with corrected arguments. Schema validation also enables type-safe clients. The TypeScript SDK generates TypeScript types from JSON schemas, so you get autocomplete and compile-time checks when calling tools programmatically.

Integrating With Existing Agent Frameworks

MCP-Use is not an agent framework. It is a protocol client. You can integrate it with LangChain, LlamaIndex, or any other orchestrator by writing a thin adapter:

import { MCPClient } from 'mcp-use';
import { Tool } from 'langchain/tools';

class MCPTool extends Tool {
  constructor(
    private client: MCPClient,
    private toolName: string
  ) {
    super();
  }

  async _call(args: string): Promise<string> {
    const result = await this.client.callTool(this.toolName, JSON.parse(args));
    return JSON.stringify(result);
  }
}

const client = new MCPClient();
await client.connectToServer('server', {
  command: 'python',
  args: ['server.py']
});

const tools = (await client.listTools()).map(t => new MCPTool(client, t.name));
// Pass tools to LangChain agent

This adapter wraps each MCP tool in a LangChain Tool instance. The agent framework does not know it is talking to MCP. It just sees a list of tools.

Failure Modes and Operational Risks

MCP-Use introduces several failure modes. Here are some common ones and their mitigations:

  • Server process crash - All tools from that server become unavailable. Mitigation: Run servers in separate containers with health checks and auto-restart.
  • Stdio buffer overflow - Mitigation: Use HTTP transport for high-throughput servers.
  • Client hangs waiting for server response - Mitigation: Implement timeouts and retry logic in the agent loop.
  • Schema mismatch - Client sends invalid arguments or cannot parse results. Mitigation: Validate tool schemas and version them, then validate on both sides.
  • Authentication token expiry - Tools fail with 401 errors. Mitigation: Implement token refresh in the server layer.

By addressing these concerns, MCP-Use helps teams move from MCP server prototyping to reliable, production-grade agent applications.

Read on DEV Community ↗ ← Back to News

Comments

No comments yet. Start the discussion.