Skip to main content
Glama
FlowMCP

mcp-agent-server

by FlowMCP
README.md
[![Test](https://img.shields.io/github/actions/workflow/status/FlowMCP/mcp-agent-server/test-on-release.yml)]() ![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)

# mcp-agent-server

**Version:** 2.0.0

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.

## Architecture

```mermaid
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]
```

## Quickstart

```bash
git clone https://github.com/FlowMCP/mcp-agent-server.git
cd mcp-agent-server
npm i
```

```javascript
import 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-6',
                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 )
```

## Features

- 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-middleware` for payment gating
- Multiple tool sources per tool via CompositeToolClient

## Table of Contents

- [Quickstart](#quickstart)
- [Architecture](#architecture)
- [Methods](#methods)
  - [AgentToolsServer.create()](#agenttoolsservercreate)
  - [AgentToolsServer.fromManifest()](#agenttoolsserverfrommanifest)
  - [.middleware()](#middleware)
  - [.listToolDefinitions()](#listtooldefinitions)
- [A2A Adapter](#a2a-adapter)
- [Tool Configuration](#tool-configuration)
- [Composition with x402](#composition-with-x402)
- [License](#license)

## Methods

### `AgentToolsServer.create()`

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**

```javascript
// AgentToolsServer instance
const { mcp } = await AgentToolsServer.create( { /* config */ } )
```

### `AgentToolsServer.fromManifest()`

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**

```javascript
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 }
} )
```

### `.middleware()`

Returns an Express middleware function that handles MCP protocol requests (POST/GET/DELETE) on the configured route path.

**Method**

```
mcp.middleware()
```

**Returns**

```javascript
// Express middleware function (req, res, next) => Promise<void>
app.use( mcp.middleware() )
```

### `.listToolDefinitions()`

Returns all registered tools in MCP ListTools format.

**Method**

```
mcp.listToolDefinitions()
```

**Returns**

```javascript
const { tools } = mcp.listToolDefinitions()
// tools: [ { name, description, inputSchema, execution? } ]
```

| Key | Type | Description |
|-----|------|-------------|
| tools | array | Array of tool definitions in MCP format |

## Tool Configuration

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 |

### Agent Configuration

| Key | Type | Description | Required |
|-----|------|-------------|----------|
| systemPrompt | string | System prompt for the LLM | Yes |
| model | string | LLM model ID (e.g. `'anthropic/claude-sonnet-4-6'`) | 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 |

### Tool Sources

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 |

## A2A Adapter

Optional Agent-to-Agent protocol support. Import separately:

```javascript
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.

### A2A Discovery

The agent loop includes an optional `discover_agent` tool that fetches Agent Cards from other A2A agents:

```javascript
const { mcp } = await AgentToolsServer.fromManifest( {
    manifest,
    llm: { ... },
    discovery: true    // Enables discover_agent tool
} )
```

## Composition with x402

No code coupling. Pure Express middleware ordering:

```javascript
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 )
```

## Documentation

Full FlowMCP documentation at [flowmcp.github.io](https://flowmcp.github.io). See the [Agent Server page](https://flowmcp.github.io/docs/ecosystem/agent-server/) for integration guides.

## License

MIT

## Contributing

PRs are welcome. Please open an issue first to discuss proposed changes.