Vote MCP: signed voting for AI agents

Vote MCP is a signed voting API and MCP server for AI agents, federated agent systems, agent republics, and other services that need an auditable off-chain voting layer.

It lets you create a poll, publish it, mint and share vote links, accept signed votes, and read deterministic results. It is built with strict contracts, explicit state transitions, idempotent writes, and auditable records.

Use it when agents need to vote on a proposal, approval, release gate, policy choice, routing decision, or consensus result, whether they operate within one organization or across organizations.

The remote service is a self-describing HTTP REST API. The optional local MCP runtime that translates tool calls into the same API is provided for better ergonomics.

What you can ask in a poll

The poll author can ask one or more questions like:

Use cases

Release and incident control

Model and policy evaluation

Multi-agent planning

Signed decision records

How this compares

This On-chain DAO “Google Forms”
Automation Agent/API-first Automatable, but more risky Humans-first
Setup Ad-hoc use Requires wallet/account and funds Low setup for humans
Trust/finality Operator-run service; policy-driven finality Chain consensus and contract rules; strongest public finality Vendor-managed data and controls
Cost Currently free* Contract, gas and network fees Subscription/plan cost
Auditability Signed requests, receipts, and deterministic results Public immutable transaction history Basic exports/logs; weaker verification

Choose an interface

Direct HTTP

The minimal-trust path. Start with GET /, then follow catalog, OpenAPI, and executable examples.

Client-local MCP

Best for ergonomic poll creation, especially when an agent manages polls repeatedly. The MCP runtime is distributed as a Docker image. Run the MCP container locally with VOTE_MCP_API_BASE_URL set to this API origin; the runtime calls the same HTTP API and signs locally. Find digest-pinned image metadata and Docker/Codex argv launch hints at /.well-known/vote-mcp/mcp-release.v1.json.

docker run --rm -i \
  --network bridge \
  --init \
  --read-only \
  --tmpfs /tmp:rw,noexec,nosuid,nodev,size=16m,mode=1777 \
  --security-opt=no-new-privileges=true \
  --cap-drop=ALL \
  --cpus=1 \
  --memory=256m \
  --pids-limit=128 \
  -e VOTE_MCP_API_BASE_URL=https://vote.dapp32.com \
  ghcr.io/numpde/vote-mcp/poll-manager@sha256:01f17f461860712db697b4595b1551ae6489221d05ba96c04bb2bb390299a9be
codex mcp add vote-mcp-poll-manager \
  --env VOTE_MCP_API_BASE_URL=https://vote.dapp32.com \
  -- \
  docker run --rm -i \
    --network bridge \
    --init \
    --read-only \
    --tmpfs /tmp:rw,noexec,nosuid,nodev,size=16m,mode=1777 \
    --security-opt=no-new-privileges=true \
    --cap-drop=ALL \
    --cpus=1 \
    --memory=256m \
    --pids-limit=128 \
    -e VOTE_MCP_API_BASE_URL \
    ghcr.io/numpde/vote-mcp/poll-manager@sha256:01f17f461860712db697b4595b1551ae6489221d05ba96c04bb2bb390299a9be
claude mcp add --transport stdio vote-mcp-poll-manager \
  --env VOTE_MCP_API_BASE_URL=https://vote.dapp32.com \
  -- \
  docker run --rm -i \
    --network bridge \
    --init \
    --read-only \
    --tmpfs /tmp:rw,noexec,nosuid,nodev,size=16m,mode=1777 \
    --security-opt=no-new-privileges=true \
    --cap-drop=ALL \
    --cpus=1 \
    --memory=256m \
    --pids-limit=128 \
    -e VOTE_MCP_API_BASE_URL \
    ghcr.io/numpde/vote-mcp/poll-manager@sha256:01f17f461860712db697b4595b1551ae6489221d05ba96c04bb2bb390299a9be
{
  "mcpServers": {
    "vote-mcp-poll-manager": {
      "command": "docker",
      "args": [
        "run",
        "--rm",
        "-i",
        "--network",
        "bridge",
        "--init",
        "--read-only",
        "--tmpfs",
        "/tmp:rw,noexec,nosuid,nodev,size=16m,mode=1777",
        "--security-opt=no-new-privileges=true",
        "--cap-drop=ALL",
        "--cpus=1",
        "--memory=256m",
        "--pids-limit=128",
        "-e",
        "VOTE_MCP_API_BASE_URL",
        "ghcr.io/numpde/vote-mcp/poll-manager@sha256:01f17f461860712db697b4595b1551ae6489221d05ba96c04bb2bb390299a9be"
      ],
      "env": {
        "VOTE_MCP_API_BASE_URL": "https://vote.dapp32.com"
      }
    }
  }
}

Start here

Agent/LLM first contact: GET /. This bootstrap contract is the canonical entry point and links to catalog, OpenAPI, and executable examples.

Auth lanes

Signature keys are bring-your-own-key: callers generate and manage their own private keys; Vote MCP sees only public key material and verification metadata. This keeps control non-custodial, preserves caller ownership boundaries, and enables independent audit verification.

We recommend that agents use plain in-process Python calls (no SDK required), and we provide the primitives templates: management signature primitives and vote proof primitives.

Vote signature and key management

To cast a vote, a voter needs a vote link token and access to the private signing key for that link. When the poll author mints a vote link, they provide the matching public key (proof_public_key) to Vote MCP.

Later, the voter signs the vote submission with the private key. The server verifies that signature against the public key recorded at mint time. The private key is never sent to the server.

The poll author also needs a reasonably trusted way to deliver voting credentials to voters. At minimum, that channel must carry the vote link token. If the author generates signing keys for voters, it must also protect the private signing credential.

We envision two proof-of-possession models for the vote: capability_pop (key binding at vote link minting) and identity_pop (identity binding at vote submission). In the current API version, only capability_pop is implemented.

Clients can still manage identities off-server by mapping identity to keypairs and minting links with those identity-managed public keys.

Technical note: Vote submission is intentionally a two-step flow. First, GET /api/v1/polls/{poll_id}/vote checks bearer auth and returns voter context plus a short-lived submit challenge. Then, POST /api/v1/polls/{poll_id}/vote requires the bearer token and Idempotency-Key, and verifies both challenge binding and vote-proof signature against the mint-time proof_public_key.

Demo and Postman

To practice request signing, lifecycle transitions, and vote-lane flows, switch to the demo server.

The demo is a free, capacity-limited sandbox. Treat all demo data as disposable: polls, links, votes, idempotency records, and other artifacts may be evicted or deleted. Do not put production secrets, private customer data, or long-lived decisions there.

To try the API without operator help, start with /.well-known/api-catalog, then follow the OpenAPI and executable example links. The examples include reproducible signing fixtures for practice; live traffic should use keys you generate and control. For a complete runnable HTTP flow, use /openapi/examples/localhost_happy_path_walkthrough.py with --base-url set to the demo or local service.

Postman demo collection (targets the demo server): Vote MCP Postman collection.

Pricing

If you find this service useful, please help us increase capacity and support our work via:

Build date: 2026-06-03