MCP Search Server
A Model Context Protocol (MCP) server built with FastMCP that provides semantic search capabilities by integrating with an OpenSearch-based search service. This server uses Server-Sent Events (SSE) transport and includes comprehensive validation, error handling, and Docker support.
Features
- ✅ FastMCP Integration: Built on FastMCP framework with SSE transport
- ✅ Pydantic Validation: Comprehensive input/output validation
- ✅ Docker Support: Multi-stage Dockerfile and docker-compose configuration
- ✅ Retry Logic: Automatic retry with exponential backoff
- ✅ Structured Logging: JSON and console logging support
- ✅ Health Checks: Built-in health monitoring
- ✅ Type Safety: Full type hints throughout the codebase
- ✅ Security: Non-root user in Docker container
- ✅ Configurable: Environment-based configuration
Architecture
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ MCP Client │ SSE │ MCP Search │ HTTP │ Search Service │
│ (Claude/App) │ ────────►│ Server │ ────────►│ (OpenSearch) │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│
│
▼
┌──────────────┐
│ Docker │
│ Network │
│ test_network │
└──────────────┘
Prerequisites
- Python 3.11+
- Docker and Docker Compose (for containerized deployment)
- Search service running on
search_service:8008(or configured URL)
Quick Start
Local Development
-
Clone the repository
cd d:\projects\DeepResearch\MCPSearch -
Install dependencies
pip install -r requirements.txt -
Configure environment
cp mcp/.env.example mcp/.env # Edit mcp/.env with your configuration -
Run the server
cd mcp python server.py
The server will start on http://0.0.0.0:8080
Docker Deployment
-
Build and run with Docker Compose
docker-compose up -d --build -
Check logs
docker logs -f mcp_search_server -
Check health
curl http://localhost:8080/health
Configuration
All configuration is managed through environment variables. Edit mcp/.env or set environment variables directly.
Environment Variables
| Variable | Default | Description |
|---|---|---|
SEARCH_SERVICE_URL | http://search_service:8008 | Base URL of the search service |
SEARCH_TIMEOUT | 30 | HTTP request timeout in seconds |
MCP_SERVER_HOST | 0.0.0.0 | Server bind address |
MCP_SERVER_PORT | 8080 | Server port |
DEFAULT_USER_ID | system | Default user ID for requests |
LOG_LEVEL | INFO | Logging level (DEBUG, INFO, WARNING, ERROR) |
LOG_FORMAT | json | Log format (json or console) |
MAX_RETRIES | 3 | Maximum retry attempts for failed requests |
RETRY_DELAY | 1.0 | Base delay between retries in seconds |
Example .env File
# Search Service Configuration
SEARCH_SERVICE_URL=http://search_service:8008
SEARCH_TIMEOUT=30
# MCP Server Configuration
MCP_SERVER_HOST=0.0.0.0
MCP_SERVER_PORT=8080
# Default Values
DEFAULT_USER_ID=system
# Logging Configuration
LOG_LEVEL=INFO
LOG_FORMAT=json
# Request Configuration
MAX_RETRIES=3
RETRY_DELAY=1.0
Usage
Available Tools
search_documents
Search for documents in OpenSearch indices using the integrated search service.
Parameters:
query(string, required): Search query text (minimum 1 character)user_id(string, required): User identifier for trackingindices(array of strings, required): OpenSearch indices to searchfilters(object, optional): Search filterscategories(array of strings): Filter by categoriesdate_from(string): Start date (ISO format)date_to(string): End date (ISO format)tags(array of strings): Filter by tagsmetadata(object): Additional metadata filters
Returns:
{
"results": [
{
"id": "doc_123",
"score": 0.95,
"index": "documents",
"source": {
"title": "Document Title",
"content": "Document content..."
},
"highlights": {
"content": ["...highlighted <em>text</em>..."]
}
}
],
"query_expanded": "query with synonyms",
"processing_time_ms": 45.2,
"request_id": "req_abc123"
}
Example Usage
Basic Search:
{
"query": "machine learning",
"user_id": "user123",
"indices": ["documents"]
}
Advanced Search with Filters:
{
"query": "deep learning",
"user_id": "researcher_001",
"indices": ["research_papers", "articles"],
"filters": {
"categories": ["AI", "Machine Learning"],
"tags": ["neural-networks", "computer-vision"],
"date_from": "2023-01-01",
"date_to": "2024-12-31",
"metadata": {
"language": "en",
"difficulty": "advanced"
}
}
}
API Documentation
Search Service Integration
The MCP server communicates with a search service at http://search_service:8008/search (configurable).
Expected Search Service API:
- Endpoint:
POST /search - Content-Type:
application/json
Request Body:
{
"query": "search text",
"user_id": "user_identifier",
"indices": ["index1", "index2"],
"filters": {
"categories": ["category1"],
"date_from": "2023-01-01",
"date_to": "2024-12-31",
"tags": ["tag1", "tag2"],
"metadata": {}
}
}
Response Body:
{
"results": [...],
"query_expanded": "expanded query",
"processing_time_ms": 45.2,
"request_id": "unique_id"
}
Docker
Building the Image
docker build -t mcp-search-server .
Running the Container
docker run -d \
--name mcp_search_server \
--network test_network \
-p 8080:8080 \
-e SEARCH_SERVICE_URL=http://search_service:8008 \
mcp-search-server
Docker Compose
The provided docker-compose.yml sets up:
- MCP Search Server on port 8080
- Connected to
test_network - Health checks enabled
- Automatic restart policy
- Logging configuration
Start services:
docker-compose up -d
Stop services:
docker-compose down
View logs:
docker-compose logs -f mcp_search_server
Development
Project Structure
MCPSearch/
├── mcp/
│ ├── __init__.py
│ ├── server.py # Main MCP server implementation
│ ├── config.py # Configuration management
│ ├── .env # Environment variables
│ ├── prompts/
│ │ └── search_prompts.md # Usage examples and documentation
│ └── tools/ # (Reserved for additional tools)
├── requirements.txt # Python dependencies
├── Dockerfile # Multi-stage Docker build
├── docker-compose.yml # Docker orchestration
└── README.md # This file
Running Tests
# Install dev dependencies
pip install pytest pytest-asyncio httpx
# Run tests (if implemented)
pytest tests/
Code Quality
The codebase follows:
- PEP 8 style guidelines
- Type hints throughout
- Docstrings for all public functions
- Pydantic for data validation
- Structured logging with context
Adding New Tools
To add new MCP tools:
- Define the tool function in
server.pyor create a new module inmcp/tools/ - Decorate with
@mcp.tool() - Add proper type hints and docstrings
- Update documentation in
prompts/directory
Example:
@mcp.tool()
async def new_tool(param: str) -> Dict[str, Any]:
"""Tool description.
Args:
param: Parameter description
Returns:
Result description
"""
# Implementation
return {"result": "data"}
Logging
The server uses structured logging with configurable output format.
JSON Format (default for production):
{
"timestamp": "2024-01-15T10:30:45.123Z",
"level": "info",
"event": "search_request_successful",
"request_id": "req_123",
"results_count": 5,
"processing_time_ms": 45.2
}
Console Format (for development):
2024-01-15 10:30:45 [info] search_request_successful request_id=req_123 results_count=5
Configure via LOG_FORMAT environment variable.
Error Handling
The server implements comprehensive error handling:
Validation Errors
{
"error": "Validation failed: query must be at least 1 character"
}
HTTP Errors
{
"error": "Search service returned error 500: Internal Server Error"
}
Connection Errors
{
"error": "Failed to connect to search service: Connection refused"
}
Retry Logic
- Automatic retry for 5xx errors and connection failures
- Exponential backoff: 1s, 2s, 3s
- Maximum 3 retry attempts (configurable)
Troubleshooting
Server Won't Start
-
Check if port 8080 is available:
netstat -ano | findstr :8080 # Windows lsof -i :8080 # Linux/Mac -
Verify Python version:
python --version # Should be 3.11+ -
Check configuration:
cat mcp/.env
Can't Connect to Search Service
-
Verify search service is running:
docker ps | grep search_service -
Check network connectivity:
docker network inspect test_network -
Test search service directly:
curl -X POST http://search_service:8008/search \ -H "Content-Type: application/json" \ -d '{"query":"test","user_id":"test","indices":["test"]}'
Container Issues
-
Check container logs:
docker logs mcp_search_server -
Inspect container:
docker inspect mcp_search_server -
Rebuild image:
docker-compose down docker-compose build --no-cache docker-compose up -d
Performance
Optimization Tips
- Connection Pooling: HTTP client uses connection pooling (max 20 connections)
- Timeout Configuration: Adjust
SEARCH_TIMEOUTbased on search complexity - Retry Strategy: Configure
MAX_RETRIESandRETRY_DELAYfor your needs - Index Selection: Only search necessary indices to improve performance
- Filter Usage: Use filters instead of including criteria in query text
Monitoring
Key metrics to monitor:
processing_time_msin search responses- HTTP request/response times
- Error rates and retry attempts
- Container resource usage
Security
Best Practices Implemented
- ✅ Non-root user in Docker container (UID 1000)
- ✅ No sensitive data in logs
- ✅ Input validation with Pydantic
- ✅ Timeout protection against slow requests
- ✅ Health check endpoints
- ✅ Minimal container image (python:3.11-slim)
Recommendations
- Use HTTPS in production
- Implement authentication/authorization
- Set up rate limiting
- Use secrets management for sensitive config
- Regular security updates for dependencies
Contributing
Contributions are welcome! Please follow these guidelines:
- Fork the repository
- Create a feature branch
- Make your changes with tests
- Update documentation
- Submit a pull request
License
[Specify your license here]
Support
For issues and questions:
- Check the Troubleshooting section
- Review logs:
docker logs mcp_search_server - Check search service logs
- Open an issue on GitHub
Resources
- FastMCP Documentation
- Model Context Protocol
- Pydantic Documentation
- OpenSearch Documentation
- Docker Documentation
Changelog
Version 1.0.0 (2024-01-15)
- Initial release
- FastMCP integration with SSE transport
- Search documents tool
- Pydantic validation
- Docker support
- Comprehensive documentation
Built with ❤️ using FastMCP and Python
