Build an MCP Chat
Build a chat app that uses tools from an MCP server.
Build a chat app that can call tools exposed by an MCP server.
Connect to the MCP server
Use lazyTools() so the MCP client is reused and cleaned up correctly.
import { convertMCPTools, createMCPClient } from "@better-agent/core/mcp";
import { lazyTools } from "@better-agent/core";
export const mcpTools = lazyTools(async () => {
const client = await createMCPClient({
transport: {
type: "http",
url: "https://mcp.context7.com/mcp",
},
});
const listed = await client.listTools();
return {
tools: convertMCPTools(client, listed.tools, { prefix: "mcp" }),
dispose: async () => await client.close?.(),
};
});Define the agent
MCP tools plug into the agent like any other tool source.
import { betterAgent, defineAgent } from "@better-agent/core";
import { createOpenAI } from "@better-agent/providers/openai";
import { mcpTools } from "./mcp";
const openai = createOpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
const assistant = defineAgent({
name: "assistant",
model: openai.text("gpt-5-mini"),
instruction: `
Use MCP tools when they help answer the user's question.
Prefer tool results over guessing.
`,
tools: mcpTools,
});
const app = betterAgent({
agents: [assistant],
baseURL: "/api",
secret: "dev-secret",
});
export default app;Create the client
import { createClient } from "@better-agent/client";
import type app from "./server";
export const client = createClient<typeof app>({
baseURL: "/api",
secret: "dev-secret",
});Build the chat
import { useState } from "react";
import { useAgent } from "@better-agent/client/react";
import { client } from "./client";
export function Chat() {
const [input, setInput] = useState("");
const { messages, status, sendMessage } = useAgent(client, {
agent: "assistant",
});
const getText = (message: (typeof messages)[number]) =>
message.parts.map((part) => (part.type === "text" ? part.text : "")).join("");
return (
<div>
<form
onSubmit={async (event) => {
event.preventDefault();
if (!input.trim()) return;
await sendMessage({ input });
setInput("");
}}
>
<input value={input} onChange={(event) => setInput(event.target.value)} />
<button type="submit">Send</button>
</form>
<p>Status: {status}</p>
<ul>
{messages.map((message) => (
<li key={message.localId}>
{message.role}: {getText(message)}
</li>
))}
</ul>
</div>
);
}Use lazyTools() for MCP so the tool source can reuse the client connection and dispose it cleanly when needed.