Skip to main content
Glama
felipecorreia

O meu voto

O meu voto

Answers to the questions a Brazilian voter asks before election day, from the open data of the Tribunal Superior Eleitoral (TSE), the electoral court: compare candidates side by side, find a polling place, check the election date. One query core serves them through an MCP server for AI assistants, a public REST API and a web page, without touching anyone's personal registration data.

Para eleitores: compare candidaturas e encontre seu local de votação em https://omeuvoto.pages.dev, ou peça ao seu assistente de IA com a frase abaixo.

Contents

Related MCP server: mcp-brasil

Try it in 30 seconds

In ChatGPT, Claude or another chat assistant, paste:

Fetch and follow the instructions at omeuvoto.pages.dev/prompt-llm to set up O meu voto in this assistant.

The setup instructions (in Portuguese, for the voter) open with a short menu, then cover MCP agents, chat apps with custom connectors and a public REST fallback for chats without a connector. Their source is web-next/public/prompt-llm.md.

In Claude Code:

claude mcp add --transport http --scope project o-meu-voto https://omeuvoto.pages.dev/mcp

Then ask, for example, "Quem são os candidatos a senador em SP?" or "Compare os candidatos 180 e 400 a senador em SP".

What it answers

  • Candidate comparison: 2 to 4 candidacies of the same office, state and round, side by side: party or alliance, ticket, registration status, whether votes for them will count, and total declared assets. Always in ballot-number order; it never ranks, scores or recommends.

  • Candidates by office, name or ballot number: ballot name, party, federation, coalition, status, occupation, gender as the TSE publishes it, and the official photo.

  • Where do I vote? The state, zone and section printed on the voter's title become the polling place address, with a warning when the place changed or the section votes at another one.

  • Polling places by city and neighborhood, for voters who do not know their section.

  • Election date and voting hours, from the official electoral calendar.

API

The same answers are open to any program, no key or sign-in. MCP for AI assistants at https://omeuvoto.pages.dev/mcp; REST under https://omeuvoto.pages.dev/api/v1, GET only:

curl -s 'https://omeuvoto.pages.dev/api/v1/candidates/compare?uf=SP&office=senador&number=180,400'

Every answer carries data, warnings and the TSE source it came from.

  • docs/api.md: the guide, with the seven MCP tools and their REST routes, how to connect, more curl examples, the answer envelope and the error codes.

  • OpenAPI and the interactive page: the generated reference.

  • docs/README.md: start here for every document, in reading order.

The server is the only source of truth

An LLM is good at conversation and bad at facts it cannot check. Here the language model only converses; every fact comes from the server. The service runs no model: core is deterministic code over an index built from the TSE files, tested without a network. Each answer carries the TSE dataset, file and generation timestamp it came from, the warnings that apply (stale data, a changed polling place, a round not yet published) and, when nothing matches, a reason instead of a guess. The setup text tells the assistant to answer only with what a tool or the API returned in the conversation, to cite that source, and never to fill a missing field from memory or a web search.

Architecture

flowchart LR
  TSE["TSE open data"] --> GHA["GitHub Actions<br/>fetch, extract, build,<br/>validate, publish"]
  GHA --> IDX[("Versioned DuckDB index<br/>(Cloud Storage)")]
  IDX --> CORE["Cloud Run<br/>one query core"]
  CORE --- MCP["/mcp (MCP)"]
  CORE --- REST["/api/v1 (REST)"]
  EDGE["Cloudflare Pages<br/>edge function"] --> MCP
  EDGE --> REST
  SITE["web-next site"] --> EDGE
  AI["AI assistants"] --> EDGE

A scheduled pipeline downloads the TSE files several times a day, on the cadence the TSE declares, and publishes one read-only DuckDB file with a manifest. A single Python service on Cloud Run loads it, swaps in new versions without a restart, and exposes the same core through MCP and REST. Cloudflare Pages serves the site and proxies the API and MCP on the same domain. Details, the refresh cadence and a one-line summary of every architecture decision: docs/architecture.md.

