OpenSearch MCP Server
by hpower2
README.md
# OpenSearch MCP Server
A Model Context Protocol (MCP) server built with FastMCP for retrieving error stack traces by transaction ID (TID) from OpenSearch logs.
## Features
- **Error stack trace retrieval**: Get complete error stack traces by transaction ID (TID)
- **Docker-based setup**: Easy local development with Docker Compose
- **Environment variable configuration**: Flexible connection settings via environment variables
## Prerequisites
- Python 3.10 or higher
- Docker and Docker Compose (for running the MCP server in a container)
- An accessible OpenSearch instance (you provide your own)
- pip or uv for package management
## Installation
1. Clone the repository:
```bash
git clone <repository-url>
cd opensearch-mcp
```
2. Install dependencies:
```bash
pip install -r requirements.txt
```
Or using uv:
```bash
uv pip install -r requirements.txt
```
3. Set up environment variables (optional):
```bash
cp .env.example .env
# Edit .env with your OpenSearch connection settings
```
## Quick Start
**Prerequisites**: You need your own OpenSearch instance running and accessible. Configure the connection details in the `.env` file.
### Option 1: Using Docker Compose (Recommended)
1. Copy the environment file and configure your OpenSearch connection:
```bash
cp .env.example .env
# Edit .env and set OPENSEARCH_HOST and OPENSEARCH_PORT to your OpenSearch instance
```
2. Start the MCP server:
```bash
docker-compose up
```
This will start:
- MCP server on port 48081 (HTTP endpoint: `http://localhost:48081/mcp`)
The MCP server will connect to your OpenSearch instance using the configuration from `.env`.
To run in detached mode:
```bash
docker-compose up -d
```
### Option 2: Run MCP Server directly with Python
1. Set up environment variables:
```bash
cp .env.example .env
# Edit .env and configure OPENSEARCH_HOST and OPENSEARCH_PORT to your OpenSearch instance
```
2. Run the MCP server:
```bash
python server.py
```
The server will start on `http://localhost:48081/mcp`.
### Option 3: Run MCP Server with Docker (standalone)
1. Build the Docker image:
```bash
docker build -t opensearch-mcp .
```
2. Run the container with your OpenSearch connection details:
```bash
docker run --rm -p 48081:48081 \
-e OPENSEARCH_HOST=your-opensearch-host \
-e OPENSEARCH_PORT=9200 \
opensearch-mcp
```
Or use an env file:
```bash
docker run --rm -p 48081:48081 --env-file .env opensearch-mcp
```
The MCP server will be available at `http://localhost:48081/mcp`.
**Note**:
- Replace `your-opensearch-host` with your actual OpenSearch hostname or IP
- Use `host.docker.internal` (macOS/Windows) or your host IP (Linux) if OpenSearch is running on the host machine
- For remote instances, use the actual hostname or IP address
## Configuration
The MCP server reads connection settings from environment variables:
| Variable | Default | Description |
|----------|---------|-------------|
| `OPENSEARCH_HOST` | `localhost` | OpenSearch host |
| `OPENSEARCH_PORT` | `9200` | OpenSearch port |
| `OPENSEARCH_USE_SSL` | `false` | Enable SSL/TLS |
| `OPENSEARCH_VERIFY_CERTS` | `false` | Verify SSL certificates |
| `OPENSEARCH_USERNAME` | - | Username for authentication (optional) |
| `OPENSEARCH_PASSWORD` | - | Password for authentication (optional) |
When using Docker Compose, these variables are automatically available from the container environment.
## MCP Tools
### `stack_trace_tid`
Retrieve full error stack trace by transaction ID (TID).
**Parameters:**
- `tid` (string, required): Transaction ID to search for
- `index_pattern` (string, optional): Index pattern to search (default: `servicelogs-*`)
- `repo_name` (string, optional): Filter by repository/application name (maps to `application_name` field)
**Example:**
```python
{
"tid": "e1020584c0a74074a930c4e90e953912.139.17702793574644017",
"repo_name": "xxx"
}
```
**Returns:**
Formatted text with one log entry per line. Each line follows the format:
```
timestamp - level - message - application_name - project
```
If a separate `stack_trace` field exists in the log document, the format is:
```
timestamp - level - message - stack_trace - application_name - project
```
**Note:** Log entries are sorted chronologically by `@timestamp` in ascending order. Newlines in messages and stack traces are normalized to ` | ` (pipe characters) for single-line format.
**Example Output:**
```
2026-02-05T08:15:57.566Z - INFO - channel GOPAY got phone A0:1ec614bbcb328b7c66436a318d7b77e0 from token - xxx - xxx
2026-02-05T08:15:57.570Z - INFO - register user for A0:44b351780fec875b97ad01d9bb946f3e , system : SYSTEM_TYPE_ATOME, channel: GOPAY - user-service-provider - pintar-user
2026-02-05T08:15:58.263Z - ERROR - POST http://:8080/user/info -d [{"module":"RESIDENTIAL_INFO","module":"RESIDENTIAL_INFO",...}] ***RESPONSE*** 400 {"timestamp":1770279358262,"status":400,"error":"Bad Request","path":"/user/info"} - xxx - xxx
2026-02-05T08:15:58.264Z - ERROR - [Gopay Linking] Failed to save user info for userId : [400 ] during [POST] to [http://:8080/user/info] [PintarUserInfoFeignClient#saveUserInfo(List)]: [{"timestamp":1770279358262,"status":400,"error":"Bad Request","path":"/user/info"}] - feign.FeignException$BadRequest: [400 ] during [POST] to [http://:8080/user/info] [PintarUserInfoFeignClient#saveUserInfo(List)]: [{"timestamp":1770279358262,"status":400,"error":"Bad Request","path":"/user/info"}] | at feign.FeignException.clientErrorStatus(FeignException.java:243) | at feign.FeignException.errorStatus(FeignException.java:223) | at feign.FeignException.errorStatus(FeignException.java:213) | ... - xxx - xxx
2026-02-05T08:15:58.267Z - INFO - [GopayServiceImpl] linking account failed for userId: , error: INVALID_KYC - xxx - xxx
```
**Note:** Log entries are sorted by timestamp in ascending order. Stack traces (when present) are included as part of the message field, with stack frames separated by ` | ` (pipe characters).
## Usage with MCP Clients
The MCP server uses **streamableHttp** transport on port **48081**. This enables HTTP-based communication instead of stdio, making it easier to deploy and scale.
### Starting the Server
**Important**: Ensure your OpenSearch instance is running and accessible before starting the MCP server.
#### Option 1: Using Docker Compose (Recommended)
1. Configure your OpenSearch connection in `.env`:
```bash
cp .env.example .env
# Edit .env with your OpenSearch host and port
```
2. Start the MCP server:
```bash
docker-compose up
```
The MCP server will be available at `http://localhost:48081/mcp`.
#### Option 2: Running Locally
1. Set up environment variables:
```bash
cp .env.example .env
# Edit .env with your OpenSearch host and port
```
2. Run the MCP server:
```bash
python server.py
```
The server will start on `http://localhost:48081/mcp`.
#### Option 3: Using Docker Run
Build the image:
```bash
docker build -t opensearch-mcp .
```
Run the container with your OpenSearch connection:
```bash
docker run --rm -p 48081:48081 \
-e OPENSEARCH_HOST=your-opensearch-host \
-e OPENSEARCH_PORT=9200 \
opensearch-mcp
```
Or use an env file:
```bash
docker run --rm -p 48081:48081 --env-file .env opensearch-mcp
```
### Configuring MCP Clients
#### Cursor IDE
Add to your Cursor MCP settings (`mcp.json`):
```json
{
"mcpServers": {
"opensearch": {
"url": "http://localhost:48081/mcp"
}
}
}
```
#### Claude Desktop
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
```json
{
"mcpServers": {
"opensearch": {
"url": "http://localhost:48081/mcp"
}
}
}
```
#### Remote Access
If the MCP server is running on a remote machine, use the remote URL:
```json
{
"mcpServers": {
"opensearch": {
"url": "http://your-server-ip:48081/mcp"
}
}
}
```
### Configuration
The MCP server port and host can be configured via environment variables:
- `MCP_PORT`: Port for the HTTP server (default: 48081)
- `MCP_HOST`: Host to bind to (default: 0.0.0.0)
Example:
```bash
MCP_PORT=8080 MCP_HOST=127.0.0.1 python server.py
```
### Docker Networking Notes
- **Local development**: Use `http://localhost:48081/mcp`
- **VPN Access**: The docker-compose.yml uses `network_mode: host` to allow the container to access VPN resources through your host's network
- **Docker Compose**: When using host network mode, the container shares your host's network stack, including VPN connections
- **Remote access**: Ensure port 48081 is accessible from your MCP client
**Note**: With `network_mode: host`, the container uses your host's network directly, so:
- VPN resources are accessible from the container
- No port mapping is needed (ports are directly accessible on the host)
- The container can access resources that require VPN connection
## Development
### Project Structure
```
opensearch-mcp/
├── src/
│ └── opensearch_mcp/
│ ├── __init__.py
│ ├── server.py # FastMCP server with tool definitions
│ └── opensearch_client.py # OpenSearch client wrapper
├── docker-compose.yml # Docker Compose setup (OpenSearch + MCP server)
├── Dockerfile # MCP server Docker image
├── .dockerignore # Docker ignore file
├── .env.example # Example environment variables
├── mcp.json.example # Example MCP client configuration
├── requirements.txt # Python dependencies
├── pyproject.toml # Python project configuration
└── README.md # This file
```
### Running Tests
```bash
# Ensure your OpenSearch instance is running and accessible
# Start MCP server with Docker Compose
docker-compose up
# Or run MCP server locally
python server.py
```
### Docker Compose Services
The `docker-compose.yml` includes only the MCP server service:
1. **opensearch-mcp**: MCP server (uses `.env` file for configuration)
The MCP server service:
- Builds from the local Dockerfile
- Loads environment variables from `.env` file
- Connects to your OpenSearch instance using the configuration from `.env`
- Exposes HTTP endpoint on port 48081 at `/mcp` path
- Uses streamableHttp transport for MCP communication
**Note**: You need to provide your own OpenSearch instance. Configure the connection details in the `.env` file.
### Log Data Format
The MCP server expects log documents with the following structure:
```json
{
"@timestamp": "2024-01-01T10:00:00Z",
"level": "ERROR",
"msg": "Error occurred...",
"application_name": "xxx",
"project": "xxx",
"stack_trace": "feign.FeignException$BadRequest: [...] | at ...",
"TID": "e1020584c0a74074a930c4e90e953912.139.17702793574644017"
}
```
Key fields:
- `@timestamp`: Log timestamp (ISO 8601 format)
- `level`: Log level (INFO, WARN, ERROR, etc.)
- `msg`: Log message content
- `application_name`: Application/service name (used for filtering via `repo_name` parameter)
- `project`: Project/repository name
- `stack_trace`: Stack trace content (optional, formatted as pipe-separated lines)
- `TID`: Transaction ID field (searched using phrase matching across all fields)
## Troubleshooting
### Connection Errors
If you see connection errors:
1. Ensure your OpenSearch instance is running and accessible
2. Check OpenSearch health: `curl http://your-opensearch-host:9200/_cluster/health`
3. Verify environment variables in `.env` are set correctly (OPENSEARCH_HOST and OPENSEARCH_PORT)
4. If running in Docker, ensure the container can reach your OpenSearch instance (network configuration)
### No Results Found
- Check that your index pattern matches existing indices (default: `servicelogs-*`)
- Verify that the TID exists in your log documents (searched using phrase matching across all fields)
- If using `repo_name` filter, verify that `application_name` field exists and matches the filter value
- Ensure the TID format matches what's stored in your logs
### SSL/TLS Issues
If using SSL:
- Set `OPENSEARCH_USE_SSL=true`
- Configure `OPENSEARCH_VERIFY_CERTS` appropriately
- Provide CA certificates if needed
## License
MIT
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues