mcp_arena
mcp_arena is an opinionated Python library for building MCP (Model Context Protocol) servers: 30+ ready-to-use presets you can stand up in one call, plus a thin bridge into a LangChain agent.
The headline feature is the MCP server — drop one in, run it, talk to it over stdio / SSE / HTTP:
# server.py
from mcp_arena.presents.github import GithubMCPServer
server = GithubMCPServer(
token="ghp_…", # or pull from $GITHUB_TOKEN
host="127.0.0.1",
port=8000,
transport="stdio", # stdio (default) | sse | http
debug=False,
)
if __name__ == "__main__":
server.run()
Any MCP client can now talk to it. The full preset list, constructor kwargs, and BaseMCPServer surface are in MCP_SERVERS_GUIDE.md. The LangChain-agent bridge is at the bottom of this README — read after you've understood the server side.
0.4.0 release: the old
ReflectionAgent/ReactAgent/PlanningAgent/ policies / memory / router stack is gone. The agent subsystem is now one function:make_mcp_agent. Migration guide inCHANGELOG.md.
Why mcp_arena?
- 30+ ready-to-run MCP server presets. Slack, GitHub, Notion, Gmail, PostgreSQL, Mongo, Redis, S3, browsers, video, audio, PDFs, QR codes, webscraping, and more — install one extra, import one class, call
server.run(). Any MCP-compatible client can talk to it. - Each preset is a real
BaseMCPServer. Tools register at construction, are exposed on_registered_toolsfor inspection, and the server can be started over stdio / SSE / HTTP in one call. - Lazy-loaded presets.
mcp_arena.presents.__init__AST-scans the directory; importing one preset doesn't pull in unrelated deps. - Drop-in extension. New MCP server? Write a
*Serversubclass inmcp_arena/presents/<name>.py; it's auto-discovered. - Optional LangChain bridge.
make_mcp_agent(llm, servers, ...)is the only function that wires the same server objects into a LangGraph agent. Forward anycreate_agentkwarg through**kwargs.
Install
⚠️
pip install mcp-arenaships three general-purpose presets in core —LocalOperationsMCPServer,GenericAPIMCPServer, andSMTPServer— so you can run a real MCP server with zero extra setup. Every other preset is gated behind an extra so you only pay for the third-party packages you actually need.
pip install mcp-arena # 3 core presets work out of the box
pip install "mcp-arena[github]" # + GitHub preset (PyGithub)
pip install "mcp-arena[github,slack]" # + several presets
pip install "mcp-arena[all]" # + every preset (~30 packages)
pip install "mcp-arena[agents]" # + LangChain bridge (langchain + MCP adapter)
What pip install mcp-arena does give you out of the box:
mcp_arena.mcp.server.BaseMCPServer— the base classmcp_arena.presentslazy loader — every preset class is importableLocalOperationsMCPServer— file / system / process tools (usespsutil+pyautogui)GenericAPIMCPServer— make any HTTP API call (useshttpx)SMTPServer— send email via any SMTP server (pure stdlib)mcp_arena.agent.make_mcp_agent/ToolRegistry/BaseToolmcp-arenaCLI (mcp-arena list,mcp-arena run <preset>)
What it does not install: anything else. If you try to instantiate a preset whose required dep isn't installed, you get a clear ImportError pointing at the exact install command — for example:
PyPDF2, fitz, pdfplumber and reportlab are required for this MCP server but are not installed.
Install it with: pip install "mcp-arena[pdf]"
Pick the right extra from INSTALLATION.md or MCP_SERVERS.md.
Python 3.12+. See INSTALLATION.md for the full extras table.
Run an MCP server
Every preset is a BaseMCPServer subclass. After construction, call server.run() to start serving.
Stdio (default — works with any local MCP client)
from mcp_arena.presents.github import GithubMCPServer
server = GithubMCPServer(token="ghp_…")
server.run() # transport="stdio"
Now point any MCP-compatible client at the process (e.g. Claude Desktop, Cursor, the mcp-arena CLI).
HTTP / SSE (for remote clients)
server = GithubMCPServer(token="ghp_…", transport="sse", host="0.0.0.0", port=8001)
server.run()
# -> listening on http://0.0.0.0:8001/sse
# or streamable-http:
server = GithubMCPServer(token="ghp_…", transport="http", port=8001)
server.run()
# -> listening on http://0.0.0.0:8001/mcp
| Transport | Endpoint | When to use |
|---|---|---|
stdio (default) |
in-process via stdin/stdout | local clients, the make_mcp_agent flow |
sse |
http://<host>:<port>/sse |
browser clients, streaming |
http |
http://<host>:<port>/mcp |
multi-process / networked setups |
streamable-http |
alias for http |
— |
Credentials: pass them in or pull from os.environ
# Inline:
server = GithubMCPServer(token="ghp_…")
# Env-var fallback (most presets read these for you):
# GITHUB_TOKEN, SLACK_BOT_TOKEN, NOTION_API_KEY, TWILIO_* …
server = GithubMCPServer() # picks up GITHUB_TOKEN
from mcp_arena import … calls python-dotenv.load_dotenv() for you, so a project-root .env is read automatically.
From the CLI
# List every preset mcp_arena knows about
mcp-arena list
# Show options for one preset
mcp-arena run github --help
# Start a server (stdio by default; pass --transport sse|http for network)
mcp-arena run github --token "$GITHUB_TOKEN"
mcp-arena run github --token "$GITHUB_TOKEN" --transport sse --host 0.0.0.0 --port 8001
Use a preset programmatically without the MCP protocol
You don't have to speak MCP — BaseMCPServer exposes the registered tools directly:
server = AudioMCPServer()
for tool_name in server.get_registered_tools():
print(tool_name)
# Or wrap them as plain Python callables:
from mcp_arena.wrapper import MCPAgentWrapper
for tool in MCPAgentWrapper(server).get_tools():
print(tool["function"]["name"])
See MCP_SERVERS_GUIDE.md for the full preset list, the BaseMCPServer constructor surface, and how to write your own.
Available presets
Every preset is one extra. Install what you need; nothing else gets pulled in.
Communication
slack, whatsapp, gmail, outlook, smtp, mail, notification
Dev platforms
github, gitlab, bitbucket
Productivity
notion, confluence, jira
Data & storage
postgres, mongo, redis, vectordb
Cloud / OS
aws (S3), cloudstorage, docker, local_operation, screencapture
Browser / web / media
browser, webscraping, generic_api, image, video, audio, pdf, qrcode, spreadsheet
See docs/MCP_SERVERS_GUIDE.md for the full table, kwargs, and transport notes.
Write your own preset
# mcp_arena/presents/greeter.py
from mcp_arena.mcp.server import BaseMCPServer
class GreeterMCPServer(BaseMCPServer):
def _register_tools(self):
@self.mcp_server.tool()
def greet(name: str) -> str:
"""Say hello."""
return f"Hello, {name}!"
# Now importable:
from mcp_arena.presents import GreeterMCPServer
The lazy loader in mcp_arena.presents AST-discovers every *Server class in the directory. Drop the file, import the class — done.
Architecture
┌─────────────────────────────────────────────────────────┐
│ mcp_arena.presents │
│ ~30 *MCPServer subclasses (auto-discovered) │
│ Browser · Slack · GH · Postgres · AWS · ... │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ BaseMCPServer.run() │
│ stdio / sse / http — talks to any MCP client │
└─────────────────────────────────────────────────────────┘
│
▼ (optional)
┌─────────────────────────────────────────────────────────┐
│ mcp_arena.agent │
│ • make_mcp_agent(llm, servers, ...) → LangGraph agent │
│ • ToolRegistry (register / keep / drop / rename / │
│ to_openai / get_callables) │
│ • BaseTool (subclass-this for non-MCP tools) │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ langchain.agents.create_agent → langgraph runnable │
│ (compiled via langchain-mcp-adapters.MultiServerMCP…) │
└─────────────────────────────────────────────────────────┘
The MCP-server layer is the product. The agent layer is a thin add-on that wraps the same server objects with a LangGraph.
Documents
- MCP Servers — quick reference: every preset, what extra to install, what env vars each reads, ready-to-copy install commands.
- MCP Servers Guide — every preset in detail;
BaseMCPServerconstructor surface; how to write your own. - Quick Start — 10-step walkthrough.
- Installation Guide — full extras table (one entry per preset / per group).
- Tools Guide —
ToolRegistry,BaseTool, custom MCP presets.
Agent & LangChain docs (read after the server-side docs above):
- Agent Guide —
make_mcp_agentreference, forwardedcreate_agentparams, troubleshooting. - LANGCHAIN_INTEGRATION.md — multi-server, transport choices, sync wrapper, migration from 0.3.x.
- tutorial.md — end-to-end "Jarvis" build (local-fs + GitHub agent).
- CHANGELOG.md — version history & migration guide.
Bonus: wire a server to a LangChain agent
If you already have a LangChain workflow and want to give it access to MCP tools, make_mcp_agent is the one-line bridge:
import asyncio, os
from langchain_openai import ChatOpenAI
from mcp_arena.agent import make_mcp_agent
from mcp_arena.presents.github import GithubMCPServer
from mcp_arena.presents.slack import SlackMCPServer
async def main():
agent = await make_mcp_agent(
ChatOpenAI(model="gpt-4o"),
[
GithubMCPServer(token=os.environ["GITHUB_TOKEN"]),
SlackMCPServer(token=os.environ["SLACK_BOT_TOKEN"]),
],
system_prompt="You can search GitHub and post to Slack.",
name="devops_bot",
)
out = await agent.ainvoke({
"messages": [{
"role": "user",
"content": "Find the top-3 starred repos in my org and post links to #general.",
}],
})
print(out["messages"][-1].content)
asyncio.run(main())
make_mcp_agent handles the connection between MCP-server transports and the LangChain MultiServerMCPClient, then forwards to langchain.agents.create_agent. See LANGCHAIN_INTEGRATION.md.
Filter tools before they reach the model
from mcp_arena.agent import ToolRegistry, make_mcp_agent
reg = ToolRegistry().register_server(slack_server)
print("Available:", reg.names()) # ['chat_postMessage', 'list_channels', ...]
reg.keep("chat_postMessage", "list_channels")
agent = await make_mcp_agent(
ChatOpenAI(model="gpt-4o"),
[slack_server],
names=reg.names(), # only these tools become agent tools
)
Add a custom (non-MCP) tool
from mcp_arena.agent import BaseTool, make_mcp_agent
class ShoutTool(BaseTool):
def __init__(self):
super().__init__(name="shout", description="Uppercase a string")
def execute(self, s: str) -> str:
return s.upper()
agent = await make_mcp_agent(
ChatOpenAI(model="gpt-4o"),
[slack_server],
extra_tools=[ShoutTool()],
)
Contributing
git clone https://github.com/SatyamSingh8306/mcp_arena
cd mcp_arena
pip install -e ".[complete]"
pytest
black .
ruff check .
mypy mcp_arena
Priority areas: new presets, bug fixes, doc accuracy.
Requirements
- Python 3.12+
- An MCP-compatible client to actually consume the servers (or use
make_mcp_agentto wire one into LangChain) - Optional: your LLM provider's
langchain-*adapter for the agent flow
License
MIT — see LICENSE.
Links
Metadata
Release files for mcp-arena 0.4.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mcp_arena-0.4.1.tar.gz | 184.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mcp_arena-0.4.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 351.0 kB
Release files / mcp_arena-0.4.1.tar.gz
| Download URL | mcp_arena-0.4.1.tar.gz |
|---|---|
| Size | 184.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b0f2876a960de2923a3605a715f5daad1f4fdbbb18f69ac8561706742f8635d2
|
|
BLAKE2b-256 checksum How to use checksums |
79ecc2cb6a4a85247789b803aa529a65f81c4a7cfa148b9a230c7f57e8f55634
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.8
|
Release files / mcp_arena-0.4.1-py3-none-any.whl
| Download URL | mcp_arena-0.4.1-py3-none-any.whl |
|---|---|
| Size | 167.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8ac2e222e778ca2fd814b307c03d2087d8b58e1a56dbcd039beba1bcb7dacf52
|
|
BLAKE2b-256 checksum How to use checksums |
4e3b6bbe17a094017af7383bea4ee30d1561b814f5d482f3a1f9337b6d6552ce
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.8
|