Data and privacy

What it never answers. Anything that depends on the individual voter registration: zone and section from a name or CPF, the status of a voter's title, justifications or debts. Those questions are redirected to the official e-Título app and TSE self-service. The site never asks for a CPF or a voter title number.

Candidate data is minimized at build time. Original TSE ZIPs and CSVs exist only in the refresh runner's temporary work directory, which is removed at the end of the run. They are never committed or published. Build drops CPF, voter title number, birth date, e-mail and the free text of declared assets, which can carry addresses and plates. These fields never reach the published index or any answer (ADR 0004, ADR 0009). A test fails if any of those columns reaches the index. A comparison never shows gender, race, marital status, education or age.

Voter inputs. Requests that carry the voter's location are never kept in the edge cache. One known gap is documented rather than hidden: the Cloud Run request logs keep the request URL, which can hold coordinates or a zone and section, for their 30-day retention (ADR 0011).

Telemetry. Anonymous product telemetry, off by default and switchable off by configuration. When enabled, each MCP tool call and REST request sends one PostHog event with the tool name or route, the latency, whether the answer was stale, which round it answered, and the not-found reason when there was one. It never carries the caller's IP, any part of the request (state, zone, section, municipality, name, ballot number), or a session or device identifier; the distinct id is a constant fixed per deployment, never generated per caller. Telemetry is sent outside the request path, and a PostHog outage never delays or fails a query. The environment variables that enable it are in docs/local-run.md.

How the data is fetched. The TSE's web infrastructure refuses plain HTTP clients, so the pipeline downloads with browser impersonation. That is stated openly, together with the volume (one download per file per refresh), and the TSE has been asked for a sanctioned route (docs/pipeline.md, ADR 0003).

How it was built

Built by AI coding agents under one human owner, from a domain model and decision records to sliced issues and automatically reviewed pull requests. The tools, the chain and what went wrong: docs/how-it-was-built.md.

The design documents are in Portuguese, the language of the domain: CONTEXT.md (the glossary), docs/domain-model.md (entities, invariants and the TSE column mapping), docs/codebase-design.md (module boundaries and tool contracts) and docs/adr/ (decisions, summarized in English in docs/architecture.md).

Run locally

Requires Python 3.12 and uv. The quickest path is the checks CI runs. The tests build their own index from small CSV fixtures and never touch the network.

uv sync
uv run ruff check .
uv run ruff format --check .
uv run pytest

Where to go next:

  • docs/README.md: every document in reading order.

  • docs/local-run.md: start the server on localhost, connect Claude Desktop, or serve an index built from the real TSE files in the service image.

  • docs/pipeline.md: the refresh pipeline, its stages, the index bucket layout and the candidate photo mirror. Written for whoever operates the pipeline.

  • web-next/README.md: build and run the site (Node 22, npm run dev) and the Cloudflare edge in front of it.

  • docs/architecture.md: the overview behind the diagram above.

License and data attribution

Code: MIT, see LICENSE.

Data: Tribunal Superior Eleitoral - Portal de Dados Abertos (CC-BY), https://dadosabertos.tse.jus.br. Every response carries the dataset, file and generation timestamp it came from. O meu voto is an independent project, not an official TSE service.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    MCP Server for accessing 36 Brazilian public data sources and 1 agent, enabling AI agents to query government data on economy, legislation, transparency, judiciary, elections, environment, health, and more.
    6
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    MCP server that connects AI agents to 28 Brazilian public APIs, providing tools to query government data on economy, legislation, transparency, judiciary, elections, environment, health, and more.
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables querying and retrieving Brazilian electoral clearance certificates (Certidão de Quitação Eleitoral) from the TSE using CPF, name, voter ID, birth date, and filiation. It provides a single read-only tool via MCP for integration with AI assistants.
    MIT