# MCP (Multi-Capability Proxy) Server
This project implements a simple MCP server using Flask. The server can host multiple "tools," where each tool exposes functionalities by calling external REST APIs.
## Project Structure
```
.
├── mcp_server.py # Main Flask application, loads tools and routes requests
├── config.py # Configuration for tools (e.g., API base URLs)
├── requirements.txt # Python dependencies
├── tools/ # Directory for tool implementations
│ ├── __init__.py # Makes 'tools' a Python package
│ └── example_tool.py # An example tool implementation
└── README.md # This file
```
## Setup and Running
1. **Clone the repository (if applicable) or ensure all files are in place.**
2. **Create a virtual environment (recommended):**
```bash
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
```
3. **Install dependencies:**
```bash
pip install -r requirements.txt
```
4. **Run the server:**
```bash
python mcp_server.py
```
The server will start, by default on `http://127.0.0.1:5001`. It will also create the `tools` directory and a basic `example_tool.py` if they don't exist upon first run (though they are included in this setup).
## Using the MCP Server
The server exposes the following endpoints:
* `GET /`: Lists all available tools and their descriptions.
* `GET /<tool_name>/<action>?param1=value1¶m2=value2`: Executes an action for a specific tool using GET. Parameters are passed as query strings.
* `POST /<tool_name>/<action>`: Executes an action for a specific tool using POST. Parameters should be sent as a JSON body.
### Example: Interacting with `example_tool`
The `example_tool` interacts with `https://jsonplaceholder.typicode.com`.
1. **List available tools:**
```bash
curl http://127.0.0.1:5001/
```
This will show `example_tool` and its available actions.
2. **Get all posts (using `example_tool`):**
```bash
curl http://127.0.0.1:5001/example_tool/get_posts
```
3. **Get a specific post by ID (using `example_tool`):**
```bash
curl http://127.0.0.1:5001/example_tool/get_post_by_id?id=1
```
4. **Create a new post (using `example_tool`):**
```bash
curl -X POST -H "Content-Type: application/json" \
-d '{"data": {"title": "My New Post", "body": "This is the content.", "userId": 1}}' \
http://127.0.0.1:5001/example_tool/create_post
```
## Adding New Tools
1. **Create a new Python file** in the `tools/` directory (e.g., `my_new_tool.py`).
2. **Implement the `execute(action, params)` function:**
* This function will receive the `action` name (string) and `params` (dictionary) from the request.
* It should contain the logic to call the external API based on the action and params.
* It must return a JSON-serializable dictionary or list.
3. **Implement the `get_tool_description()` function:**
* This function should return a dictionary describing the tool, including its name, a general description, and a dictionary of available actions with their descriptions and parameters. See `tools/example_tool.py` for a template.
4. **(Optional) Add configuration** for your new tool in `config.py` and import it into your tool file.
5. **Restart the MCP server.** It will automatically detect and load the new tool.
Example structure for a new tool (`tools/my_new_tool.py`):
```python
import requests
# from config import MY_NEW_TOOL_CONFIG # If you have config
# MY_API_BASE_URL = MY_NEW_TOOL_CONFIG.get("BASE_URL", "default_url_if_any")
def execute(action, params=None):
if params is None:
params = {}
if action == "some_action":
# api_key = params.get("api_key")
# some_id = params.get("id")
# response = requests.get(f"{MY_API_BASE_URL}/endpoint/{some_id}?key={api_key}")
# response.raise_for_status()
# return response.json()
return {"message": f"Action 'some_action' executed with params: {params}"}
elif action == "another_action":
# data_payload = params.get("data")
# response = requests.post(f"{MY_API_BASE_URL}/other_endpoint", json=data_payload)
# response.raise_for_status()
# return response.json()
return {"message": f"Action 'another_action' executed with data: {params.get('data')}"}
else:
return {"error": f"Action '{action}' not found in my_new_tool"}
def get_tool_description():
return {
"name": "My New Tool",
"description": "This tool does new and exciting things.",
"actions": {
"some_action": {
"description": "Performs some action that requires an ID.",
"params": {"id": "string (required)", "api_key": "string (optional)"},
"method": "GET", # Or POST, PUT, DELETE
"path_template": "/endpoint/{id}"
},
"another_action": {
"description": "Performs another action that requires a data payload.",
"params": {"data": "object (required in body)"},
"method": "POST",
"path_template": "/other_endpoint"
}
}
}
```
This provides a flexible way to extend the MCP server's capabilities.