mcp-agent-kit
一个完整且直观的SDK,用于以最少的工作量构建MCP服务器、MCP代理和LLM集成(OpenAI、Claude、Gemini)。它抽象了MCP协议的所有复杂性,提供了具有自动模型路由的智能代理,并通过一个简单而强大的统一接口,包含用于外部API的通用客户端。非常适合聊天机器人、企业自动化、内部系统集成以及基于MCP的生态系统的快速开发。
文档
mcp-agent-kit
Orchestrate MCP servers and build AI agents in TypeScript — in your app, not in your infrastructure
Connect your agent to several MCP servers in three lines — no gateway, no proxy, no container:
const mcp = await connectMCP({
servers: {
files: { command: "npx", args: ["-y", "@modelcontextprotocol/server-filesystem", "./"] },
github: { url: "https://mcp.example.com/mcp" },
},
});
const agent = createAgent({ provider: "openai" });
agent.use(mcp);
const answer = await agent.chat("Summarise the open issues into notes.md");
mcp-agent-kit is a TypeScript package for working with MCP from inside your application:
- 🎛️ MCP Orchestrator — connect to many MCP servers, aggregate and filter their tools
- 🔍
inspectCLI — validate any MCP server’s tools without writing code - 🔌 MCP Servers — publish your own tools over stdio or Streamable HTTP
- 🤖 AI Agents with automatic tool execution across four LLM providers
- 🧠 Intelligent Routers for multi-LLM routing
- 💬 Chatbots with conversation memory
- 🌐 API Helpers with retry and timeout
Features
- In-process: an
npm install, not another service to deploy - Tool loop included: tools are executed and their results fed back to the model
- Tool filtering: allow/deny globs keep 60 tools from bloating the prompt
- Fault isolation: a server that is down degrades the tool set, not the agent
- Multi-Provider: OpenAI, Anthropic, Gemini, Ollama
- Type-Safe: Full TypeScript support with autocomplete
- Verified docs: every example on this page is compiled in CI
Installation
npm install mcp-agent-kit
Quick Start
Create an AI Agent (1 line!)
import { createAgent } from "mcp-agent-kit";
const agent = createAgent({ provider: "openai" });
const response = await agent.chat("Hello!");
console.log(response.content);
Create an MCP Server (1 function!)
import { createMCPServer } from "mcp-agent-kit";
const server = await createMCPServer({
name: "my-server",
tools: [
{
name: "get_weather",
description: "Get weather for a location",
inputSchema: {
type: "object",
properties: {
location: { type: "string" },
},
},
handler: async ({ location }) => {
return `Weather in ${location}: Sunny, 72°F`;
},
},
],
});
await server.start();
Create a Chatbot with Memory
import { createChatbot, createAgent } from "mcp-agent-kit";
const bot = createChatbot({
agent: createAgent({ provider: "openai" }),
system: "You are a helpful assistant",
maxHistory: 10,
});
await bot.chat("Hi, my name is John");
await bot.chat("What is my name?"); // Remembers context!
Documentation
Table of Contents
- AI Agents
- MCP Servers
- MCP Orchestrator
- CLI: inspect
- LLM Router
- Chatbots
- API Requests
- Configuration
- Examples
AI Agents
Create intelligent agents that work with multiple LLM providers.
Basic Usage
import { createAgent } from "mcp-agent-kit";
const agent = createAgent({
provider: "openai",
model: "gpt-4o",
temperature: 0.7,
maxTokens: 2000,
});
const response = await agent.chat("Explain TypeScript");
console.log(response.content);
Supported Providers
| Provider | Models | API Key Required |
|---|---|---|
| OpenAI | GPT-4o, GPT-4, o-series | ✅ Yes |
| Anthropic | Claude Opus 5, Sonnet 5 | ✅ Yes |
| Gemini | Gemini 2.0+ | ✅ Yes |
| Ollama | Local models | ❌ No |
With Tools (Function Calling)
const agent = createAgent({
provider: "openai",
tools: [
{
name: "calculate",
description: "Perform calculations",
parameters: {
type: "object",
properties: {
operation: { type: "string", enum: ["add", "subtract"] },
a: { type: "number" },
b: { type: "number" },
},
required: ["operation", "a", "b"],
},
handler: async ({ operation, a, b }) => {
return operation === "add" ? a + b : a - b;
},
},
],
});
const response = await agent.chat("What is 15 + 27?");
With System Prompt
const agent = createAgent({
provider: "anthropic",
system: "You are an expert Python developer. Always provide code examples.",
});
Smart Tool Calling
Smart Tool Calling adds reliability and performance to tool execution with automatic retry, timeout, and caching.
Basic Configuration
const agent = createAgent({
provider: "openai",
toolConfig: {
forceToolUse: true, // Force model to use tools
maxRetries: 3, // Retry up to 3 times on failure
toolTimeout: 30000, // 30 second timeout
onToolNotCalled: "retry", // Action when tool not called
},
tools: [...],
});
With Caching
const agent = createAgent({
provider: "openai",
toolConfig: {
cacheResults: {
enabled: true,
ttl: 300000, // Cache for 5 minutes
maxSize: 100, // Store up to 100 results
},
},
tools: [...],
});
Direct Tool Execution
// Execute a tool directly with retry and caching
const result = await agent.executeTool("get_weather", {
location: "San Francisco, CA",
});
Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
forceToolUse | boolean | false | Force the model to use tools when available |
maxRetries | number | 3 | Maximum retry attempts on tool failure |
onToolNotCalled | string | "retry" | Action when tool not called: "retry", "error", "warn", "allow" |
toolTimeout | number | 30000 | Timeout for tool execution (ms) |
cacheResults.enabled | boolean | true | Enable result caching |
cacheResults.ttl | number | 300000 | Cache time-to-live (ms) |
cacheResults.maxSize | number | 100 | Maximum cached results |
debug | boolean | false | Enable debug logging |
autoExecuteTools | boolean | true | Run requested tools and feed results back to the model |
maxIterations | number | 5 | Maximum model round-trips in one chat() call |
Complete Example
const agent = createAgent({
provider: "openai",
model: "gpt-4o",
toolConfig: {
forceToolUse: true,
maxRetries: 3,
onToolNotCalled: "retry",
toolTimeout: 30000,
cacheResults: {
enabled: true,
ttl: 300000,
maxSize: 100,
},
debug: true,
},
tools: [
{
name: "get_weather",
description: "Get current weather for a location",
parameters: {
type: "object",
properties: {
location: { type: "string" },
},
required: ["location"],
},
handler: async ({ location }) => {
// Your weather API logic
return { location, temp: 72, condition: "Sunny" };
},
},
],
});
// Tools are executed automatically and their results are sent back to the
// model, so `content` is the final answer — not an empty string with a
// pending tool call.
const response = await agent.chat("What's the weather in NYC?");
console.log(response.content); // "It's 72°F and sunny in New York."
console.log(response.toolResults); // [{ name: 'get_weather', result: {...} }]
console.log(response.iterations); // 2 (one call for the tool, one for the answer)
// Or execute directly with retry and caching
const result = await agent.executeTool("get_weather", {
location: "New York, NY",
});
MCP Servers
Create Model Context Protocol servers to expose tools and resources.
Basic MCP Server
import { createMCPServer } from "mcp-agent-kit";
const server = await createMCPServer({
name: "my-mcp-server",
port: 7777,
logLevel: "info",
});
await server.start(); // Starts on stdio by default
With Tools
const server = await createMCPServer({
name: "weather-server",
tools: [
{
name: "get_weather",
description: "Get current weather",
inputSchema: {
type: "object",
properties: {
location: { type: "string" },
units: { type: "string", enum: ["celsius", "fahrenheit"] },
},
required: ["location"],
},
handler: async ({ location, units = "celsius" }) => {
// Your weather API logic here
return { location, temp: 22, units, condition: "Sunny" };
},
},
],
});
With Resources
const server = await createMCPServer({
name: "data-server",
resources: [
{
uri: "config://app-settings",
name: "Application Settings",
description: "Current app configuration",
mimeType: "application/json",
handler: async () => {
return JSON.stringify({ version: "1.0.0", env: "production" });
},
},
],
});
Streamable HTTP Transport
const server = await createMCPServer({
name: "http-server",
port: 8080,
path: "/mcp", // default
});
await server.start("http"); // Serves MCP at http://localhost:8080/mcp
The
websockettransport was removed in v1.2.0. It accepted connections but was never wired to the MCP server, so it answered no requests while reporting itself as healthy. Use"http"(Streamable HTTP) instead — it is the transport current MCP clients speak.
MCP Orchestrator
createMCPServer publishes tools. connectMCP is the other half: it connects
to MCP servers, aggregates their tools behind one namespace, and hands them to
an agent — no gateway, no proxy, no container.
Connect to several servers
import { connectMCP, createAgent } from "mcp-agent-kit";
const mcp = await connectMCP({
servers: {
// stdio: started as a child process
files: {
command: "npx",
args: ["-y", "@modelcontextprotocol/server-filesystem", "./"],
},
// Streamable HTTP: a remote server
github: {
url: "https://mcp.example.com/mcp",
headers: { Authorization: `Bearer ${process.env.GITHUB_TOKEN}` },
},
},
});
const agent = createAgent({ provider: "openai" });
agent.use(mcp); // every MCP tool is now callable by the agent
const answer = await agent.chat("Summarise the open issues into notes.md");
await mcp.close();
Tools are namespaced by server, so two servers can both expose read without
colliding: files__read, github__read.
Filter the tool surface
Six servers easily add up to 60 tools, which bloats the prompt and degrades the model's choices. Expose only what the task needs:
const mcp = await connectMCP({
servers: {
files: { command: "npx", args: ["-y", "@modelcontextprotocol/server-filesystem", "./"] },
},
// Globs match the namespaced name
allowTools: ["files__read_*", "files__list_*"],
denyTools: ["*__delete_*"],
});
Filters can also be set per server, against the bare tool name:
const mcp = await connectMCP({
servers: {
files: {
command: "npx",
args: ["-y", "@modelcontextprotocol/server-filesystem", "./"],
denyTools: ["write_file", "move_file"],
},
},
});
A server being down does not take the agent with it
By default a failed server degrades the tool set and the rest keep working:
const mcp = await connectMCP({
servers: {
good: { command: "npx", args: ["-y", "@modelcontextprotocol/server-filesystem", "./"] },
flaky: { url: "https://might-be-down.example.com/mcp" },
},
});
console.log(mcp.listServers());
// [{ name: 'good', state: 'connected', tools: 12, toolsAvailable: 12 },
// { name: 'flaky', state: 'failed', error: 'fetch failed', tools: 0 }]
Pass strict: true to fail the whole connection instead.
Observability
console.log(mcp.getStats());
// { servers: 2, connected: 1, failed: 1, tools: 12,
// calls: 4, failedCalls: 0, avgDurationMs: 37 }
console.log(mcp.getCallLog());
// [{ server: 'good', tool: 'read_file', durationMs: 12, ok: true }, ...]
Orchestrator options
| Option | Type | Default | Description |
|---|---|---|---|
servers | object | — | Servers to connect to, keyed by namespace |
allowTools | string[] | — | Allowlist of namespaced names (globs allowed) |
denyTools | string[] | — | Denylist of namespaced names (globs allowed) |
connectTimeout | number | 30000 | Time to wait for a server to connect (ms) |
toolTimeout | number | 60000 | Time a single tool call may take (ms) |
namespaceSeparator | string | __ | Separator between server name and tool name |
strict | boolean | false | Fail the whole connect when any server fails |
autoReconnect | boolean | true | Reconnect and retry once when a connection drops |
Methods: getTools(), getServerTools(name), getRawTools(name),
listResources(name), callTool(name, params), listServers(), getStats(),
getCallLog(), getServerLogs(name), reconnect(name), close().
CLI: inspect an MCP server
inspect connects to any MCP server, lists what it publishes, validates its
tool schemas and times the round-trips. It needs no config and no code — point
it at a server and read the report.
npx mcp-agent-kit inspect npx -y @modelcontextprotocol/server-filesystem ./
Target npx -y @modelcontextprotocol/server-filesystem ./
Transport stdio
✓ connected in 2453ms
✓ 14 tool(s), 0 resource(s) listed in 2ms
TOOLS
⚠ read_file { path: string, tail?: number, head?: number }
✓ read_multiple_files { paths: array }
⚠ write_file { path: string, content: string }
...
ISSUES
⚠ read_file: property "path" has no description
Summary 14 tool(s) · 0 resource(s) · 0 errors · 18 warning(s)
What it validates
Errors are things a real client will reject; warnings are things that make a model choose worse:
| Level | Check |
|---|---|
| Error | Tool name outside [a-zA-Z0-9_-]{1,64} — OpenAI and Anthropic reject it |
| Error | required lists a key that properties does not declare |
| Error | Missing inputSchema, or type other than object |
| Error | Duplicate tool names |
| Warning | Tool or property with no description |
| Warning | Property with no declared type |
Call a tool
npx mcp-agent-kit inspect --call read_file --args '{"path":"README.md"}' \
npx -y @modelcontextprotocol/server-filesystem ./
Remote servers
npx mcp-agent-kit inspect https://mcp.example.com/mcp -H "Authorization: Bearer $TOKEN"
In CI
--json gives machine-readable output, and the exit code is meaningful:
0 clean, 1 schema errors, 2 could not connect.
npx mcp-agent-kit inspect --json node ./my-server.js | jq '.issues'
Options
| Option | Description |
|---|---|
--json | Machine-readable output |
--quiet, -q | Summary and issues only |
--timeout <ms> | Connect and call timeout (default 30000) |
--header, -H | HTTP header, repeatable ("Name: value") |
--env, -e | Environment variable for a stdio server, repeatable |
--call <tool> | Call a tool after inspecting |
--args <json> | Arguments for --call |
Options go before the target; everything after the target is the server's own command line.
LLM Router
Route requests to different LLMs based on intelligent rules.
Basic Router
import { createLLMRouter } from "mcp-agent-kit";
const router = createLLMRouter({
rules: [
{
when: (input) => input.length < 200,
use: { provider: "openai", model: "gpt-4o" },
},
{
when: (input) => input.includes("code"),
use: { provider: "anthropic", model: "claude-opus-5" },
},
{
default: true,
use: { provider: "openai", model: "gpt-4o" },
},
],
});
const response = await router.route("Write a function to sort an array");
With Fallback and Retry
const router = createLLMRouter({
rules: [...],
fallback: {
provider: 'openai',
model: 'gpt-4o'
},
retryAttempts: 3,
logLevel: 'debug'
});
Router Statistics
const stats = router.getStats();
console.log(stats);
// { totalRules: 3, totalAgents: 2, hasFallback: true }
const agents = router.listAgents();
console.log(agents);
// ['openai:gpt-4o', 'anthropic:claude-opus-5']
Chatbots
Create conversational AI with automatic memory management.
Basic Chatbot
import { createChatbot, createAgent } from "mcp-agent-kit";
const bot = createChatbot({
agent: createAgent({ provider: "openai" }),
system: "You are a helpful assistant",
maxHistory: 10,
});
await bot.chat("Hi, I am learning TypeScript");
await bot.chat("Can you help me with interfaces?");
await bot.chat("Thanks!");
With Router
const bot = createChatbot({
router: createLLMRouter({ rules: [...] }),
maxHistory: 20
});
Memory Management
// Get conversation history
const history = bot.getHistory();
// Get statistics
const stats = bot.getStats();
console.log(stats);
// {
// messageCount: 6,
// userMessages: 3,
// assistantMessages: 3,
// oldestMessage: Date,
// newestMessage: Date
// }
// Reset conversation
bot.reset();
// Update system prompt
bot.setSystemPrompt("You are now a Python expert");
API Requests
Simplified HTTP requests with automatic retry and timeout.
Basic Request
import { api } from "mcp-agent-kit";
const response = await api.get("https://api.example.com/data");
console.log(response.data);
POST Request
const response = await api.post(
"https://api.example.com/users",
{ name: "John", email: "john@example.com" },
{
name: "create-user",
headers: { "Content-Type": "application/json" },
}
);
With Retry and Timeout
const response = await api.request({
name: "important-request",
url: "https://api.example.com/data",
method: "GET",
timeout: 10000, // 10 seconds
retries: 5, // 5 attempts
query: { page: 1, limit: 10 },
});
All HTTP Methods
await api.get(url, config);
await api.post(url, body, config);
await api.put(url, body, config);
await api.patch(url, body, config);
await api.delete(url, config);
Configuration
Environment Variables
All configuration is optional. Set these environment variables or pass them in code:
# MCP Server
MCP_SERVER_NAME=my-server
MCP_PORT=7777
# Logging
LOG_LEVEL=info # debug | info | warn | error
# LLM API Keys
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
GEMINI_API_KEY=...
OLLAMA_HOST=http://localhost:11434
Using .env File
# .env
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
LOG_LEVEL=debug
The package automatically loads .env files using dotenv.
Examples
Check out the /examples directory for complete working examples:
basic-agent.ts- Simple agent usagesmart-tool-calling.ts- Smart tool calling with retry and cachingmcp-server.ts- MCP server with tools and resourcesmcp-server-http.ts- MCP server over Streamable HTTPmcp-orchestrator.ts- Connect to several MCP servers and hand them to an agentllm-router.ts- Intelligent routing between LLMschatbot-basic.ts- Chatbot with conversation memorychatbot-with-router.ts- Chatbot using routerapi-requests.ts- HTTP requests with retry
Running Examples
# Install dependencies
npm install
# Run an example
npx ts-node examples/basic-agent.ts
API Reference
Agent API
createAgent(config: AgentConfig)
Creates a new AI agent instance.
Parameters:
provider(required): LLM provider - "openai", "anthropic", "gemini", or "ollama"model(optional): Model name (defaults to provider's default)temperature(optional): Sampling temperature 0-2 (default: 0.7)maxTokens(optional): Maximum tokens in response (default: 2000)apiKey(optional): API key (reads from env if not provided)tools(optional): Array of tool definitionssystem(optional): System prompttoolConfig(optional): Smart tool calling configuration
Returns: Agent instance
Methods:
chat(message: string | AgentMessage[]): Promise<AgentResponse>- Send a message and get the final answer, running any tools the model asks forexecuteTool(name: string, params: any): Promise<any>- Execute a tool directlyregisterTools(tools: AgentTool[]): Agent- Add tools to a live agentlistTools(): AgentTool[]- Tools currently visible to the modelcleanup(): void- Release cache timers
AgentResponse
Response object from agent.chat():
{
content: string; // Final response text
toolCalls?: Array<{ // Tools still pending (empty once the loop finishes)
id?: string;
name: string;
arguments: any;
}>;
toolResults?: Array<{ // Tools executed while producing this response
toolCallId: string;
name: string;
result: any;
isError?: boolean;
}>;
iterations?: number; // Model round-trips taken (1 when no tool ran)
usage?: { // Token usage, summed across every round-trip
promptTokens: number;
completionTokens: number;
totalTokens: number;
};
}
MCP Server API
createMCPServer(config: MCPServerConfig)
Creates a new MCP server instance.
Parameters:
name(optional): Server name (default: from env or "mcp-server")port(optional): Port number (default: 7777)logLevel(optional): Log level - "debug", "info", "warn", "error"tools(optional): Array of tool definitionsresources(optional): Array of resource definitions
Returns: Promise<MCPServer> — the function is async (it loads the MCP SDK first), so always await it
Methods:
start(transport?: "stdio" | "websocket"): Promise<void>- Start the server
Router API
createLLMRouter(config: LLMRouterConfig)
Creates a new LLM router instance.
Parameters:
rules(required): Array of routing rulesfallback(optional): Fallback provider configurationretryAttempts(optional): Number of retry attempts (default: 3)logLevel(optional): Log level
Returns: Router instance
Methods:
route(input: string): Promise<AgentResponse>- Route input to appropriate LLMgetStats(): object- Get router statisticslistAgents(): string[]- List all configured agents
Chatbot API
createChatbot(config: ChatbotConfig)
Creates a new chatbot instance with conversation memory.
Parameters:
agentorrouter(required): Agent or router instancesystem(optional): System promptmaxHistory(optional): Maximum messages to keep (default: 10)
Returns: Chatbot instance
Methods:
chat(message: string): Promise<string>- Send message with context, returns the reply textgetHistory(): ChatMessage[]- Get conversation historygetStats(): object- Get conversation statisticsreset(): void- Clear conversation historysetSystemPrompt(prompt: string): void- Update system prompt
API Request Helpers
api.request(config: APIRequestConfig)
Make HTTP request with retry and timeout.
Parameters:
name(optional): Request name for loggingurl(required): Request URLmethod(optional): HTTP method (default: "GET")headers(optional): Request headersquery(optional): Query parametersbody(optional): Request bodytimeout(optional): Timeout in ms (default: 30000)retries(optional): Retry attempts (default: 3)
Returns: Promise<APIResponse>
Convenience Methods:
api.get(url, config?)- GET requestapi.post(url, body, config?)- POST requestapi.put(url, body, config?)- PUT requestapi.patch(url, body, config?)- PATCH requestapi.delete(url, config?)- DELETE request
Advanced Usage
Custom Provider
// Coming soon: Plugin system for custom providers
Middleware
// Coming soon: Middleware support for request/response processing
Streaming Responses
// Coming soon: Streaming support for real-time responses
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
MIT © Dominique Kossi
Acknowledgments
- Built with TypeScript
- Uses MCP SDK
- Powered by OpenAI, Anthropic, Google, and Ollama
Support
- Email: houessoudominique@gmail.com
- Issues: GitHub Issues
- Discussions: GitHub Discussions
Made by developers, for developers