argonaut-mcp-server 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. argonaut_mcp_server-0.1.1/.github/workflows/publish.yml +37 -0
  2. argonaut_mcp_server-0.1.1/.gitignore +15 -0
  3. argonaut_mcp_server-0.1.1/AGENTS.md +34 -0
  4. argonaut_mcp_server-0.1.1/CLAUDE.md +1 -0
  5. argonaut_mcp_server-0.1.1/LICENSE +21 -0
  6. argonaut_mcp_server-0.1.1/PKG-INFO +161 -0
  7. argonaut_mcp_server-0.1.1/README.md +118 -0
  8. argonaut_mcp_server-0.1.1/argonaut/__init__.py +8 -0
  9. argonaut_mcp_server-0.1.1/argonaut/adapters/__init__.py +64 -0
  10. argonaut_mcp_server-0.1.1/argonaut/adapters/aws.py +497 -0
  11. argonaut_mcp_server-0.1.1/argonaut/adapters/base.py +151 -0
  12. argonaut_mcp_server-0.1.1/argonaut/adapters/kafka.py +512 -0
  13. argonaut_mcp_server-0.1.1/argonaut/adapters/rabbitmq.py +354 -0
  14. argonaut_mcp_server-0.1.1/argonaut/adapters/redis_streams.py +370 -0
  15. argonaut_mcp_server-0.1.1/argonaut/config/__init__.py +21 -0
  16. argonaut_mcp_server-0.1.1/argonaut/config/schema.py +209 -0
  17. argonaut_mcp_server-0.1.1/argonaut/core/__init__.py +37 -0
  18. argonaut_mcp_server-0.1.1/argonaut/core/models.py +194 -0
  19. argonaut_mcp_server-0.1.1/argonaut/errors.py +87 -0
  20. argonaut_mcp_server-0.1.1/argonaut/formatter.py +106 -0
  21. argonaut_mcp_server-0.1.1/argonaut/policy/__init__.py +18 -0
  22. argonaut_mcp_server-0.1.1/argonaut/policy/audit.py +56 -0
  23. argonaut_mcp_server-0.1.1/argonaut/policy/confirm.py +64 -0
  24. argonaut_mcp_server-0.1.1/argonaut/policy/engine.py +160 -0
  25. argonaut_mcp_server-0.1.1/argonaut/policy/globs.py +44 -0
  26. argonaut_mcp_server-0.1.1/argonaut/policy/redaction.py +127 -0
  27. argonaut_mcp_server-0.1.1/argonaut/server.py +533 -0
  28. argonaut_mcp_server-0.1.1/argonaut.example.yaml +59 -0
  29. argonaut_mcp_server-0.1.1/pyproject.toml +68 -0
  30. argonaut_mcp_server-0.1.1/tests/__init__.py +0 -0
  31. argonaut_mcp_server-0.1.1/tests/contract/__init__.py +0 -0
  32. argonaut_mcp_server-0.1.1/tests/contract/backend_aws.py +83 -0
  33. argonaut_mcp_server-0.1.1/tests/contract/backends.py +114 -0
  34. argonaut_mcp_server-0.1.1/tests/contract/conftest.py +20 -0
  35. argonaut_mcp_server-0.1.1/tests/contract/test_contract.py +137 -0
  36. argonaut_mcp_server-0.1.1/tests/integration/__init__.py +0 -0
  37. argonaut_mcp_server-0.1.1/tests/integration/test_kafka_integration.py +64 -0
  38. argonaut_mcp_server-0.1.1/tests/unit/__init__.py +0 -0
  39. argonaut_mcp_server-0.1.1/tests/unit/test_aws_adapter.py +121 -0
  40. argonaut_mcp_server-0.1.1/tests/unit/test_config.py +100 -0
  41. argonaut_mcp_server-0.1.1/tests/unit/test_confirm.py +50 -0
  42. argonaut_mcp_server-0.1.1/tests/unit/test_globs.py +24 -0
  43. argonaut_mcp_server-0.1.1/tests/unit/test_kafka_adapter.py +240 -0
  44. argonaut_mcp_server-0.1.1/tests/unit/test_policy_modes.py +14 -0
  45. argonaut_mcp_server-0.1.1/tests/unit/test_rabbitmq_adapter.py +170 -0
  46. argonaut_mcp_server-0.1.1/tests/unit/test_redaction.py +56 -0
  47. argonaut_mcp_server-0.1.1/tests/unit/test_security_fixes.py +159 -0
  48. argonaut_mcp_server-0.1.1/tests/unit/test_server_smoke.py +32 -0
  49. argonaut_mcp_server-0.1.1/uv.lock +1445 -0
@@ -0,0 +1,37 @@
1
+ name: Publish
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+ - uses: astral-sh/setup-uv@v3
14
+ - run: uv sync --all-extras --dev
15
+ - run: uv run pytest
16
+
17
+ publish:
18
+ needs: test
19
+ runs-on: ubuntu-latest
20
+ environment: pypi
21
+ permissions:
22
+ id-token: write
23
+ contents: read
24
+ steps:
25
+ - uses: actions/checkout@v4
26
+ - uses: astral-sh/setup-uv@v3
27
+ - run: uv build
28
+ - name: Confere se a versão do pacote bate com a tag
29
+ run: |
30
+ tag="${GITHUB_REF_NAME#v}"
31
+ wheel=$(ls dist/*.whl | head -n1)
32
+ pkg_version=$(echo "$wheel" | sed -E 's/.*-([0-9][^-]*)-[^-]+-[^-]+-[^-]+\.whl/\1/')
33
+ if [ "$tag" != "$pkg_version" ]; then
34
+ echo "Tag v$tag não bate com a versão do pacote ($pkg_version) — corrija o pyproject.toml antes de criar a tag."
35
+ exit 1
36
+ fi
37
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,15 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .pytest_cache/
6
+ .ruff_cache/
7
+
8
+ # Ambientes e caches locais
9
+ .venv/
10
+ .uv-cache/
11
+
12
+ # Configuração e auditoria locais (contêm segredos / dados)
13
+ *.local.yaml
14
+ argonaut-audit.jsonl
15
+ .env
@@ -0,0 +1,34 @@
1
+ # Argonaut
2
+
3
+ Servidor MCP (stdio) em Python para descobrir, inspecionar e diagnosticar filas e tópicos (Redis Streams, Kafka, RabbitMQ, SQS, SNS). Irmão do Nautilus (bancos de dados).
4
+
5
+ > Projeto novo: o esqueleto de código ainda não existe. Os comandos abaixo refletem a stack decidida (Python + uv) e passam a valer quando a primeira mudança criar o `pyproject.toml`.
6
+
7
+ <!-- plumb:start -->
8
+ ## Comandos
9
+
10
+ - Instalar dependências: `uv sync`
11
+ - Suíte de testes: `uv run pytest`
12
+ - Um único teste: `uv run pytest tests/path/test_x.py::test_nome`
13
+ - Testes de integração (brokers reais): `uv run pytest -m integration` (Testcontainers / LocalStack)
14
+ - Rodar o servidor local (stdio): `uv run argonaut`
15
+
16
+ Convenções do Plumb: commits `tipo(escopo): descrição em pt-BR`, branches `feature/<nome>` a partir de `master`; commit por tarefa: sim. Mensagens ao usuário e erros em pt-BR; logging sempre em `stderr` (stdout é o canal do MCP stdio).
17
+
18
+ Worktree: rodar `uv sync` no worktree novo; sem porta, banco ou serviço a ajustar (conexões vêm de variáveis de ambiente).
19
+
20
+ ## Workflow
21
+ Todo pedido segue o Plumb (skill `plumb`): pergunta responde direto depois de
22
+ consultar o segundo cérebro; correção pequena vai pela trilha direta; mudança
23
+ padrão ou profunda começa com o grill (todas as perguntas de uma vez, com a
24
+ recomendação primeiro, sempre pela ferramenta de perguntas), vira uma spec (objetivo,
25
+ resultados esperados observáveis, fases com critério de saída) aprovada antes
26
+ de codar — com o segundo cérebro, guarde-a como item `spec/<id>` — e roda em um
27
+ worktree próprio, um por mudança. Escreva os testes primeiro; peça confirmação
28
+ antes de push, PR ou merge. Em qualquer mudança, até um typo: rode os testes e o
29
+ lint afetados e reporte a evidência — nunca diga "pronto" sem isso. Falta
30
+ documentação atual de uma biblioteca: `plumb-find-docs`; falta acesso a um
31
+ sistema: `plumb-find-mcps`; falta uma competência: `plumb-find-skills` — a
32
+ recomendação vai ao usuário e nada se instala sem o "sim". O que durar (regra,
33
+ decisão, procedimento) vai para o cérebro, não para arquivos.
34
+ <!-- plumb:end -->
@@ -0,0 +1 @@
1
+ @AGENTS.md
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 KauaLealz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,161 @@
1
+ Metadata-Version: 2.5
2
+ Name: argonaut-mcp-server
3
+ Version: 0.1.1
4
+ Summary: Servidor MCP para descobrir, inspecionar e diagnosticar filas e tópicos (Redis Streams, Kafka, RabbitMQ, SQS, SNS).
5
+ Author: KauaLealz
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Keywords: diagnostics,kafka,mcp,message-queue,model-context-protocol,observability,rabbitmq,redis-streams,sns,sqs
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Intended Audience :: System Administrators
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Topic :: System :: Monitoring
17
+ Classifier: Topic :: System :: Systems Administration
18
+ Requires-Python: >=3.11
19
+ Requires-Dist: mcp<2,>=1.6
20
+ Requires-Dist: pydantic>=2.0
21
+ Requires-Dist: pyyaml>=6.0
22
+ Provides-Extra: all
23
+ Requires-Dist: boto3>=1.34; extra == 'all'
24
+ Requires-Dist: confluent-kafka>=2.3; extra == 'all'
25
+ Requires-Dist: redis>=5.0; extra == 'all'
26
+ Requires-Dist: requests>=2.31; extra == 'all'
27
+ Provides-Extra: aws
28
+ Requires-Dist: boto3>=1.34; extra == 'aws'
29
+ Provides-Extra: dev
30
+ Requires-Dist: boto3>=1.34; extra == 'dev'
31
+ Requires-Dist: fakeredis>=2.21; extra == 'dev'
32
+ Requires-Dist: moto[cloudwatch,sns,sqs]>=5.0; extra == 'dev'
33
+ Requires-Dist: pytest>=8.0; extra == 'dev'
34
+ Requires-Dist: redis>=5.0; extra == 'dev'
35
+ Requires-Dist: requests>=2.31; extra == 'dev'
36
+ Provides-Extra: kafka
37
+ Requires-Dist: confluent-kafka>=2.3; extra == 'kafka'
38
+ Provides-Extra: rabbitmq
39
+ Requires-Dist: requests>=2.31; extra == 'rabbitmq'
40
+ Provides-Extra: redis
41
+ Requires-Dist: redis>=5.0; extra == 'redis'
42
+ Description-Content-Type: text/markdown
43
+
44
+ # Argonaut
45
+
46
+ Servidor **MCP (stdio)** em Python que deixa um agente **descobrir, inspecionar e
47
+ diagnosticar** filas e tópicos em **Redis Streams, Kafka, RabbitMQ, Amazon SQS e
48
+ Amazon SNS**. As operações de escrita existem para resolver o problema encontrado,
49
+ sempre atrás de modo, allowlist e confirmação em duas etapas.
50
+
51
+ Faz par com o [Nautilus](https://github.com/KauaLealz/nautilus-mcp-server) (MCP de
52
+ bancos de dados): Nautilus para bases, Argonaut para mensageria.
53
+
54
+ ## O que faz
55
+
56
+ O caso de uso central é diagnóstico: *"por que essa fila está crescendo?"*,
57
+ *"qual consumer group está atrasado?"*, *"o que tem na DLQ?"*, *"por que a mensagem
58
+ publicada no SNS não chegou na fila X?"*.
59
+
60
+ ## Instalação e execução
61
+
62
+ Requer **Python 3.11+**. Dependências com [uv](https://docs.astral.sh/uv/).
63
+
64
+ ```bash
65
+ uv sync --extra all # ou --extra redis / kafka / rabbitmq / aws
66
+ ARGONAUT_CONFIG=./argonaut.yaml uv run argonaut
67
+ ```
68
+
69
+ O servidor fala por **stdio**. Configure-o no seu cliente MCP apontando o comando
70
+ para `uv run argonaut` (ou o console script `argonaut`) com a variável de ambiente
71
+ `ARGONAUT_CONFIG` e os segredos que o YAML referencia.
72
+
73
+ ## Configuração
74
+
75
+ Um único arquivo YAML descreve defaults e conexões. Segredos **sempre** por
76
+ `${VAR}` (resolvidos do ambiente) — nunca valores no arquivo. Veja
77
+ [`argonaut.example.yaml`](./argonaut.example.yaml). O servidor valida na
78
+ inicialização e falha cedo, com mensagens claras.
79
+
80
+ ```yaml
81
+ defaults:
82
+ peek: { max_messages: 20, max_payload_bytes: 4096 }
83
+ confirm_token_ttl: 120s
84
+ audit_log: ./argonaut-audit.jsonl
85
+ redact:
86
+ json_paths: ["$.customer.document", "$.card.*"]
87
+ patterns: [cpf, email, credit_card]
88
+
89
+ connections:
90
+ redis-dev:
91
+ type: redis_streams
92
+ url: "${REDIS_URL}"
93
+ mode: admin
94
+ dlq_convention: "{name}:dlq"
95
+ ```
96
+
97
+ Tipos de conexão: `redis_streams`, `kafka`, `rabbitmq`, `aws` (SQS + SNS juntos).
98
+
99
+ ## Tools
100
+
101
+ | Tool | Modo | O que faz |
102
+ |------|------|-----------|
103
+ | `list_connections` | readonly | Conexões configuradas, com tipo e modo |
104
+ | `list_destinations` | readonly | Filas, tópicos e streams (filtro por glob) |
105
+ | `describe_destination` | readonly | Atributos nativos, DLQ e configuração |
106
+ | `get_stats` | readonly | Profundidade, em voo, idade da mais antiga, taxas |
107
+ | `list_consumer_groups` | readonly | Grupos, membros e estado |
108
+ | `get_consumer_lag` | readonly | Lag por grupo e partição |
109
+ | `get_topology` | readonly | Bindings do RabbitMQ e subscriptions do SNS |
110
+ | `peek_messages` | readonly¹ | Leitura sem consumir |
111
+ | `get_message` | readonly | Uma mensagem por posição (offset, id) |
112
+ | `publish_message` | operator | Publica uma mensagem |
113
+ | `redrive_dlq` | operator | Move mensagens da DLQ de volta à origem |
114
+ | `reset_offsets` | operator | Reposiciona um consumer group |
115
+ | `reclaim_pending` | operator | Reatribui mensagens pendentes travadas (Redis) |
116
+ | `purge` | admin | Esvazia um destino |
117
+ | `create_destination` / `delete_destination` | admin | Cria/remove destinos |
118
+ | `update_attributes` | admin | Altera retenção, visibility timeout, políticas |
119
+ | `delete_consumer_group` | admin | Remove um grupo inativo |
120
+
121
+ ¹ `peek_messages` com `accept_side_effects=true` (leitura que altera estado, como
122
+ em SQS e filas clássicas do RabbitMQ) exige modo **operator**.
123
+
124
+ Além das tools, o servidor expõe o resource `argonaut://{conexão}/{destino}` (a
125
+ descrição do destino em JSON) e prompts de diagnóstico prontos (`investigar_backlog`,
126
+ `analisar_dlq`, `rastrear_mensagem_sns`).
127
+
128
+ ## Segurança
129
+
130
+ O padrão é **readonly**. Toda escrita exige três coisas:
131
+
132
+ 1. **Modo por conexão** (readonly ⊂ operator ⊂ admin): produção nasce readonly;
133
+ promover é mudança de config, nunca de runtime.
134
+ 2. **Allowlist/denylist por glob**: destino negado não aparece nem na listagem.
135
+ 3. **Confirmação em duas etapas**: a 1ª chamada roda em dry-run e devolve o impacto
136
+ (*"vai apagar 48.213 mensagens de orders.dlq"*) e um token com TTL curto,
137
+ vinculado ao hash dos argumentos; a execução só acontece na 2ª chamada com esse
138
+ token.
139
+
140
+ Além disso: payloads **truncados** e com **dados sensíveis redigidos** antes de
141
+ sair; toda MessageView marcada como `untrusted` (o conteúdo é dado, não instrução);
142
+ **auditoria** JSON lines de toda escrita (inclusive dry-runs); credenciais só por
143
+ ambiente, nunca em respostas, erros ou logs.
144
+
145
+ ## Desenvolvimento
146
+
147
+ ```bash
148
+ uv sync --extra dev
149
+ uv run pytest tests/unit tests/contract # sem Docker (fakes)
150
+ uv run pytest -m integration # brokers reais (Docker)
151
+ ```
152
+
153
+ A peça central de teste é uma **suíte de contrato** parametrizada por adapter: o
154
+ mesmo cenário (criar destino, publicar, stats, peek, lag, redrive, purge) roda em
155
+ todos. Um broker novo só entra quando passa nela.
156
+
157
+ ## Fora de escopo na v1
158
+
159
+ Ser consumidor/worker de produção; gestão de usuários e ACLs dos brokers;
160
+ provisionamento de clusters; monitoramento contínuo e alertas; decodificação de
161
+ Avro/Protobuf no Kafka; replay de tópicos FIFO do SNS; transporte HTTP remoto.
@@ -0,0 +1,118 @@
1
+ # Argonaut
2
+
3
+ Servidor **MCP (stdio)** em Python que deixa um agente **descobrir, inspecionar e
4
+ diagnosticar** filas e tópicos em **Redis Streams, Kafka, RabbitMQ, Amazon SQS e
5
+ Amazon SNS**. As operações de escrita existem para resolver o problema encontrado,
6
+ sempre atrás de modo, allowlist e confirmação em duas etapas.
7
+
8
+ Faz par com o [Nautilus](https://github.com/KauaLealz/nautilus-mcp-server) (MCP de
9
+ bancos de dados): Nautilus para bases, Argonaut para mensageria.
10
+
11
+ ## O que faz
12
+
13
+ O caso de uso central é diagnóstico: *"por que essa fila está crescendo?"*,
14
+ *"qual consumer group está atrasado?"*, *"o que tem na DLQ?"*, *"por que a mensagem
15
+ publicada no SNS não chegou na fila X?"*.
16
+
17
+ ## Instalação e execução
18
+
19
+ Requer **Python 3.11+**. Dependências com [uv](https://docs.astral.sh/uv/).
20
+
21
+ ```bash
22
+ uv sync --extra all # ou --extra redis / kafka / rabbitmq / aws
23
+ ARGONAUT_CONFIG=./argonaut.yaml uv run argonaut
24
+ ```
25
+
26
+ O servidor fala por **stdio**. Configure-o no seu cliente MCP apontando o comando
27
+ para `uv run argonaut` (ou o console script `argonaut`) com a variável de ambiente
28
+ `ARGONAUT_CONFIG` e os segredos que o YAML referencia.
29
+
30
+ ## Configuração
31
+
32
+ Um único arquivo YAML descreve defaults e conexões. Segredos **sempre** por
33
+ `${VAR}` (resolvidos do ambiente) — nunca valores no arquivo. Veja
34
+ [`argonaut.example.yaml`](./argonaut.example.yaml). O servidor valida na
35
+ inicialização e falha cedo, com mensagens claras.
36
+
37
+ ```yaml
38
+ defaults:
39
+ peek: { max_messages: 20, max_payload_bytes: 4096 }
40
+ confirm_token_ttl: 120s
41
+ audit_log: ./argonaut-audit.jsonl
42
+ redact:
43
+ json_paths: ["$.customer.document", "$.card.*"]
44
+ patterns: [cpf, email, credit_card]
45
+
46
+ connections:
47
+ redis-dev:
48
+ type: redis_streams
49
+ url: "${REDIS_URL}"
50
+ mode: admin
51
+ dlq_convention: "{name}:dlq"
52
+ ```
53
+
54
+ Tipos de conexão: `redis_streams`, `kafka`, `rabbitmq`, `aws` (SQS + SNS juntos).
55
+
56
+ ## Tools
57
+
58
+ | Tool | Modo | O que faz |
59
+ |------|------|-----------|
60
+ | `list_connections` | readonly | Conexões configuradas, com tipo e modo |
61
+ | `list_destinations` | readonly | Filas, tópicos e streams (filtro por glob) |
62
+ | `describe_destination` | readonly | Atributos nativos, DLQ e configuração |
63
+ | `get_stats` | readonly | Profundidade, em voo, idade da mais antiga, taxas |
64
+ | `list_consumer_groups` | readonly | Grupos, membros e estado |
65
+ | `get_consumer_lag` | readonly | Lag por grupo e partição |
66
+ | `get_topology` | readonly | Bindings do RabbitMQ e subscriptions do SNS |
67
+ | `peek_messages` | readonly¹ | Leitura sem consumir |
68
+ | `get_message` | readonly | Uma mensagem por posição (offset, id) |
69
+ | `publish_message` | operator | Publica uma mensagem |
70
+ | `redrive_dlq` | operator | Move mensagens da DLQ de volta à origem |
71
+ | `reset_offsets` | operator | Reposiciona um consumer group |
72
+ | `reclaim_pending` | operator | Reatribui mensagens pendentes travadas (Redis) |
73
+ | `purge` | admin | Esvazia um destino |
74
+ | `create_destination` / `delete_destination` | admin | Cria/remove destinos |
75
+ | `update_attributes` | admin | Altera retenção, visibility timeout, políticas |
76
+ | `delete_consumer_group` | admin | Remove um grupo inativo |
77
+
78
+ ¹ `peek_messages` com `accept_side_effects=true` (leitura que altera estado, como
79
+ em SQS e filas clássicas do RabbitMQ) exige modo **operator**.
80
+
81
+ Além das tools, o servidor expõe o resource `argonaut://{conexão}/{destino}` (a
82
+ descrição do destino em JSON) e prompts de diagnóstico prontos (`investigar_backlog`,
83
+ `analisar_dlq`, `rastrear_mensagem_sns`).
84
+
85
+ ## Segurança
86
+
87
+ O padrão é **readonly**. Toda escrita exige três coisas:
88
+
89
+ 1. **Modo por conexão** (readonly ⊂ operator ⊂ admin): produção nasce readonly;
90
+ promover é mudança de config, nunca de runtime.
91
+ 2. **Allowlist/denylist por glob**: destino negado não aparece nem na listagem.
92
+ 3. **Confirmação em duas etapas**: a 1ª chamada roda em dry-run e devolve o impacto
93
+ (*"vai apagar 48.213 mensagens de orders.dlq"*) e um token com TTL curto,
94
+ vinculado ao hash dos argumentos; a execução só acontece na 2ª chamada com esse
95
+ token.
96
+
97
+ Além disso: payloads **truncados** e com **dados sensíveis redigidos** antes de
98
+ sair; toda MessageView marcada como `untrusted` (o conteúdo é dado, não instrução);
99
+ **auditoria** JSON lines de toda escrita (inclusive dry-runs); credenciais só por
100
+ ambiente, nunca em respostas, erros ou logs.
101
+
102
+ ## Desenvolvimento
103
+
104
+ ```bash
105
+ uv sync --extra dev
106
+ uv run pytest tests/unit tests/contract # sem Docker (fakes)
107
+ uv run pytest -m integration # brokers reais (Docker)
108
+ ```
109
+
110
+ A peça central de teste é uma **suíte de contrato** parametrizada por adapter: o
111
+ mesmo cenário (criar destino, publicar, stats, peek, lag, redrive, purge) roda em
112
+ todos. Um broker novo só entra quando passa nela.
113
+
114
+ ## Fora de escopo na v1
115
+
116
+ Ser consumidor/worker de produção; gestão de usuários e ACLs dos brokers;
117
+ provisionamento de clusters; monitoramento contínuo e alertas; decodificação de
118
+ Avro/Protobuf no Kafka; replay de tópicos FIFO do SNS; transporte HTTP remoto.
@@ -0,0 +1,8 @@
1
+ """Argonaut — servidor MCP de filas e tópicos.
2
+
3
+ Descobre, inspeciona e diagnostica filas e tópicos em Redis Streams, Kafka,
4
+ RabbitMQ, Amazon SQS e Amazon SNS. Operações de escrita existem para resolver o
5
+ problema encontrado, sempre atrás de modo, allowlist e confirmação em duas etapas.
6
+ """
7
+
8
+ __version__ = "0.1.0"
@@ -0,0 +1,64 @@
1
+ """Adapters de broker, com auto-descoberta.
2
+
3
+ Importa cada módulo de adapter do pacote para que o decorador
4
+ ``@register_adapter`` registre a classe. Um módulo que falhe ao importar (por
5
+ exemplo, dependência opcional ausente) é pulado com aviso, sem derrubar os
6
+ demais.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import importlib
12
+ import logging
13
+ import pkgutil
14
+
15
+ from argonaut.adapters.base import (
16
+ BrokerAdapter,
17
+ get_adapter_class,
18
+ register_adapter,
19
+ registered_types,
20
+ )
21
+ from argonaut.config.schema import Config
22
+
23
+ logger = logging.getLogger("argonaut")
24
+
25
+ _SKIP = {"base"}
26
+
27
+
28
+ def _discover() -> None:
29
+ for info in pkgutil.iter_modules(__path__):
30
+ if info.name in _SKIP:
31
+ continue
32
+ try:
33
+ importlib.import_module(f"{__name__}.{info.name}")
34
+ except Exception as exc: # pragma: no cover - resiliência de import
35
+ logger.warning("Adapter '%s' não pôde ser carregado: %s", info.name, exc)
36
+
37
+
38
+ _discover()
39
+
40
+
41
+ def build_adapters(config: Config) -> dict[str, BrokerAdapter]:
42
+ """Instancia um adapter por conexão. Não conecta ao broker (conexão é preguiçosa)."""
43
+ adapters: dict[str, BrokerAdapter] = {}
44
+ for cid, conn in config.connections.items():
45
+ cls = get_adapter_class(conn.type)
46
+ if cls is None:
47
+ logger.warning(
48
+ "Conexão '%s': nenhum adapter registrado para o tipo '%s' "
49
+ "(dependência do broker instalada?).",
50
+ cid,
51
+ conn.type,
52
+ )
53
+ continue
54
+ adapters[cid] = cls(conn)
55
+ return adapters
56
+
57
+
58
+ __all__ = [
59
+ "BrokerAdapter",
60
+ "get_adapter_class",
61
+ "register_adapter",
62
+ "registered_types",
63
+ "build_adapters",
64
+ ]