MCP Server with agent-powered tools, mountable as Express middleware. Each tool starts an LLM Agent Loop that iteratively calls FlowMCP schema tools to solve problems.
flowchart LR
MC[MCP Client] -->|MCP Protocol| B[AgentToolsServer]
A2A[A2A Client] -->|message/send| AD[A2A Adapter]
AD -->|tools/call| B
B --> C[ToolRegistry]
C --> D[AgentLoop]
D -->|OpenRouter| E[LLM]
D -->|callTool| F[ToolClient]
D -->|discover| G[A2A Discovery]
F --> H[FlowMCP Schemas]
H --> I[External APIs]
git clone https://github.com/flowmcp/mcp-agent-server.git
cd mcp-agent-server
npm iimport express from 'express'
import { AgentToolsServer } from 'mcp-agent-server'
const app = express()
app.use( express.json() )
const { mcp } = await AgentToolsServer.create( {
name: 'My Agent Server',
version: '1.0.0',
routePath: '/mcp',
llm: {
baseURL: 'https://openrouter.ai/api',
apiKey: process.env.OPENROUTER_API_KEY
},
tools: [
{
name: 'defi-research',
description: 'Research DeFi protocols',
inputSchema: {
type: 'object',
properties: {
query: { type: 'string' }
},
required: [ 'query' ]
},
agent: {
systemPrompt: 'You are a DeFi research agent.',
model: 'anthropic/claude-sonnet-4-5-20250929',
maxRounds: 10,
maxTokens: 4096
},
toolSources: [
{
type: 'flowmcp',
schemas: [ defilamaSchema, coingeckoSchema ],
serverParams: { DEFILAMA_KEY: process.env.DEFILAMA_KEY }
}
],
execution: {
taskSupport: 'optional'
}
}
]
} )
app.use( mcp.middleware() )
app.listen( 4100 )- MCP Server with StreamableHTTP transport (session-based)
- LLM Agent Loop with iterative tool calling via Anthropic SDK
- FlowMCP schemas as tool sources (in-process, no external server needed)
- A2A Adapter — optional Agent-to-Agent protocol support (
@a2a-js/sdk) - A2A Discovery — built-in tool to discover other A2A agents
- Agent Card — auto-generated from manifest at
/.well-known/agent.json - Configurable answer schema per tool
- MCP Tasks API for async tool execution
- Composable with
x402-mcp-middlewarefor payment gating - Multiple tool sources per tool via CompositeToolClient
Creates a new MCP server instance from configuration.
Method
AgentToolsServer.create( { name, version, routePath, llm, tools, tasks } )
| Key | Type | Description | Required |
|---|---|---|---|
| name | string | Server name for MCP handshake | Yes |
| version | string | Server version | Yes |
| routePath | string | Express route path. Default '/mcp' |
No |
| llm | object | LLM config { baseURL, apiKey } |
Yes |
| tools | array | Array of tool configurations | Yes |
| tasks | object | Task store config { store }. Default InMemoryTaskStore |
No |
Returns
// AgentToolsServer instance
const { mcp } = await AgentToolsServer.create( { /* config */ } )Creates a server from a FlowMCP v3.0.0 agent manifest.
Method
AgentToolsServer.fromManifest( { manifest, llm, schemas, serverParams, routePath } )
| Key | Type | Description | Required |
|---|---|---|---|
| manifest | object | Agent manifest (export const agent from agent.mjs) |
Yes |
| llm | object | LLM config { baseURL, apiKey } |
Yes |
| schemas | array | FlowMCP schema objects for the agent's tools | No |
| serverParams | object | API keys for schemas | No |
| routePath | string | Express route path. Default '/mcp' |
No |
Example
import { agent } from './agent.mjs'
const { mcp } = await AgentToolsServer.fromManifest( {
manifest: agent,
llm: {
baseURL: 'https://openrouter.ai/api',
apiKey: process.env.OPENROUTER_API_KEY
},
schemas: [ etherscanSchema, defilamaSchema ],
serverParams: { ETHERSCAN_API_KEY: process.env.ETHERSCAN_API_KEY }
} )Returns an Express middleware function that handles MCP protocol requests (POST/GET/DELETE) on the configured route path.
Method
mcp.middleware()
Returns
// Express middleware function (req, res, next) => Promise<void>
app.use( mcp.middleware() )Returns all registered tools in MCP ListTools format.
Method
mcp.listToolDefinitions()
Returns
const { tools } = mcp.listToolDefinitions()
// tools: [ { name, description, inputSchema, execution? } ]| Key | Type | Description |
|---|---|---|
| tools | array | Array of tool definitions in MCP format |
Each tool in the tools array has the following structure:
| Key | Type | Description | Required |
|---|---|---|---|
| name | string | Tool name (used in MCP protocol) | Yes |
| description | string | Tool description | Yes |
| inputSchema | object | JSON Schema for tool input | Yes |
| agent | object | Agent configuration (see below) | Yes |
| toolSources | array | Array of tool source configs (see below) | Yes |
| execution | object | { taskSupport: 'optional' | 'required' } |
No |
| Key | Type | Description | Required |
|---|---|---|---|
| systemPrompt | string | System prompt for the LLM | Yes |
| model | string | LLM model ID (e.g. 'anthropic/claude-sonnet-4-5-20250929') |
Yes |
| maxRounds | number | Maximum agent iterations. Default 10 |
No |
| maxTokens | number | Max completion tokens. Default 4096 |
No |
| answerSchema | object | Custom JSON Schema for submit_answer tool |
No |
Each entry in toolSources defines where the agent gets its tools from:
| Key | Type | Description | Required |
|---|---|---|---|
| type | string | Source type. Currently 'flowmcp' |
Yes |
| schemas | array | FlowMCP schema objects | Yes |
| serverParams | object | Environment variables / API keys for schemas | No |
Optional Agent-to-Agent protocol support. Import separately:
import { AgentToolsServer } from 'mcp-agent-server'
import { A2AAdapter } from 'mcp-agent-server/a2a'
const { mcp } = await AgentToolsServer.fromManifest( { manifest, llm: { ... } } )
app.use( mcp.middleware() )
// A2A (optional)
const a2a = A2AAdapter.from( { mcp, manifest, serverUrl: 'https://my-agent.com' } )
app.use( '/.well-known/agent.json', a2a.agentCardMiddleware() )
app.use( '/a2a/v1', a2a.handler() )This exposes three endpoints:
| Endpoint | Protocol | Purpose |
|---|---|---|
POST /mcp |
MCP | Tool calls from MCP clients (Claude Desktop, Cursor) |
GET /.well-known/agent.json |
A2A | Agent discovery (Agent Card) |
POST /a2a/v1 |
A2A | Agent-to-agent communication (message/send) |
The A2A adapter translates message/send into MCP tools/call — no separate agent loop.
The agent loop includes an optional discover_agent tool that fetches Agent Cards from other A2A agents:
const { mcp } = await AgentToolsServer.fromManifest( {
manifest,
llm: { ... },
discovery: true // Enables discover_agent tool
} )No code coupling. Pure Express middleware ordering:
import express from 'express'
import { X402Middleware } from 'x402-mcp-middleware/v2'
import { AgentToolsServer } from 'mcp-agent-server'
const app = express()
app.use( express.json() )
// 1. Payment gate (optional)
const x402 = await X402Middleware.create( { /* config */ } )
app.use( x402.mcp() )
// 2. Agent MCP Server
const { mcp } = await AgentToolsServer.create( { /* config */ } )
app.use( mcp.middleware() )
app.listen( 4100 )Full FlowMCP documentation at docs.flowmcp.org. See the Agent Server page for integration guides.
MIT
PRs are welcome. Please open an issue first to discuss proposed changes.