Skip to main content
Glama
alissonf216

parliamentary-nlp-mcp

by alissonf216

Parliamentary NLP MCP Auditor

Python 3.10+ MCP Protocol Transformers Hugging Face License: MIT Docker

A Model Context Protocol (MCP) server providing structured tools for auditing hate speech and offensive language in formal Brazilian parliamentary speeches.

Built for institutional speech moderation in a low-resource NLP setting (Brazilian Portuguese): a BERTimbau-family classifier with explicit uncertainty quantification and a stable tool contract for LLM clients (Cursor, Claude Desktop, MCP Inspector).


Table of Contents

  1. Summary

  2. Architecture

  3. Demo

  4. Key Features

  5. Quickstart via Docker

  6. Installation Tutorial

  7. Usage Tutorial

  8. Connect to Cursor / Claude Desktop

  9. Sample Output

  10. Project Layout

  11. Inference Pipeline

  12. Troubleshooting

  13. License


Related MCP server: glin-profanity-mcp

Summary

Legislative chambers produce a continuous stream of floor speeches and digital rhetoric. Offensive language, ad-hominem attacks, and hate speech in that stream are costly to review manually and poorly covered by English-centric moderation stacks.

This repository is the serving layer of a parliamentary discourse auditor:

Layer

What it does

MCP tool

Exposes audit_parliamentary_speech(text) over stdio for assistants and IDE agents

Inference engine

Tokenize → BERTimbau-family forward pass → softmax → Shannon entropy → structured JSON

Human-in-the-loop

requires_human_review=true when entropy (> 0.60) (ambiguous predictions)

Modeling (corpus, taxonomy, training, metrics, results, figures) lives in a dedicated document:

👉 docs/MODELING.md — full modeling & evaluation specification

Reproducible experiments (notebook + pipeline) and raw tables:

Runtime model strategy (important)

Stage

Checkpoint

Purpose

Default

alissonf216/parliamentary-bertimbau-auditor

Fine-tuned parliamentary BERTimbau (4-class taxonomy) — see docs/MODELING.md

Override without code changes:

export PARLIAMENTARY_NLP_MODEL_ID="alissonf216/parliamentary-bertimbau-auditor"
parliamentary-nlp-mcp

Canonical labels: NEUTRAL, GENERIC_OFFENSE, TARGETED_OFFENSE, EXPLICIT_HATE_SPEECH.


Architecture

The server is a thin MCP façade over a fine-tuned transformer. Agents talk MCP over stdio; weights load lazily from Hugging Face on the first tool call.

flowchart LR
  subgraph Clients
    Claude[Claude Desktop]
    Cursor[Cursor / IDE agent]
    Inspector[MCP Inspector]
  end

  subgraph "This repository"
    MCP["MCP Server<br/>audit_parliamentary_speech"]
    Engine["Inference engine<br/>tokenize → softmax → Shannon entropy"]
  end

  HF["Hugging Face<br/>parliamentary-bertimbau-auditor"]

  Claude -->|MCP stdio| MCP
  Cursor -->|MCP stdio| MCP
  Inspector -->|MCP stdio| MCP
  MCP --> Engine
  Engine -->|lazy download / cache| HF

Demo

Screen capture of the tool classifying a parliamentary utterance via an MCP client (Claude Desktop, Cursor, or MCP Inspector):

Add your demo: record a short GIF/video of audit_parliamentary_speech returning classification, confidence, and requires_human_review, then place it at docs/demo/mcp-audit-demo.gif (see docs/demo/README.md).

Until a recording is available, use the Sample Output JSON and the MCP Inspector walkthrough below.


Key Features

Feature

Detail

MCP / FastMCP integration

Single tool audit_parliamentary_speech over stdio (SDK 1.x FastMCP or 2.x MCPServer), ready for Cursor / Claude Desktop / MCP Inspector

Portuguese BERT backbone

Default: alissonf216/parliamentary-bertimbau-auditor; override via PARLIAMENTARY_NLP_MODEL_ID

Research taxonomy

Canonical labels: NEUTRAL, GENERIC_OFFENSE, TARGETED_OFFENSE, EXPLICIT_HATE_SPEECH (see MODELING.md)

Uncertainty quantification

Softmax probabilities + Shannon entropy (H(X)=-\sum P(x)\log P(x)); requires_human_review=true when entropy (> 0.60)

Lazy singleton load

Model weights download on first tool call, not at import time

Documented evaluation

Stratified CV, imbalance strategies, Flat / binary / cascade — MODELING.md + notebooks/ + figures

Docker image

