AWS OpenSearch MCP Server
# AWS OpenSearch MCP, read-only
MCP em Python para investigar domínios do **Amazon OpenSearch Service** usando profiles AWS locais, incluindo SSO. O servidor foi desenhado para operação segura em CI, QA, produção e telemetry, sem ferramentas de escrita.
## O que ele entrega
- Profiles permitidos: `ci`, `qa`, `prod` e `telemetry`.
- Regiões permitidas por configuração.
- Descoberta de domínios AWS-managed com `boto3`.
- Targets customizados/self-managed, incluindo OpenSearch atrás de Nginx ou proxy.
- Autenticação no data plane com AWS Signature Version 4, Basic, header de ambiente ou nenhuma autenticação somente para targets explícitos.
- Consultas limitadas por tamanho, timeout e quantidade de documentos.
- Redação de campos com aparência de segredo ou credencial.
- Diagnósticos específicos para shards, flood stage, mappings e timestamps antigos.
- Nenhum endpoint genérico de request e nenhuma ferramenta de escrita.
## Tools disponíveis
| Tool | Objetivo |
|---|---|
| `list_aws_profiles` | Mostra profiles permitidos, disponibilidade local e regiões |
| `list_domains` | Lista domínios por profile e região |
| `get_domain_config` | Lê configuração AWS do domínio |
| `get_cluster_health` | Saúde green/yellow/red |
| `get_cluster_stats` | Estatísticas gerais do cluster |
| `list_indices` | Índices, tamanho, documentos e shards |
| `get_index_details` | Settings, mappings, aliases e stats |
| `search_index` | Query DSL read-only com limites |
| `get_latest_documents` | Documentos mais recentes por timestamp |
| `get_field_mapping` | Tipo e conflito de mapping de um campo |
| `get_field_count` | Uso de `index.mapping.total_fields.limit` |
| `get_shard_allocation` | Distribuição e estado dos shards |
| `explain_unassigned_shard` | Motivo de shard não alocado |
| `get_disk_allocation` | Disco por nó |
| `get_cluster_settings` | Settings transient, persistent e default |
| `get_indexing_stats` | Indexação, busca, merges, refresh e segmentos |
| `get_pending_tasks` | Tarefas pendentes no cluster manager |
| `get_ingest_pipelines` | Pipelines de ingestão |
| `diagnose_cluster` | Diagnóstico consolidado de saúde, disco e flood stage |
| `diagnose_timestamp` | Min/max, mapping e amostras para dados antigos |
## Pré-requisitos
- Python 3.10 ou superior.
- `uv` recomendado.
- Profiles AWS já configurados em `~/.aws/config` e `~/.aws/credentials`.
- Rota de rede até o endpoint. Para domínio VPC-only, a máquina precisa estar na VPN, VPC ou em um túnel apropriado.
## Instalação
```bash
git clone https://github.com/thiagorchaves/aws-opensearch-mcp-server.git
cd aws-opensearch-mcp-server
cp config.example.yaml config.yaml
uv sync --extra dev
```
Valide os profiles utilizados:
```bash
aws sts get-caller-identity --profile telemetry
aws opensearch list-domain-names --profile telemetry --region us-east-1
```
Quando o profile usa AWS SSO:
```bash
aws sso login --profile telemetry
```
## Rodar manualmente
```bash
AWS_OPENSEARCH_MCP_CONFIG="$PWD/config.yaml" \
AWS_SDK_LOAD_CONFIG=1 \
uv run aws-opensearch-mcp
```
O transporte padrão é `stdio`, portanto o processo aparentemente fica sem imprimir respostas no terminal. Isso é esperado: ele aguarda um cliente MCP.
## Targets customizados, self-managed e Nginx
As tools que recebem `domain` também aceitam o nome de um target em `profiles.settings.<profile>.opensearch_targets`. Quando o nome corresponde a um target configurado, o servidor conecta diretamente em `endpoint_url` e **não chama `DescribeDomain`**. Caso contrário, o fluxo AWS-managed permanece inalterado: `DescribeDomain` resolve e valida o domínio antes da conexão.
```yaml
profiles:
settings:
telemetry:
# Mantido por compatibilidade: valida o domínio AWS e usa o proxy no data plane.
endpoint_overrides:
logs-production: https://nginx.internal.example
# Não chama DescribeDomain; use `nginx-logs` no parâmetro domain.
opensearch_targets:
nginx-logs:
endpoint_url: https://nginx.internal.example
auth_mode: sigv4
signing_region: us-east-1 # padrão: região informada à tool
signing_service: es # padrão: es
signing_host: search-logs.us-east-1.es.amazonaws.com
tls_verify: true # padrão: true
self-managed-basic:
endpoint_url: https://opensearch.internal.example
auth_mode: basic
username_env: OPENSEARCH_READONLY_USERNAME
password_env: OPENSEARCH_READONLY_PASSWORD
self-managed-header:
endpoint_url: https://opensearch.internal.example
auth_mode: header
header_name: Authorization
header_value_env: OPENSEARCH_READONLY_AUTHORIZATION
```
`endpoint_url` deve usar `https://` por padrão; `http://` só é permitido com `allow_insecure_http: true` no próprio target. URLs com `..` no path, query string ou fragment são rejeitadas. O servidor rejeita campos não suportados no target, portanto senhas e tokens literais em YAML não são aceitos: `basic` e `header` leem seus valores exclusivamente das variáveis de ambiente indicadas.
Para `sigv4`, `signing_host` é aplicado como o header HTTP `Host`, que é o valor usado pelo `AWSV4SignerAuth` ao calcular a assinatura, enquanto a conexão TCP/TLS continua apontando para `endpoint_url`. Isso exige que o Nginx/proxy aceite e encaminhe esse Host. Não há canal separado de host de assinatura no `opensearch-py`; se o proxy precisar receber um Host diferente do host assinado, é necessária uma custom connection/proxy que resolva essa tradução.
`auth_mode: none` é permitido exclusivamente em um `opensearch_targets` configurado de forma explícita; domínios AWS-managed e `endpoint_overrides` legados continuam usando SigV4.
## Configuração no Kiro
Use o arquivo de usuário `~/.kiro/settings/mcp.json` ou o arquivo do workspace `.kiro/settings/mcp.json`:
```json
{
"mcpServers": {
"aws-opensearch-readonly": {
"command": "uv",
"args": [
"--directory",
"/home/SEU_USUARIO/Projects/aws-opensearch-mcp-server",
"run",
"aws-opensearch-mcp"
],
"env": {
"AWS_OPENSEARCH_MCP_CONFIG": "/home/SEU_USUARIO/Projects/aws-opensearch-mcp-server/config.yaml",
"AWS_SDK_LOAD_CONFIG": "1",
"AWS_OPENSEARCH_MCP_LOG_LEVEL": "INFO"
},
"timeout": 120000
}
}
}
```
Evite `autoApprove` no primeiro uso. Depois de revisar os parâmetros e resultados, ferramentas puramente informativas, como `get_cluster_health`, podem ser aprovadas conforme a política do time.
## Testes
```bash
uv run pytest -q
uv run ruff check .
```
Para abrir no MCP Inspector:
```bash
uv run mcp dev mcp_server.py
```
## Exemplos de prompts no Kiro
```text
Use o profile telemetry em us-east-1 e liste os domínios OpenSearch disponíveis.
```
```text
No domínio logs-production, rode diagnose_cluster e explique apenas achados warning ou superiores.
```
```text
No índice sentinelone-*, verifique o mapping de @timestamp e diagnostique por que os documentos mais novos parecem ser de janeiro.
```
```text
Liste os 20 maiores índices e verifique quais estão próximos de index.mapping.total_fields.limit.
```
```text
Encontre shards não alocados e execute explain_unassigned_shard para o primeiro deles. Não faça alterações.
```
## Privacidade dos dados
As respostas das tools entram no contexto do cliente de IA. Restrinja `source_fields`, evite consultar documentos com dados pessoais desnecessários e use uma identidade com acesso somente aos índices necessários.
## IAM mínimo
O arquivo `examples/iam-policy.example.json` contém uma base. Ajuste conta, domínio e regiões.
A permissão `es:ESHttpPost` aparece porque APIs read-only como `_search` e `_cluster/allocation/explain` usam POST. O MCP não expõe endpoints arbitrários nem operações de escrita, mas a identidade AWS ainda deve seguir privilégio mínimo e, quando disponível, Fine-Grained Access Control do OpenSearch.
## Proteções implementadas
- Allowlist de profile e região.
- Validação de domínio, índice e campo.
- Bloqueio de path injection.
- Limite de documentos, bytes de query e resposta.
- Timeout em consultas.
- Bloqueio de `script`, `script_fields`, `runtime_mappings`, `rescore` e `stored_fields` nas queries fornecidas pelo modelo.
- Redação recursiva de tokens, senhas, cookies, chaves e segredos.
- Paginação e tamanhos internos de agregações limitados.
- Logs enviados para `stderr`, preservando o protocolo MCP em `stdout`.
- Produção e todos os demais profiles permanecem read-only nesta versão.
## Evolução sugerida
Uma segunda versão pode adicionar ferramentas de escrita estritamente específicas, sempre em pares `preview_*` e `apply_*`, com confirmação explícita e bloqueio por profile. Não adicione uma tool de request HTTP arbitrário, pois ela contornaria todas as proteções deste servidor.
## Referências oficiais
- MCP Python SDK: https://github.com/modelcontextprotocol/python-sdk
- Amazon OpenSearch Service, assinatura SigV4: https://docs.aws.amazon.com/opensearch-service/latest/developerguide/managedomains-signing-service-requests.html
- OpenSearch API: https://docs.opensearch.org/latest/api-reference/
- Kiro MCP configuration: https://kiro.dev/docs/mcp/configuration/
TDQS
Scored across 20 tools
Each tool targets a distinct aspect of OpenSearch cluster management and diagnosis, such as health, shards, fields, ingest pipelines, and domain configuration. Even tools with overlapping themes (e.g., diagnose_cluster and explain_unassigned_shard) have clearly different scopes, minimizing confusion.
All tool names follow a consistent verb-noun pattern using snake_case (e.g., diagnose_cluster, get_cluster_health, list_indices). Verbs like 'get', 'list', 'diagnose', 'explain' are used predictably, making it easy for an agent to infer functionality.
With 20 tools, the set is well-scoped for a dedicated OpenSearch diagnostic server. It covers a wide range of inspection and diagnosis tasks without being overwhelming, and each tool serves a clear purpose.
The tool set comprehensively covers read-only diagnostics: cluster health, stats, settings, shard allocation, field mappings, ingest pipelines, and more. Minor gaps exist, such as lack of node-specific details or thread pool stats, but these are acceptable given the read-only and diagnostic focus.