|
1 | 1 | # Armory: An MCP Gateway for AI Agents |
2 | 2 |
|
3 | | -*Coming Soon* |
4 | | - |
5 | | -::: warning Draft |
6 | | -This article is in progress. Check back soon for the full post. |
7 | | -::: |
| 3 | +*Published January 2026* |
8 | 4 |
|
9 | 5 | ## Overview |
10 | 6 |
|
11 | | -Armory is to MCP servers what OpenRouter is to LLMs - one unified interface to many tool backends. |
| 7 | +Armory is to MCP servers what [OpenRouter](https://openrouter.ai/) is to LLMs—one unified interface to many tool backends. |
| 8 | + |
| 9 | +Just as OpenRouter provides a single API to access [400+ AI models](https://openrouter.ai/docs/guides/overview/models) from dozens of providers, Armory aggregates tools from multiple [Model Context Protocol (MCP)](https://modelcontextprotocol.io/specification/2025-11-25) servers into a single gateway. This eliminates the need to manage separate connections, handle different naming conventions, and maintain multiple integrations. |
| 10 | + |
| 11 | + |
| 12 | + |
| 13 | +## The Problem: Tool Fragmentation |
| 14 | + |
| 15 | +As AI agents become more capable, they need access to more tools—weather data, web search, databases, APIs, and custom business logic. The MCP standard, [introduced by Anthropic in November 2024](https://www.anthropic.com/news/model-context-protocol), provides a protocol for exposing these tools to LLMs. With [over 13,000 MCP servers](https://github.com/modelcontextprotocol/modelcontextprotocol) now available on GitHub, the ecosystem is thriving. |
| 16 | + |
| 17 | +But this growth creates a management challenge: |
| 18 | + |
| 19 | +- **Multiple endpoints**: Each MCP server runs on its own URL |
| 20 | +- **Tool name collisions**: Different servers might have tools with the same name (e.g., `search`) |
| 21 | +- **No unified discovery**: Clients must know about each server individually |
| 22 | +- **Connection overhead**: Managing connections to many servers adds complexity |
| 23 | + |
| 24 | +## The Solution: Hybrid MCP Gateway |
| 25 | + |
| 26 | +Armory solves this by acting as an MCP gateway that aggregates tools from multiple backend servers while preserving direct access when needed. |
| 27 | + |
| 28 | +### Endpoint Architecture |
| 29 | + |
| 30 | +Armory exposes three types of endpoints: |
| 31 | + |
| 32 | +| Endpoint | Purpose | |
| 33 | +|----------|---------| |
| 34 | +| `/mcp` | Aggregated endpoint—all tools from all backends with prefixed names | |
| 35 | +| `/mcp/{server}` | Direct passthrough to a specific backend's tools (unprefixed) | |
| 36 | +| `/.well-known/mcp.json` | Discovery metadata for clients | |
| 37 | + |
| 38 | +### Tool Namespacing |
| 39 | + |
| 40 | +To avoid collisions, tools are namespaced with a double-underscore prefix: |
| 41 | + |
| 42 | +``` |
| 43 | +{backend_prefix}__{tool_name} |
| 44 | +``` |
| 45 | + |
| 46 | +For example, if you have a `weather` backend with a `get_forecast` tool, it becomes available as `weather__get_forecast` on the aggregated endpoint. Clients can still access the original `get_forecast` name via the `/mcp/weather` mount point. |
| 47 | + |
| 48 | +## Implementation |
| 49 | + |
| 50 | +Armory is built with: |
| 51 | + |
| 52 | +- **[FastAPI](https://fastapi.tiangolo.com/)** for the HTTP server and Admin API |
| 53 | +- **[FastMCP](https://gofastmcp.com/) Client** for connecting to backend MCP servers |
| 54 | +- **SQLAlchemy** with async support for the database layer |
| 55 | +- **SQLite** (development) or **PostgreSQL** (production) for persistence |
| 56 | +- **Vue.js** for the Admin UI |
| 57 | + |
| 58 | +The gateway implements the MCP JSON-RPC protocol, handling `initialize`, `tools/list`, `tools/call`, and `ping` methods. |
| 59 | + |
| 60 | +### Backend Connection Flow |
| 61 | + |
| 62 | +On startup, Armory loads enabled backends from the database, connects to each MCP server, and caches their available tools. |
| 63 | + |
| 64 | + |
| 65 | + |
| 66 | +### Tool Call Flow |
| 67 | + |
| 68 | +When a client calls a tool through the aggregated endpoint, Armory looks up the tool in its registry, routes the call to the correct backend using the original (unprefixed) tool name, and records metrics. |
| 69 | + |
| 70 | + |
| 71 | + |
| 72 | +## The Admin UI |
| 73 | + |
| 74 | +Armory includes a full-featured Admin UI for managing backends and monitoring tool usage. |
| 75 | + |
| 76 | +### Dashboard |
12 | 77 |
|
13 | | -## Planned Sections |
| 78 | +The dashboard provides an at-a-glance view of: |
| 79 | +- Connected backends and their status |
| 80 | +- Total tools available across all backends |
| 81 | +- Aggregate call metrics (total calls, success rate, average latency) |
14 | 82 |
|
15 | | -### The Problem: Tool Fragmentation |
16 | | -- Multiple MCP servers, each with their own endpoint |
17 | | -- Different tool naming conventions |
18 | | -- No unified discovery mechanism |
19 | | -- Managing connections to many servers |
| 83 | + |
20 | 84 |
|
21 | | -### Our Solution: Hybrid MCP Gateway |
22 | | -- Aggregated endpoint (`/mcp`) - all tools with server prefixes |
23 | | -- Direct access (`/mcp/{server}`) - passthrough to specific servers |
24 | | -- Discovery metadata (`/.well-known/mcp.json`) |
| 85 | +### Backend Management |
25 | 86 |
|
26 | | -### Architecture Deep-Dive |
27 | | -- FastMCP for the gateway implementation |
28 | | -- Backend registration and health checking |
29 | | -- Tool namespacing to avoid conflicts |
30 | | -- Admin UI for management |
| 87 | +Add, configure, enable/disable, and refresh backends. Each backend can have: |
| 88 | +- Custom URL prefix for namespacing |
| 89 | +- Configurable timeout |
| 90 | +- Mount point enabled/disabled (for direct access) |
31 | 91 |
|
32 | | -### The Admin UI |
33 | | -- Dashboard showing connected backends |
34 | | -- Tool registry browser |
35 | | -- MCP Playground for testing tools |
36 | | -- Metrics and monitoring |
| 92 | + |
37 | 93 |
|
38 | | -### Docker Deployment |
39 | | -- Multi-service setup with Docker Compose |
40 | | -- Auto-registration of MCP servers |
41 | | -- Health endpoints and readiness checks |
| 94 | +### Tool Registry |
42 | 95 |
|
43 | | -### Code Examples |
44 | | -- Registering a new backend |
45 | | -- Calling tools through the gateway |
46 | | -- Building custom MCP servers with FastMCP |
| 96 | +Browse all available tools across backends with their descriptions and input schemas. |
| 97 | + |
| 98 | + |
| 99 | + |
| 100 | +### Metrics & Monitoring |
| 101 | + |
| 102 | +Track tool call performance with: |
| 103 | +- Success/error rates |
| 104 | +- Latency percentiles (p50, p95, p99) |
| 105 | +- Time-series charts for trend analysis |
| 106 | +- Per-tool breakdown |
| 107 | + |
| 108 | + |
| 109 | + |
| 110 | +## Running Armory |
| 111 | + |
| 112 | +### Quick Start with Docker |
| 113 | + |
| 114 | +```bash |
| 115 | +# Clone the repo |
| 116 | +git clone https://github.com/agentic-forge/forge-armory |
| 117 | +cd forge-armory |
| 118 | + |
| 119 | +# Start with Docker Compose |
| 120 | +docker compose up |
| 121 | +``` |
| 122 | + |
| 123 | +### Local Development |
| 124 | + |
| 125 | +```bash |
| 126 | +# Install dependencies |
| 127 | +uv sync |
| 128 | + |
| 129 | +# Run the server |
| 130 | +uv run armory serve |
| 131 | + |
| 132 | +# Add a backend |
| 133 | +uv run armory backend add weather --url http://localhost:4050/mcp |
| 134 | +``` |
| 135 | + |
| 136 | +### Environment Variables |
| 137 | + |
| 138 | +```bash |
| 139 | +ARMORY_DATABASE_URL=postgresql+asyncpg://user:pass@localhost:5432/forge_armory |
| 140 | +ARMORY_HOST=0.0.0.0 |
| 141 | +ARMORY_PORT=4042 |
| 142 | +``` |
| 143 | + |
| 144 | +## API Examples |
| 145 | + |
| 146 | +### Register a Backend |
| 147 | + |
| 148 | +```bash |
| 149 | +curl -X POST http://localhost:4042/admin/backends \ |
| 150 | + -H "Content-Type: application/json" \ |
| 151 | + -d '{ |
| 152 | + "name": "weather", |
| 153 | + "url": "http://localhost:4050/mcp", |
| 154 | + "enabled": true |
| 155 | + }' |
| 156 | +``` |
| 157 | + |
| 158 | +### List Tools via MCP |
| 159 | + |
| 160 | +```bash |
| 161 | +curl -X POST http://localhost:4042/mcp \ |
| 162 | + -H "Content-Type: application/json" \ |
| 163 | + -d '{ |
| 164 | + "jsonrpc": "2.0", |
| 165 | + "method": "tools/list", |
| 166 | + "id": 1 |
| 167 | + }' |
| 168 | +``` |
| 169 | + |
| 170 | +Response: |
| 171 | + |
| 172 | +```json |
| 173 | +{ |
| 174 | + "jsonrpc": "2.0", |
| 175 | + "result": { |
| 176 | + "tools": [ |
| 177 | + { |
| 178 | + "name": "weather__get_forecast", |
| 179 | + "description": "Get weather forecast for a location", |
| 180 | + "inputSchema": { ... } |
| 181 | + } |
| 182 | + ] |
| 183 | + }, |
| 184 | + "id": 1 |
| 185 | +} |
| 186 | +``` |
| 187 | + |
| 188 | +### Call a Tool |
| 189 | + |
| 190 | +```bash |
| 191 | +curl -X POST http://localhost:4042/mcp \ |
| 192 | + -H "Content-Type: application/json" \ |
| 193 | + -d '{ |
| 194 | + "jsonrpc": "2.0", |
| 195 | + "method": "tools/call", |
| 196 | + "params": { |
| 197 | + "name": "weather__get_forecast", |
| 198 | + "arguments": {"location": "London"} |
| 199 | + }, |
| 200 | + "id": 2 |
| 201 | + }' |
| 202 | +``` |
| 203 | + |
| 204 | +## What's Next |
| 205 | + |
| 206 | +Armory provides the foundation for more advanced agent patterns: |
| 207 | + |
| 208 | +- **Tool RAG**: Dynamic tool selection using semantic search instead of loading all tools into context |
| 209 | +- **Protocol Adapters**: Support for OpenAI Function Calling and other tool formats |
| 210 | +- **Rate Limiting & Auth**: Production-ready access controls |
| 211 | +- **Caching**: Cache tool responses for frequently-called operations |
47 | 212 |
|
48 | 213 | ## Source Code |
49 | 214 |
|
50 | | -- [forge-armory](https://github.com/agentic-forge/forge-armory) |
51 | | -- [mcp-weather](https://github.com/agentic-forge/mcp-weather) - Example MCP server |
52 | | -- [mcp-web-search](https://github.com/agentic-forge/mcp-web-search) - Example MCP server |
| 215 | +- [forge-armory](https://github.com/agentic-forge/forge-armory) — The MCP gateway |
| 216 | +- [forge-anvil](https://github.com/agentic-forge/forge-anvil) — CLI and web UI for testing MCP servers |
| 217 | +- [mcp-weather](https://github.com/agentic-forge/mcp-weather) — Example MCP server (Open-Meteo) |
| 218 | +- [mcp-web-search](https://github.com/agentic-forge/mcp-web-search) — Example MCP server (Brave Search) |
| 219 | + |
| 220 | +## References |
| 221 | + |
| 222 | +- [Model Context Protocol Specification](https://modelcontextprotocol.io/specification/2025-11-25) |
| 223 | +- [FastMCP Documentation](https://gofastmcp.com/) |
| 224 | +- [OpenRouter](https://openrouter.ai/) — Inspiration for the unified gateway approach |
53 | 225 |
|
54 | 226 | --- |
55 | 227 |
|
56 | | -*This is part of a series on building Agentic Forge.* |
| 228 | +*This is part of a series on building [Agentic Forge](https://agentic-forge.github.io).* |
0 commit comments