Skip to main content
Glama
lucaspmgomess

querido-diario-mcp-server

Querido Diário MCP Server

Consulte diários oficiais municipais brasileiros diretamente pelo Claude, Cursor, Codex e outros clientes compatíveis com MCP.

PyPI Python CI MCP Registry License: MIT

Sem chave de API · Executa localmente · Somente leitura · Sem telemetria · Código aberto

🌎 English version: README.en.md

O Querido Diário MCP Server conecta agentes de IA à API pública do Querido Diário, permitindo consultar diários oficiais municipais brasileiros por meio de ferramentas estruturadas do Model Context Protocol (MCP).

Exemplo de uso:
"Encontre todas as menções a inteligência artificial nos diários oficiais de Porto Alegre em 2026."

O agente pode identificar o município correto, resolver seu código IBGE, consultar o índice de diários oficiais e devolver resultados estruturados sem que o usuário precise conhecer a API.


Por que este projeto existe?

O Querido Diário, mantido pela Open Knowledge Brasil, torna diários oficiais municipais brasileiros pesquisáveis por meio de uma plataforma de dados abertos e de uma API pública.

Este projeto adiciona uma interface nativa de MCP sobre essa API, permitindo que clientes e agentes de IA utilizem os dados diretamente como ferramentas.

Sem o MCP, um fluxo típico exigiria:

  1. descobrir o município correto;

  2. obter o código IBGE correspondente;

  3. conhecer a API do Querido Diário;

  4. montar os parâmetros de busca;

  5. interpretar manualmente a resposta.

Com este servidor, um agente compatível com MCP pode executar esse fluxo de forma estruturada.

O servidor roda localmente como um subprocesso e realiza apenas requisições HTTPS de leitura para a API pública do Querido Diário.


Related MCP server: Prefeitura PE Recife: CPOM

O que dá para fazer?

Licitações, compras públicas e contratos

Pesquise empresas, processos licitatórios, contratos, termos de contratação e referências a compras governamentais.

"Encontre menções à ACME Ltda nos diários oficiais de Porto Alegre entre janeiro e julho de 2026."

Pessoas e organizações

Acompanhe menções a pessoas, empresas, associações, órgãos públicos e outras organizações.

"Pesquise João da Silva nos diários oficiais de Torres, RS."

Leis, decretos e atos administrativos

Pesquise legislação municipal, decretos, nomeações, exonerações, atos administrativos e mudanças regulatórias.

"Encontre publicações relacionadas à regulamentação de inteligência artificial."

Jornalismo de dados e pesquisa cívica

Use o Querido Diário como fonte estruturada em fluxos de pesquisa assistidos por IA.

"Busque contratos públicos relacionados a reconhecimento facial nos diários oficiais de Porto Alegre."

Agentes e automações

Combine a busca em diários oficiais com outros servidores MCP para criar fluxos maiores de investigação, classificação, acompanhamento e análise de informações públicas.


Início rápido

Requisitos:

  • Python 3.12+

  • uv, que fornece o comando uvx

Não é necessário clonar o repositório.

uvx querido-diario-mcp-server

Esse comando inicia o servidor MCP via stdio.

O servidor não possui interface interativa no terminal por design: ele foi feito para ser iniciado por um cliente MCP.


Conectando ao seu cliente de IA

Todos os clientes abaixo usam o mesmo comando:

uvx querido-diario-mcp-server

Claude Desktop / Claude Code

Adicione ao claude_desktop_config.json no Claude Desktop ou ao .mcp.json do projeto no Claude Code:

{
  "mcpServers": {
    "querido-diario": {
      "command": "uvx",
      "args": ["querido-diario-mcp-server"]
    }
  }
}

Cursor

Adicione ao .cursor/mcp.json do projeto ou às configurações globais de MCP do Cursor:

{
  "mcpServers": {
    "querido-diario": {
      "command": "uvx",
      "args": ["querido-diario-mcp-server"]
    }
  }
}

Codex CLI

Adicione ao arquivo ~/.codex/config.toml:

[mcp_servers.querido-diario]
command = "uvx"
args = ["querido-diario-mcp-server"]

Outros clientes MCP

Qualquer cliente compatível com servidores MCP locais via stdio pode utilizar:

  • comando: uvx

  • argumento: querido-diario-mcp-server

Consulte a documentação do seu cliente para o formato exato da configuração.


Ferramentas disponíveis

O servidor expõe propositalmente uma superfície pequena e somente leitura.

Ferramenta

Finalidade

search_cities

Busca municípios brasileiros por nome parcial e resolve o código IBGE de 7 dígitos. Permite filtro opcional por estado.

get_city

Consulta os detalhes de um município usando seu código IBGE exato de 7 dígitos.

search_gazettes

Realiza busca textual em diários oficiais indexados, com filtros por município, período, paginação e ordenação.

Sintaxe de busca

A ferramenta search_gazettes utiliza a sintaxe simple query string do OpenSearch usada pela API do Querido Diário.

Exemplos:

Consulta

Significado

inteligência artificial

Encontra qualquer um dos termos

+inteligência +artificial

Exige os dois termos

-cancelado

Exclui um termo

"João da Silva"

Busca uma expressão exata


Exemplo completo

O usuário pergunta:

Encontre menções à ACME Ltda nos diários oficiais de Porto Alegre
entre janeiro e julho de 2026.

O cliente MCP pode executar:

1. search_cities(city_name="Porto Alegre")
   → territory_id: "4314902"

2. search_gazettes(
       query='"ACME Ltda"',
       territory_ids=["4314902"],
       published_since="2026-01-01",
       published_until="2026-07-31",
   )

O agente recebe os resultados de forma estruturada e pode então resumir, comparar, classificar ou combinar essas informações com outras ferramentas.

Outros exemplos de prompts:

Qual é o registro do município de Torres, RS, no Querido Diário?
Pesquise referências a compras públicas de inteligência artificial
nos diários oficiais de Porto Alegre.
Encontre publicações mencionando uma determinada empresa durante 2025.
Consulte o município correspondente ao código IBGE 3550308.

Demonstração

Ainda não há um vídeo, GIF ou captura de tela desta seção — de propósito, para não sugerir um comportamento que não foi validado de fato. Assim que houver uma demonstração real do servidor rodando dentro do Claude ou do Cursor, ela será adicionada aqui.


Como funciona

Cliente de IA
   │
   │ MCP / stdio
   ▼
querido-diario-mcp-server
   │
   │ requisições HTTPS tipadas
   ▼
API pública do Querido Diário

Estrutura do projeto:

src/querido_diario_mcp_server/
    __init__.py   # versão do pacote + ponto de entrada
    config.py     # configuração por variáveis de ambiente
    errors.py     # hierarquia de erros da integração
    models.py     # modelos Pydantic tipados
    client.py     # cliente HTTP assíncrono da API
    server.py     # camada MCP e definição das ferramentas

A implementação separa propositalmente a integração HTTP da camada MCP:

  • client.py não depende do MCP;

  • server.py concentra validação, ferramentas e comportamento de protocolo;

  • um único httpx.AsyncClient é criado no ciclo de vida do servidor e reutilizado;

  • erros da API são convertidos em mensagens MCP curtas e compreensíveis para o agente.


Local-first e somente leitura

O projeto foi desenhado para ser conservador em relação ao que um agente pode fazer.

  • Executa somente requisições GET.

  • Não possui operações de escrita.

  • Não exige conta.

  • Não exige chave de API.

  • Não coleta telemetria.

  • Não utiliza backend proprietário.

  • Não utiliza proxy hospedado.

  • O agente não pode fornecer uma URL arbitrária para o servidor buscar.

  • URLs de diários retornadas pela API não são abertas automaticamente.

  • Código IBGE, datas, paginação e ordenação são validados.

  • Respostas de erro HTML da API não são repassadas integralmente ao agente.

Isso reduz a superfície de risco e evita transformar o servidor MCP em um mecanismo genérico de requisições externas ou SSRF.

Fora de escopo nesta fase

A versão atual não implementa:

  • busca arbitrária de URLs;

  • download automático de PDF ou texto integral;

  • OCR;

  • operações de escrita;

  • banco de dados local;

  • crawling;

  • jobs em segundo plano;

  • sumarização por LLM embutida;

  • interface web.

O objetivo é manter uma camada MCP pequena, previsível e segura sobre a API pública existente.


Configuração

Variável

Padrão

Finalidade

QD_API_BASE_URL

https://api.queridodiario.org.br

URL base da API do Querido Diário. Pode ser sobrescrita para ambientes locais ou de staging.

Para uso normal, nenhuma configuração adicional é necessária.


Instalação e distribuição

O pacote está publicado em:

Nome no PyPI:

querido-diario-mcp-server

Nome no MCP Registry:

io.github.lucaspmgomess/querido-diario-mcp-server

Desenvolvimento

Clone o repositório apenas se quiser contribuir ou trabalhar na implementação:

git clone https://github.com/lucaspmgomess/querido-diario-mcp-server.git
cd querido-diario-mcp-server
uv sync

Execute os mesmos checks usados pelo CI:

uv run ruff check .
uv run ruff format --check .
uv run pyright
uv run pytest --cov

Para executar o servidor a partir do checkout local:

uv run querido-diario-mcp-server

Para inspecionar as ferramentas MCP interativamente:

uv run mcp dev src/querido_diario_mcp_server/server.py:mcp

Estratégia de testes

Testes do cliente HTTP

O client.py é testado com httpx.MockTransport, portanto a suíte automatizada não depende de internet nem de uma instância ativa do Querido Diário.

A cobertura inclui:

  • requisições bem-sucedidas;

  • busca de municípios;

  • serialização de parâmetros;

  • múltiplos territory_ids;

  • períodos;

  • paginação;

  • ordenação;

  • resultados vazios;

  • erros 400/404/422;

  • erros 5xx;

  • respostas malformadas;

  • timeouts;

  • falhas de conexão.

Testes de integração MCP

Os testes de integração executam o servidor MCP real por meio do cliente in-process do SDK.

Eles verificam:

  • descoberta das ferramentas;

  • schemas de entrada;

  • saída estruturada e tipada;

  • falhas de validação;

  • falhas da integração upstream;

  • conversão de erros em mensagens MCP limpas, sem traceback Python bruto.

Smoke tests manuais

Há dois scripts de verificação manual contra produção:

uv run python scripts/smoke_test_api.py
uv run python scripts/smoke_test_mcp.py

Eles podem ser usados para validar a API real e o caminho completo MCP → cliente HTTP → API.


Relação com o Querido Diário

Este é um projeto comunitário e não oficial.

O Querido Diário é mantido pela Open Knowledge Brasil e sua comunidade.

Este repositório:

  • não é um projeto oficial da Open Knowledge Brasil, salvo eventual adoção expressa pela organização;

  • não é afiliado, endossado ou mantido pela Open Knowledge Brasil;

  • não copia nem distribui a implementação do Querido Diário;

  • apenas consulta a API pública do projeto.

Repositórios upstream relevantes:

A API de produção utilizada por padrão é:

https://api.queridodiario.org.br

Notas adicionais sobre o histórico dos endpoints estão em:

docs/upstream-api-history.md


Por que código aberto?

Existem integrações hospedadas que expõem dados do Querido Diário para clientes MCP.

Este projeto segue uma abordagem diferente:

  • a implementação do servidor é pública;

  • o servidor roda na máquina do próprio usuário;

  • não há middleware hospedado;

  • não há conta;

  • não há chave de API;

  • não há telemetria;

  • a API pública do Querido Diário é acessada diretamente.

Assim, todo o caminho entre o agente e a fonte de dados pode ser inspecionado.


Feedback e uso real

Se você utilizar este projeto em pesquisa, civic tech, jornalismo de dados, análise de compras públicas ou em algum fluxo com agentes de IA, seu feedback é especialmente útil.

Exemplos de feedback que ajudam:

  • uma busca difícil de expressar;

  • um caso de município que não funcionou como esperado;

  • dificuldade de configuração em algum cliente MCP;

  • um filtro que faria diferença no uso real;

  • comportamento inesperado da API upstream;

  • um exemplo de como você está utilizando o servidor.

Abra uma issue descrevendo o caso de uso ou problema encontrado.

O objetivo é evoluir o projeto com base em uso real, mantendo o servidor pequeno, seguro e somente leitura.


Contribuindo

Issues e pull requests são bem-vindos.

Antes de abrir uma PR, execute:

uv run ruff check .
uv run ruff format --check .
uv run pyright
uv run pytest --cov

Mantenha novas ferramentas e comportamentos alinhados ao objetivo do projeto: oferecer a agentes compatíveis com MCP acesso seguro e estruturado à API pública do Querido Diário.


Licença

MIT

Available Tools

3 tools
get_cityA

Retrieve one Brazilian municipality by its 7-digit IBGE territory ID.

Use this when the exact territory_id is already known (from `search_cities`, or
supplied directly) and you need that city's details: name, state, Querido Diário
availability date, and known official publication URLs. If you only have a city
name, call `search_cities` instead.
ParametersJSON Schema
NameRequiredDescriptionDefault
territory_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
levelNo
state_codeYes
territory_idYes
territory_nameYes
publication_urlsNo
availability_dateNo

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the burden. 'Retrieve' signals a read-only lookup, and the description discloses what the result contains: name, state, availability date, and official publication URLs. It does not discuss not-found behavior, but for a simple single-ID lookup this is acceptable.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is short, front-loaded with the primary action, and every sentence is informative. The usage guidance and alternative routing are placed after the purpose without padding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool is simple: one required parameter, no nested objects, an output schema exists, and the description names the only two sibling tools. It covers what the tool returns, when to use it, and when not to use it. Nothing essential is missing for an agent to call it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema only lists territory_id as a plain string with no description. The description compensates by explaining it is a 7-digit IBGE territory ID and that it typically comes from search_cities. This gives the agent enough semantic grounding to select and format the parameter.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: 'Retrieve one Brazilian municipality by its 7-digit IBGE territory ID.' The phrase 'one' and the ID-based lookup clearly distinguish it from the fuzzy/name-based sibling search_cities.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says when to use this tool: when the exact territory_id is already known. It also names the alternative and its trigger: 'If you only have a city name, call search_cities instead.' This is direct routing guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_citiesA

Find Brazilian municipalities indexed by Querido Diário and resolve their 7-digit IBGE territory IDs.

Call this FIRST whenever a request names a city but you don't already have its
numeric territory ID — both `get_city` and `search_gazettes` require that ID, not
a city name. For example, to answer "find gazettes about ACME in Porto Alegre",
first call search_cities(city_name="Porto Alegre") to get its territory_id, then
pass that ID to search_gazettes.

Matching is by partial, case-insensitive name similarity (as implemented by the
upstream API), not exact string equality, so it tolerates minor spelling
variation. `state_code` is an optional two-letter Brazilian state/UF filter
(e.g. "RS", "SP") applied after the upstream search, useful for disambiguating
cities that share a name across different states.
ParametersJSON Schema
NameRequiredDescriptionDefault
city_nameYes
state_codeNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
citiesYes

TDQS

A4.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden of behavioral disclosure. It clearly discloses the matching semantics (partial, case-insensitive similarity rather than exact equality) and the post-filtering behavior of state_code. It does not go into return shape or pagination, but an output schema exists and the core behavior is well explained.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core purpose, immediately followed by usage context and parameter semantics. Every paragraph earns its place, including the concrete example that clarifies the intended call flow without excessive verbosity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 2-parameter search tool with no annotations, the description covers purpose, when to call it, how to call it, parameter behavior, and the relationship to sibling tools. An output schema exists, so the lack of return-format detail is acceptable. Nothing essential for correct tool invocation is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description fully compensates. It explains city_name as a partial, case-insensitive name query and state_code as an optional two-letter Brazilian state/UF filter applied after the upstream search, including examples like 'RS' and 'SP' and its disambiguation purpose.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb ('Find') and a concrete resource ('Brazilian municipalities indexed by Querido Diário'), and explains the tool's unique role: resolving 7-digit IBGE territory IDs. It explicitly differentiates from siblings by noting that both get_city and search_gazettes require the territory ID that this tool provides.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit when-to-use guidance: 'Call this FIRST whenever a request names a city but you don't already have its numeric territory ID.' It names the sibling tools and gives a concrete example workflow, effectively telling the agent when to use this tool versus alternatives and what to do after.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_gazettesA

Search the text of published Brazilian municipal official gazettes.

Each result is one gazette (one city's official publication, one edition, one
date) with a short excerpt showing where `query` matched. This searches gazette
*content*, not city metadata — resolve a city name to its territory_id with
`search_cities` first.

`query` uses OpenSearch's "simple query string syntax": bare words are OR'd
together, a leading `+` requires a term, a leading `-` excludes it, and double
quotes match an exact phrase — e.g. '"João da Silva"' for an exact name, or
'+licitação +pregão' to require both terms together. An empty query returns
matching gazettes' metadata without excerpts.

`territory_ids` restricts the search to specific cities by their 7-digit IBGE ID
(from `search_cities` or `get_city`); omit it to search across all available
cities. `published_since` / `published_until` are inclusive ISO dates
(YYYY-MM-DD) bounding the gazette's publication date — leave both unset to search
the full available history. `size` is capped at 50 results per call; use `offset`
to page through more. `sort_by` defaults to relevance; use "descending_date" or
"ascending_date" to sort chronologically instead.

Example: to find mentions of "Empresa X" in Porto Alegre gazettes published
between January and July 2026, once territory_id "4314902" is known, call
search_gazettes(query='"Empresa X"', territory_ids=["4314902"],
published_since="2026-01-01", published_until="2026-07-31").
ParametersJSON Schema
NameRequiredDescriptionDefault
sizeNo
queryNo
offsetNo
sort_byNorelevance
territory_idsNo
published_sinceNo
published_untilNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
gazettesYes
total_gazettesYes

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden and does so thoroughly: each result is one gazette with an excerpt, empty queries return metadata without excerpts, dates are inclusive, size is capped at 50, and sort defaults to relevance. This goes well beyond the schema in explaining actual behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long but justified by seven undocumented parameters and no annotations. It is front-loaded with the core semantics, organized into focused paragraphs per concern, and closes with a complete example, with no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity, optional parameters, and lack of annotations/schema descriptions, the description is complete: it covers result units, matching behavior, all filters, defaults, limits, pagination, and a realistic example. The presence of an output schema means return-value details are already covered elsewhere.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must explain every parameter, and it does: query syntax (OR, +, -, quoted phrases), territory_ids as 7-digit IBGE IDs, inclusive ISO dates, size/offset pagination, and sort_by values. The included example ties the parameters together concretely.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Search the text of published Brazilian municipal official gazettes.' It then explicitly distinguishes gazette content from city metadata and points to search_cities, making the purpose unmistakable and differentiating it from sibling tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives a clear workflow: resolve a city name to territory_id with search_cities first, or omit territory_ids to search all cities. It also explains query syntax, date filtering, pagination, and sort options, so an agent knows exactly when and how to invoke the tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 3 tool updatesv0.1.0
    • First observedget_city
    • First observedsearch_cities
    • First observedsearch_gazettes

TDQS

A4.8/5.0

Scored across 3 tools

Disambiguation5/5

Each tool has a clearly distinct role: search_cities resolves city names to IDs, get_city retrieves metadata by ID, and search_gazettes searches gazette content. No two tools overlap in purpose or output.

Naming Consistency5/5

All tool names follow a consistent verb_noun snake_case pattern: search_cities, get_city, search_gazettes. The singular/plural difference for 'city' is natural and does not create confusion.

Tool Count5/5

Three tools is a well-scoped set for a focused gazette search server. Each tool is necessary and covers a distinct step in the core workflow: resolve city, inspect city, search gazettes.

Completeness4/5

The core search workflow is fully covered: city name resolution, city detail lookup, and gazette full-text search with filtering. Minor gaps exist (e.g., no direct single-gazette fetch endpoint or full-text retrieval beyond excerpts), but they do not break the primary use case.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables querying official data from the Prefeitura de Recife (Recife City Hall) through a hosted, read-only MCP server. Supports MCP over HTTP with usage-based prepaid billing.
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    MCP server for querying municipal debt clearance certificates (Certidão Negativa de Débitos) from the Canela/RS city government. Enables read-only certificate lookups via natural language in MCP-compatible clients.
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Provides read-only access to Brazilian municipality codes from IBGE, an official government source, via MCP. Works with any MCP-compatible client to query municipality data using natural language.
    MIT