parlaibero-mcp 0.2.0__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.
@@ -0,0 +1,12 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ .pytest_cache/
5
+ dist/
6
+ build/
7
+ *.egg-info/
8
+ .DS_Store
9
+ .env
10
+ *.part
11
+ *.duckdb
12
+ *.duckdb.wal
@@ -0,0 +1,194 @@
1
+ Metadata-Version: 2.5
2
+ Name: parlaibero-mcp
3
+ Version: 0.2.0
4
+ Summary: MCP server for ParlaIbero: plenary speeches from 16 Ibero-American parliaments, for AI agents
5
+ Project-URL: Homepage, https://rodrodr.github.io/parlaibero/
6
+ Project-URL: Repository, https://github.com/rodrodr/parlaibero-tools
7
+ Project-URL: Data, https://dataverse.harvard.edu/dataverse/parlaibero
8
+ Author: Rodrigo Rodrigues-Silveira
9
+ License-Expression: MIT
10
+ Keywords: Latin America,Portugal,Spain,corpus,mcp,parliament,parliamentary speeches
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Natural Language :: English
14
+ Classifier: Natural Language :: Portuguese
15
+ Classifier: Natural Language :: Spanish
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
19
+ Classifier: Topic :: Sociology
20
+ Classifier: Topic :: Text Processing :: Linguistic
21
+ Requires-Python: >=3.10
22
+ Requires-Dist: duckdb>=1.1
23
+ Requires-Dist: httpx>=0.27
24
+ Requires-Dist: mcp>=1.10
25
+ Description-Content-Type: text/markdown
26
+
27
+ # parlaibero-mcp
28
+
29
+ An [MCP](https://modelcontextprotocol.io) server that gives AI agents access to **ParlaIbero**:
30
+ 10 million plenary interventions from the lower or single chambers of 16 Ibero-American countries,
31
+ linked to their deputies, deposited in [Harvard Dataverse](https://dataverse.harvard.edu/dataverse/parlaibero)
32
+ (CC BY 4.0, one DOI per country).
33
+
34
+ The server downloads each country's dataset once (MD5-verified, straight from Dataverse), loads it
35
+ into a local [DuckDB](https://duckdb.org) database and exposes tools to search, count and read it.
36
+ Nothing is sent anywhere except plain GET requests to Dataverse.
37
+
38
+ ## Install
39
+
40
+ You need [uv](https://docs.astral.sh/uv/getting-started/installation/). No clone is required.
41
+
42
+ **Claude Code**
43
+
44
+ ```bash
45
+ claude mcp add parlaibero -s user -- uvx --from "git+https://github.com/rodrodr/parlaibero-tools#subdirectory=mcp" parlaibero-mcp
46
+ ```
47
+
48
+ **Codex CLI**
49
+
50
+ ```bash
51
+ codex mcp add parlaibero -- uvx --from "git+https://github.com/rodrodr/parlaibero-tools#subdirectory=mcp" parlaibero-mcp
52
+ ```
53
+
54
+ **Claude Desktop** (`claude_desktop_config.json`), **Cursor** (`~/.cursor/mcp.json`), **Gemini CLI**
55
+ (`~/.gemini/settings.json`), **Windsurf** and most other clients use the same block:
56
+
57
+ ```json
58
+ {
59
+ "mcpServers": {
60
+ "parlaibero": {
61
+ "command": "uvx",
62
+ "args": ["--from", "git+https://github.com/rodrodr/parlaibero-tools#subdirectory=mcp", "parlaibero-mcp"]
63
+ }
64
+ }
65
+ }
66
+ ```
67
+
68
+ Desktop apps do not always see your shell's `PATH`: if the server fails to start, replace `"uvx"` with
69
+ the full path printed by `which uvx` (e.g. `/Users/you/.local/bin/uvx`).
70
+
71
+ To install it as a regular command instead: `uv tool install "git+https://github.com/rodrodr/parlaibero-tools#subdirectory=mcp"`,
72
+ then use `parlaibero-mcp` as the command.
73
+
74
+ ## Tools
75
+
76
+ **Getting and understanding the data**
77
+
78
+ | tool | what it does |
79
+ |---|---|
80
+ | `list_countries` | the 16 datasets: DOI, published version, download size, what is already local |
81
+ | `download_country` | download a country from Dataverse and load it (skips files already current) |
82
+ | `import_from_folder` | load CSV files you downloaded by hand from Dataverse |
83
+ | `get_documentation` | README, data dictionary, known limitations, process report, corpus facts (`en`/`es`/`pt`) — no download needed |
84
+ | `describe_data` | schema of the local tables, loaded countries, example queries |
85
+ | `coverage` | **check the base before reading a trend**: sessions, speech words, linked and sex-known shares per year or legislature, missing years, low-base periods, each corpus's linkage figures |
86
+ | `how_to_cite` | formatted citation of a country's dataset with DOI and version |
87
+
88
+ **Counting words over time**
89
+
90
+ | tool | what it does |
91
+ |---|---|
92
+ | `ngram_viewer` | Google Books Ngram-style series: several words or phrases at once (`,` separates series, `+` sums variants, `*` ends a prefix), per million words, raw counts or % of interventions, optional smoothing, one line per country if wanted. Every point carries its base (words, sessions) and low-base years are flagged. Can write the chart as **`.svg`** (figure for a paper or slide) or **`.html`** (hover read-out, dark mode, data table) |
93
+ | `term_counter` | totals for one or more terms: occurrences, interventions, sessions, speakers, by sex and party with per-million rates, and the **first and last use in each country** |
94
+ | `term_frequency` | one term grouped by year, decade, country, legislature, party, sex, speaker or session type |
95
+
96
+ **Who speaks and how**
97
+
98
+ | tool | what it does |
99
+ |---|---|
100
+ | `share_of_voice` | share of words (or turns) and of speakers by sex or party, against the group's share of members on the register in that period, and their ratio (> 1 = speaks more than its weight) |
101
+ | `distinctive_words` | the words that most distinguish two groups — women vs men, party vs party, period vs period — by weighted log-odds with an informative Dirichlet prior (Monroe, Colaresi & Quinn 2008) |
102
+ | `kwic` | keyword-in-context concordance lines from a reproducible random sample |
103
+ | `collocations` | words over-represented around a term (Dunning log-likelihood) |
104
+ | `search_text` | find interventions by word, phrase or regex, with filters, newest first |
105
+ | `get_intervention` · `get_session` | read one turn with its context, or a whole session |
106
+
107
+ **Anything else, and reproducibility**
108
+
109
+ | tool | what it does |
110
+ |---|---|
111
+ | `query_sql` | read-only DuckDB SQL; the connection cannot write, read other files or reach the network |
112
+ | `export_result` | save a query's full result as CSV or Parquet for R, Python or Stata |
113
+ | `query_log` | every analysis run (tool, parameters, dataset versions); can write a Markdown methods note |
114
+
115
+ The resource `parlaibero://about` summarises the collection.
116
+
117
+ ### Two filters that change results
118
+
119
+ Most analysis tools take `exclude_chair` and `max_turn_words`, and warn when you should use them:
120
+
121
+ - **`max_turn_words`** — the published corpora keep documents read into the record (committee
122
+ reports, bills, lists) as speech when the record marks no separator. They sit in very long turns
123
+ and, measured on 3 October 2026, hold **54 % of the speech words in Argentina, 26 % in Uruguay and
124
+ 25 % in Mexico**, against under 1 % in Spain or Brazil. Any rate per million words or share of
125
+ words is affected; `max_turn_words=10000` leaves those turns out, and `coverage` reports the share
126
+ per country (`long_turn_words_pct`).
127
+ - **`exclude_chair`** — the presiding officer's procedural turns (giving the floor, calling votes)
128
+ can dominate a group. Comparing women and men deputies in Spain 2016-2023, the most distinctive
129
+ "women's words" are *votación, votos, pausa* with the chair in, and *mujeres, violencia,
130
+ igualdad, género* without it.
131
+
132
+ Rows without a date (6,187 in Ecuador, 44 in Costa Rica) are left out of anything by year or
133
+ period, and the tools say so.
134
+
135
+ ### How words are counted
136
+
137
+ A word is a run of letters, digits or combining marks. Counts are whole-word, case- and
138
+ accent-insensitive (`nacion` = `Nación`), so `corrupción` does not count `anticorrupción` — add
139
+ it as a variant (`corrupción+anticorrupción`) if you want both. `n_words`, the precomputed word
140
+ table and every tool use this same definition, so their figures add up. Single words are served
141
+ from a precomputed table (instant); phrases and filters by party or deputy scan the text (seconds).
142
+
143
+ ## Data model
144
+
145
+ ```
146
+ interventions country · id_session · id_int · legislature · legislative_session · session_number ·
147
+ date · session_type · intervention_order · speaker_raw · id_dep · speaker_name ·
148
+ sex · party · district · dm_speech · text · n_words
149
+ unigrams word counts of speech rows by country, year and sex (accent-folded)
150
+ vocab display form of each folded word
151
+ deputies country + the core columns of every deputy register
152
+ deputies_{iso} the full register of one country, with its own extra columns
153
+ datasets what is loaded: DOI, version, source, rows, sessions, date range
154
+ ```
155
+
156
+ Things worth knowing before drawing conclusions (the server also tells the agent):
157
+
158
+ - `dm_speech = 1` marks speech. Rows with `0` are cover pages, summaries, vote tallies and
159
+ reproduced documents; `intervention_order = 0` is the session's Prolegomena.
160
+ - `sex`, `party` and `district` come from the deputy register and are empty when the speaker is
161
+ not a linked deputy (ministers, clerks, collective or anonymous voices).
162
+ - Coverage, linkage and caveats differ by country: read `known_limitations` and `corpus_info`.
163
+ - `id_session` / `id_int` are stable within a published version, not across versions. Cite the
164
+ dataset with its version.
165
+
166
+ ## Command line
167
+
168
+ ```bash
169
+ parlaibero-mcp # serve MCP over stdio (what clients run)
170
+ parlaibero-mcp download SV ES # download and load countries ('all' for the 16)
171
+ parlaibero-mcp import ~/Downloads/parlaibero
172
+ parlaibero-mcp status
173
+ parlaibero-mcp reindex # after upgrading from an older version (no re-download)
174
+ parlaibero-mcp remove SV
175
+ ```
176
+
177
+ Large countries (Brazil, Mexico, Portugal, Ecuador, Spain: 1-1.5 GB each) are best downloaded from
178
+ the terminal so a client time-out does not interrupt them. All 16 take about 12 GB of downloads
179
+ plus 15 GB of database; the downloaded CSVs can be deleted afterwards to save space
180
+ (`~/.parlaibero/data/{ISO}/*.csv`), but they are needed to re-import.
181
+
182
+ ## Configuration
183
+
184
+ | variable | default | |
185
+ |---|---|---|
186
+ | `PARLAIBERO_HOME` | `~/.parlaibero` | where downloads and the database live |
187
+ | `PARLAIBERO_DATAVERSE_URL` | `https://dataverse.harvard.edu` | Dataverse installation |
188
+ | `PARLAIBERO_LOG` | `1` | `0` turns off the query log (`~/.parlaibero/query_log.jsonl`) |
189
+
190
+ ## License
191
+
192
+ MIT for the code. The data are CC BY 4.0 and are cited per country with their DOI.
193
+
194
+ Grant PID2022-141706NB-C22 funded by MICIU/AEI/10.13039/501100011033 and by ERDF/EU.
@@ -0,0 +1,168 @@
1
+ # parlaibero-mcp
2
+
3
+ An [MCP](https://modelcontextprotocol.io) server that gives AI agents access to **ParlaIbero**:
4
+ 10 million plenary interventions from the lower or single chambers of 16 Ibero-American countries,
5
+ linked to their deputies, deposited in [Harvard Dataverse](https://dataverse.harvard.edu/dataverse/parlaibero)
6
+ (CC BY 4.0, one DOI per country).
7
+
8
+ The server downloads each country's dataset once (MD5-verified, straight from Dataverse), loads it
9
+ into a local [DuckDB](https://duckdb.org) database and exposes tools to search, count and read it.
10
+ Nothing is sent anywhere except plain GET requests to Dataverse.
11
+
12
+ ## Install
13
+
14
+ You need [uv](https://docs.astral.sh/uv/getting-started/installation/). No clone is required.
15
+
16
+ **Claude Code**
17
+
18
+ ```bash
19
+ claude mcp add parlaibero -s user -- uvx --from "git+https://github.com/rodrodr/parlaibero-tools#subdirectory=mcp" parlaibero-mcp
20
+ ```
21
+
22
+ **Codex CLI**
23
+
24
+ ```bash
25
+ codex mcp add parlaibero -- uvx --from "git+https://github.com/rodrodr/parlaibero-tools#subdirectory=mcp" parlaibero-mcp
26
+ ```
27
+
28
+ **Claude Desktop** (`claude_desktop_config.json`), **Cursor** (`~/.cursor/mcp.json`), **Gemini CLI**
29
+ (`~/.gemini/settings.json`), **Windsurf** and most other clients use the same block:
30
+
31
+ ```json
32
+ {
33
+ "mcpServers": {
34
+ "parlaibero": {
35
+ "command": "uvx",
36
+ "args": ["--from", "git+https://github.com/rodrodr/parlaibero-tools#subdirectory=mcp", "parlaibero-mcp"]
37
+ }
38
+ }
39
+ }
40
+ ```
41
+
42
+ Desktop apps do not always see your shell's `PATH`: if the server fails to start, replace `"uvx"` with
43
+ the full path printed by `which uvx` (e.g. `/Users/you/.local/bin/uvx`).
44
+
45
+ To install it as a regular command instead: `uv tool install "git+https://github.com/rodrodr/parlaibero-tools#subdirectory=mcp"`,
46
+ then use `parlaibero-mcp` as the command.
47
+
48
+ ## Tools
49
+
50
+ **Getting and understanding the data**
51
+
52
+ | tool | what it does |
53
+ |---|---|
54
+ | `list_countries` | the 16 datasets: DOI, published version, download size, what is already local |
55
+ | `download_country` | download a country from Dataverse and load it (skips files already current) |
56
+ | `import_from_folder` | load CSV files you downloaded by hand from Dataverse |
57
+ | `get_documentation` | README, data dictionary, known limitations, process report, corpus facts (`en`/`es`/`pt`) — no download needed |
58
+ | `describe_data` | schema of the local tables, loaded countries, example queries |
59
+ | `coverage` | **check the base before reading a trend**: sessions, speech words, linked and sex-known shares per year or legislature, missing years, low-base periods, each corpus's linkage figures |
60
+ | `how_to_cite` | formatted citation of a country's dataset with DOI and version |
61
+
62
+ **Counting words over time**
63
+
64
+ | tool | what it does |
65
+ |---|---|
66
+ | `ngram_viewer` | Google Books Ngram-style series: several words or phrases at once (`,` separates series, `+` sums variants, `*` ends a prefix), per million words, raw counts or % of interventions, optional smoothing, one line per country if wanted. Every point carries its base (words, sessions) and low-base years are flagged. Can write the chart as **`.svg`** (figure for a paper or slide) or **`.html`** (hover read-out, dark mode, data table) |
67
+ | `term_counter` | totals for one or more terms: occurrences, interventions, sessions, speakers, by sex and party with per-million rates, and the **first and last use in each country** |
68
+ | `term_frequency` | one term grouped by year, decade, country, legislature, party, sex, speaker or session type |
69
+
70
+ **Who speaks and how**
71
+
72
+ | tool | what it does |
73
+ |---|---|
74
+ | `share_of_voice` | share of words (or turns) and of speakers by sex or party, against the group's share of members on the register in that period, and their ratio (> 1 = speaks more than its weight) |
75
+ | `distinctive_words` | the words that most distinguish two groups — women vs men, party vs party, period vs period — by weighted log-odds with an informative Dirichlet prior (Monroe, Colaresi & Quinn 2008) |
76
+ | `kwic` | keyword-in-context concordance lines from a reproducible random sample |
77
+ | `collocations` | words over-represented around a term (Dunning log-likelihood) |
78
+ | `search_text` | find interventions by word, phrase or regex, with filters, newest first |
79
+ | `get_intervention` · `get_session` | read one turn with its context, or a whole session |
80
+
81
+ **Anything else, and reproducibility**
82
+
83
+ | tool | what it does |
84
+ |---|---|
85
+ | `query_sql` | read-only DuckDB SQL; the connection cannot write, read other files or reach the network |
86
+ | `export_result` | save a query's full result as CSV or Parquet for R, Python or Stata |
87
+ | `query_log` | every analysis run (tool, parameters, dataset versions); can write a Markdown methods note |
88
+
89
+ The resource `parlaibero://about` summarises the collection.
90
+
91
+ ### Two filters that change results
92
+
93
+ Most analysis tools take `exclude_chair` and `max_turn_words`, and warn when you should use them:
94
+
95
+ - **`max_turn_words`** — the published corpora keep documents read into the record (committee
96
+ reports, bills, lists) as speech when the record marks no separator. They sit in very long turns
97
+ and, measured on 3 October 2026, hold **54 % of the speech words in Argentina, 26 % in Uruguay and
98
+ 25 % in Mexico**, against under 1 % in Spain or Brazil. Any rate per million words or share of
99
+ words is affected; `max_turn_words=10000` leaves those turns out, and `coverage` reports the share
100
+ per country (`long_turn_words_pct`).
101
+ - **`exclude_chair`** — the presiding officer's procedural turns (giving the floor, calling votes)
102
+ can dominate a group. Comparing women and men deputies in Spain 2016-2023, the most distinctive
103
+ "women's words" are *votación, votos, pausa* with the chair in, and *mujeres, violencia,
104
+ igualdad, género* without it.
105
+
106
+ Rows without a date (6,187 in Ecuador, 44 in Costa Rica) are left out of anything by year or
107
+ period, and the tools say so.
108
+
109
+ ### How words are counted
110
+
111
+ A word is a run of letters, digits or combining marks. Counts are whole-word, case- and
112
+ accent-insensitive (`nacion` = `Nación`), so `corrupción` does not count `anticorrupción` — add
113
+ it as a variant (`corrupción+anticorrupción`) if you want both. `n_words`, the precomputed word
114
+ table and every tool use this same definition, so their figures add up. Single words are served
115
+ from a precomputed table (instant); phrases and filters by party or deputy scan the text (seconds).
116
+
117
+ ## Data model
118
+
119
+ ```
120
+ interventions country · id_session · id_int · legislature · legislative_session · session_number ·
121
+ date · session_type · intervention_order · speaker_raw · id_dep · speaker_name ·
122
+ sex · party · district · dm_speech · text · n_words
123
+ unigrams word counts of speech rows by country, year and sex (accent-folded)
124
+ vocab display form of each folded word
125
+ deputies country + the core columns of every deputy register
126
+ deputies_{iso} the full register of one country, with its own extra columns
127
+ datasets what is loaded: DOI, version, source, rows, sessions, date range
128
+ ```
129
+
130
+ Things worth knowing before drawing conclusions (the server also tells the agent):
131
+
132
+ - `dm_speech = 1` marks speech. Rows with `0` are cover pages, summaries, vote tallies and
133
+ reproduced documents; `intervention_order = 0` is the session's Prolegomena.
134
+ - `sex`, `party` and `district` come from the deputy register and are empty when the speaker is
135
+ not a linked deputy (ministers, clerks, collective or anonymous voices).
136
+ - Coverage, linkage and caveats differ by country: read `known_limitations` and `corpus_info`.
137
+ - `id_session` / `id_int` are stable within a published version, not across versions. Cite the
138
+ dataset with its version.
139
+
140
+ ## Command line
141
+
142
+ ```bash
143
+ parlaibero-mcp # serve MCP over stdio (what clients run)
144
+ parlaibero-mcp download SV ES # download and load countries ('all' for the 16)
145
+ parlaibero-mcp import ~/Downloads/parlaibero
146
+ parlaibero-mcp status
147
+ parlaibero-mcp reindex # after upgrading from an older version (no re-download)
148
+ parlaibero-mcp remove SV
149
+ ```
150
+
151
+ Large countries (Brazil, Mexico, Portugal, Ecuador, Spain: 1-1.5 GB each) are best downloaded from
152
+ the terminal so a client time-out does not interrupt them. All 16 take about 12 GB of downloads
153
+ plus 15 GB of database; the downloaded CSVs can be deleted afterwards to save space
154
+ (`~/.parlaibero/data/{ISO}/*.csv`), but they are needed to re-import.
155
+
156
+ ## Configuration
157
+
158
+ | variable | default | |
159
+ |---|---|---|
160
+ | `PARLAIBERO_HOME` | `~/.parlaibero` | where downloads and the database live |
161
+ | `PARLAIBERO_DATAVERSE_URL` | `https://dataverse.harvard.edu` | Dataverse installation |
162
+ | `PARLAIBERO_LOG` | `1` | `0` turns off the query log (`~/.parlaibero/query_log.jsonl`) |
163
+
164
+ ## License
165
+
166
+ MIT for the code. The data are CC BY 4.0 and are cited per country with their DOI.
167
+
168
+ Grant PID2022-141706NB-C22 funded by MICIU/AEI/10.13039/501100011033 and by ERDF/EU.
@@ -0,0 +1,41 @@
1
+ [project]
2
+ name = "parlaibero-mcp"
3
+ version = "0.2.0"
4
+ description = "MCP server for ParlaIbero: plenary speeches from 16 Ibero-American parliaments, for AI agents"
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ license = "MIT"
8
+ authors = [{ name = "Rodrigo Rodrigues-Silveira" }]
9
+ keywords = ["mcp", "parliament", "parliamentary speeches", "Latin America", "Spain", "Portugal", "corpus"]
10
+ classifiers = [
11
+ "Development Status :: 4 - Beta",
12
+ "Intended Audience :: Science/Research",
13
+ "Natural Language :: Spanish",
14
+ "Natural Language :: Portuguese",
15
+ "Natural Language :: English",
16
+ "Operating System :: OS Independent",
17
+ "Programming Language :: Python :: 3",
18
+ "Topic :: Scientific/Engineering :: Information Analysis",
19
+ "Topic :: Sociology",
20
+ "Topic :: Text Processing :: Linguistic",
21
+ ]
22
+ dependencies = [
23
+ "mcp>=1.10",
24
+ "duckdb>=1.1",
25
+ "httpx>=0.27",
26
+ ]
27
+
28
+ [project.urls]
29
+ Homepage = "https://rodrodr.github.io/parlaibero/"
30
+ Repository = "https://github.com/rodrodr/parlaibero-tools"
31
+ Data = "https://dataverse.harvard.edu/dataverse/parlaibero"
32
+
33
+ [project.scripts]
34
+ parlaibero-mcp = "parlaibero_mcp.cli:main"
35
+
36
+ [build-system]
37
+ requires = ["hatchling"]
38
+ build-backend = "hatchling.build"
39
+
40
+ [tool.hatch.build.targets.wheel]
41
+ packages = ["src/parlaibero_mcp"]
@@ -0,0 +1,2 @@
1
+ """ParlaIbero MCP server: parliamentary speeches from Latin America, Spain and Portugal for AI agents."""
2
+ __version__ = "0.2.0"