Reproducible runtime via Dockerfile + docker compose (HF cache volume)


Quickstart via Docker

Requires Docker with Compose v2.

1 — Build and start the MCP server

git clone https://github.com/alissonf216/parliamentary-nlp-mcp.git
cd parliamentary-nlp-mcp
docker compose up --build

The container entrypoint is parliamentary-nlp-mcp (MCP over stdio). It will look idle in the terminal until a client attaches — that is expected. Model weights download on the first tool call and persist in the hf-cache volume.

Optional overrides (create a local .env or export before compose up):

export PARLIAMENTARY_NLP_MODEL_ID=alissonf216/parliamentary-bertimbau-auditor
# export HF_TOKEN=hf_...   # only if the checkpoint is private
docker compose up --build

2 — Point an MCP client at the container

One-shot interactive run (recommended for Claude Desktop / Cursor):

{
  "mcpServers": {
    "parliamentary-nlp": {
      "command": "docker",
      "args": [
        "compose",
        "-f",
        "/absolute/path/to/parliamentary-nlp-mcp/docker-compose.yml",
        "run",
        "--rm",
        "-i",
        "parliamentary-nlp-mcp"
      ]
    }
  }
}

Or a direct image run after docker compose build:

docker compose run --rm -i parliamentary-nlp-mcp

Prefer a local venv instead? Skip to Installation Tutorial.


Installation Tutorial

Follow these steps from a clean machine. Commands assume macOS / Linux; Windows notes are included inline.

Step 0 — Prerequisites

Requirement

Why

Python 3.10+

Runtime for the package (python3 --version)

pip / venv

Dependency isolation

~500 MB free disk

First download of the Hugging Face checkpoint

Node.js 18+ (optional)

Only needed for the MCP Inspector (npx)

Check your Python version:

python3 --version
# Expected: Python 3.10.x or newer

If python3 points to 3.9 or older, install a newer interpreter (Homebrew, pyenv, Conda, etc.) and use that binary in the steps below.

Step 1 — Clone the repository

git clone https://github.com/alissonf216/parliamentary-nlp-mcp.git
cd parliamentary-nlp-mcp

Or, if you already have the folder locally:

cd /path/to/parliamentary-nlp-mcp

Step 2 — Create and activate a virtual environment

python3 -m venv .venv

# macOS / Linux
source .venv/bin/activate

# Windows (PowerShell)
# .venv\Scripts\Activate.ps1

You should see (.venv) in your shell prompt.

Step 3 — Install the package (editable + dev tools)

pip install -U pip setuptools wheel
pip install -e ".[dev]"

What this does:

  • installs mcp, torch, transformers, and project code in editable mode

  • adds pytest for the test suite

  • registers the console command parliamentary-nlp-mcp

Verify the install:

which parliamentary-nlp-mcp
python -c "import parliamentary_nlp; print(parliamentary_nlp.__version__)"

Tests mock Hugging Face — no GPU and no model download:

pytest -v

Expected: all tests pass (e.g. 5 passed).


Usage Tutorial

There are three ways to use the auditor: Python API, MCP server + Inspector, or IDE / Claude Desktop.

Option A — Call the model from Python

Useful for notebooks, scripts, and debugging the prediction schema.

from parliamentary_nlp import ParliamentaryModel

# First run downloads and caches the default Hugging Face model
model = ParliamentaryModel()

result = model.predict(
    "Esse parlamentar é um corrupto incompetente e não merece ocupar a cadeira."
)
print(result)

Use your own fine-tuned checkpoint:

model = ParliamentaryModel(
    model_id="alissonf216/parliamentary-bertimbau-auditor"
)
print(model.predict("Senhor presidente, peço a palavra."))

Or via environment variable (also works for the MCP server):

export PARLIAMENTARY_NLP_MODEL_ID="alissonf216/parliamentary-bertimbau-auditor"

Option B — Run the MCP server locally

With the venv active:

parliamentary-nlp-mcp

Equivalents:

python -m parliamentary_nlp
python -m parliamentary_nlp.server

The process speaks MCP over stdio (it will look “idle” in the terminal — that is normal). Stop it with Ctrl+C.

Option C — Interactive demo with MCP Inspector

Best way to try the tool without wiring an IDE yet.

  1. Keep the venv activated (so parliamentary-nlp-mcp is on PATH).

  2. In the same project directory, run:

npx @modelcontextprotocol/inspector parliamentary-nlp-mcp
  1. The Inspector opens in the browser.

  2. Connect to the server, then select the tool audit_parliamentary_speech.

  3. Pass a Portuguese string in the text argument, for example:

O debate deve ser respeitoso e baseado em evidências.
  1. Click Run. The first call may take a minute while the model downloads; later calls are faster.

If npx cannot find the command, pass the absolute path to the binary:

npx @modelcontextprotocol/inspector /absolute/path/to/parliamentary-nlp-mcp/.venv/bin/parliamentary-nlp-mcp

Connect to Cursor / Claude Desktop

Cursor

  1. Open Cursor Settings → MCP (or edit your MCP config JSON).

  2. Add a server entry. Prefer the absolute path to the venv binary so Cursor does not depend on your shell PATH:

{
  "mcpServers": {
    "parliamentary-nlp": {
      "command": "/absolute/path/to/parliamentary-nlp-mcp/.venv/bin/parliamentary-nlp-mcp",
      "env": {
        "PARLIAMENTARY_NLP_MODEL_ID": "alissonf216/parliamentary-bertimbau-auditor"
      }
    }
  }
}
  1. Restart Cursor (or reload MCP servers).

  2. In chat, ask something like: “Use the parliamentary NLP auditor on this speech: …” — the client should invoke audit_parliamentary_speech.

Claude Desktop

Edit the Claude Desktop config file:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json

  • Windows: %APPDATA%\Claude\claude_desktop_config.json

{
  "mcpServers": {
    "parliamentary-nlp": {
      "command": "/absolute/path/to/parliamentary-nlp-mcp/.venv/bin/parliamentary-nlp-mcp",
      "env": {
        "PARLIAMENTARY_NLP_MODEL_ID": "alissonf216/parliamentary-bertimbau-auditor"
      }
    }
  }
}

Restart Claude Desktop and confirm the hammer / tools icon lists audit_parliamentary_speech.


Sample Output

Input (PT-BR): "Esse parlamentar é um corrupto incompetente e não merece ocupar a cadeira."

Output schema (illustrative):

{
  "text": "Esse parlamentar é um corrupto incompetente e não merece ocupar a cadeira.",
  "classification": "TARGETED_OFFENSE",
  "confidence": 0.812345,
  "entropy_uncertainty": 0.5412,
  "class_probabilities": {
    "NEUTRAL": 0.052101,
    "GENERIC_OFFENSE": 0.098234,
    "TARGETED_OFFENSE": 0.812345,
    "EXPLICIT_HATE_SPEECH": 0.03732
  },
  "requires_human_review": false
}

Note: With alissonf216/parliamentary-bertimbau-auditor, class_probabilities uses the 4-class research taxonomy above.

Field

Meaning

classification

Argmax label after softmax

confidence

Softmax mass of the top class

entropy_uncertainty

Shannon entropy in nats, rounded to 4 decimals

requires_human_review

true if entropy (> 0.60)


Project Layout

parliamentary-nlp-mcp/
├── docs/
│   ├── MODELING.md      # Modeling & evaluation (with figures)
│   ├── demo/            # GIF / screen capture of MCP in action
│   ├── figures/         # Heatmaps, CMs, ROC/PR, bars
│   └── results/         # CSV + JSON experiment tables
├── notebooks/
│   ├── README.md
│   ├── finetune_bertimbau_huggingface.ipynb  # train + save for Hugging Face
│   ├── experiments_hierarchy_imbalance.ipynb
│   └── experimentos_pipeline.py
├── src/parliamentary_nlp/
│   ├── __init__.py
│   ├── __main__.py      # python -m parliamentary_nlp
│   ├── model.py         # PyTorch / Hugging Face inference engine
│   └── server.py        # MCP tool surface
├── tests/
│   └── test_model.py
├── Dockerfile
├── docker-compose.yml
├── pyproject.toml
├── .gitignore
└── README.md

For corpus design, label definitions, training protocol, metrics, and quantitative results, read docs/MODELING.md. To reproduce experiments, start from notebooks/README.md.

Inference Pipeline

  1. Tokenize with AutoTokenizer (max_length=512, truncation on).

  2. Forward pass via AutoModelForSequenceClassification under torch.no_grad().

  3. Softmax over logits → class probabilities.

  4. Shannon entropy over the probability vector.

  5. Emit the structured AuditResult dictionary consumed by the MCP tool.


Troubleshooting

Problem

Fix

Python 3.9 / requires a different Python

Install Python 3.10+ and recreate .venv with that binary

command not found: parliamentary-nlp-mcp

Activate .venv, or use the absolute path under .venv/bin/

First Inspector call hangs

Normal — model download. Check network / Hugging Face access

Cursor does not see the tool

Use absolute command path; restart MCP; confirm venv has the package installed

Want CPU-only torch

Install a CPU wheel from pytorch.org before pip install -e ".[dev]" if needed

Docker build is slow / large

First build pulls PyTorch; later builds use the layer cache. HF weights live in the hf-cache volume

Claude/Cursor + Docker: no tools

Use docker compose run --rm -i … (stdin must stay open); prefer absolute path to docker-compose.yml


License

MIT — see LICENSE. Model weights remain under their respective Hugging Face licenses (BERTimbau / fine-tuned checkpoint).


Citation / Research Context

This MCP server is the serving layer of a computational auditor for institutional discourse in Brazilian Portuguese: domain-adapted transformers, calibrated uncertainty, and human-review escalation. Modeling details, experimental protocol, and results are documented in docs/MODELING.md.

Available Tools

1 tool
audit_parliamentary_speechA

Audit Portuguese parliamentary or political speech for offensive content.

Use this tool whenever a user asks you to analyse, moderate, classify, or safety-check political / legislative discourse in Portuguese (PT-BR), including floor speeches, committee interventions, social-media posts by elected officials, and campaign rhetoric.

The underlying BERTimbau-based classifier estimates:

  • Predominant category (neutral vs. offense / hate-speech tiers)

  • Softmax confidence for the top class

  • Shannon-entropy uncertainty; high entropy (> 0.60) sets requires_human_review=True so borderline cases can be escalated

Args: text: Raw Portuguese utterance or transcript excerpt to audit.

Returns: Structured audit dictionary with classification, confidence, entropy_uncertainty, class_probabilities, and requires_human_review.

ParametersJSON Schema
NameRequiredDescriptionDefault
textYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

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 burden and exceeds it. It details the BERTimbau-based classifier, outputs (classification, confidence, entropy), the entropy threshold of >0.60, and how it triggers requires_human_review. This gives the agent a complete behavioral model of what the tool does and what to expect.

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 well-structured with an opening summary, usage guidance, bullet-pointed behavior details, and clear Args/Returns sections. While moderately long, each sentence provides necessary context; it is front-loaded and scannable without fluff.

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 analytical complexity and the presence of an output schema, the description adequately explains return fields (classification, confidence, entropy_uncertainty, class_probabilities, requires_human_review) and the decision logic. It also covers a broad range of use cases, making it complete for an agent to select and invoke correctly.

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?

The schema only defines 'text' as a string with no description. The tool description compensates fully by specifying 'Raw Portuguese utterance or transcript excerpt to audit', clarifying language and content expectations beyond the schema. Coverage gap of 0% is effectively closed.

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 clearly states the tool audits Portuguese parliamentary or political speech for offensive content, with a specific verb 'audit' and resource. It elaborates on scope including floor speeches, committee interventions, social media posts, and campaign rhetoric, making the purpose unambiguous even without siblings.

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 instructs 'Use this tool whenever a user asks you to analyse, moderate, classify, or safety-check political / legislative discourse in Portuguese (PT-BR)', providing concrete triggers for use. It effectively communicates when to apply the tool, even without listing alternatives.

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. 1 tool updatev0.1.0
    • First observedaudit_parliamentary_speech

TDQS

A4.9/5.0

Scored across 1 tool

Disambiguation5/5

With only one tool, there is no possibility of confusion. The tool's purpose is clearly defined and covers the entire domain of auditing parliamentary speech.

Naming Consistency5/5

The single tool name 'audit_parliamentary_speech' follows a clear verb_object pattern. Although there is only one example, the naming is consistent with common conventions.

Tool Count3/5

The server has just one tool, which is on the thin end of the range. For a domain like parliamentary speech analysis, one might expect additional tools (e.g., for summarizing or extracting topics), but for the stated auditing purpose, a single tool can be acceptable.

Completeness5/5

The tool provides a comprehensive audit result including classification, confidence, entropy, and a human-review flag, covering all stated use cases (analyse, moderate, classify, safety-check). There are no obvious dead ends within the tool's intended scope.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    An MCP server that provides access to the Brazilian Chamber of Deputies open data API. It enables users to search for deputies, track their expenses, and query legislative information such as bills and API endpoints.
    6
    1
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Content moderation and profanity detection MCP server with 19 tools, 24 language support, leetspeak/Unicode obfuscation detection, context-aware analysis, batch processing, and user tracking for AI-powered content safety.
    21 npm
    60
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    MCP server for the Brazilian Chamber of Deputies open-data API, enabling search and retrieval of federal legislative bills and their status.
    15
    42 PyPI
    1
    Apache 2.0