uruguay-mcp 0.1.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.
- uruguay_mcp-0.1.0/.gitignore +28 -0
- uruguay_mcp-0.1.0/EXAMPLES.md +282 -0
- uruguay_mcp-0.1.0/LICENSE +21 -0
- uruguay_mcp-0.1.0/PKG-INFO +206 -0
- uruguay_mcp-0.1.0/README.es.md +182 -0
- uruguay_mcp-0.1.0/README.md +180 -0
- uruguay_mcp-0.1.0/pyproject.toml +65 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/__init__.py +3 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/__main__.py +4 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/cli.py +164 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/meta/__init__.py +1 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/meta/search.py +53 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/meta/tools.py +124 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/__init__.py +29 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/bcu/__init__.py +27 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/bcu/client.py +134 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/bcu/constants.py +27 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/bcu/prompts.py +57 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/bcu/resources.py +53 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/bcu/schemas.py +57 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/bcu/tools.py +137 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/catalogodatos/__init__.py +26 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/catalogodatos/client.py +49 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/catalogodatos/constants.py +17 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/catalogodatos/prompts.py +59 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/catalogodatos/resources.py +60 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/catalogodatos/schemas.py +42 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/catalogodatos/tools.py +159 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/datastore/__init__.py +27 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/datastore/client.py +255 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/datastore/constants.py +31 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/datastore/prompts.py +42 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/datastore/resources.py +46 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/datastore/schemas.py +44 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/datastore/tools.py +73 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/gubuy/__init__.py +25 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/gubuy/client.py +46 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/gubuy/constants.py +29 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/gubuy/prompts.py +71 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/gubuy/resources.py +67 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/gubuy/schemas.py +38 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/gubuy/tools.py +190 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/ine/__init__.py +26 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/ine/client.py +68 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/ine/constants.py +31 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/ine/prompts.py +68 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/ine/resources.py +68 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/ine/schemas.py +45 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/ine/tools.py +211 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/montevideo/__init__.py +28 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/montevideo/client.py +148 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/montevideo/constants.py +48 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/montevideo/prompts.py +83 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/montevideo/resources.py +77 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/montevideo/schemas.py +102 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/modules/montevideo/tools.py +412 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/server.py +74 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/shared/__init__.py +1 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/shared/cache.py +47 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/shared/config.py +40 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/shared/envelope.py +48 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/shared/errors.py +50 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/shared/http.py +85 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/shared/i18n.py +36 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/shared/rate_limiter.py +43 -0
- uruguay_mcp-0.1.0/src/uruguay_mcp/shared/registry.py +223 -0
- uruguay_mcp-0.1.0/tests/__init__.py +0 -0
- uruguay_mcp-0.1.0/tests/test_bcu.py +228 -0
- uruguay_mcp-0.1.0/tests/test_catalogodatos.py +114 -0
- uruguay_mcp-0.1.0/tests/test_cli.py +97 -0
- uruguay_mcp-0.1.0/tests/test_datastore.py +138 -0
- uruguay_mcp-0.1.0/tests/test_gubuy.py +198 -0
- uruguay_mcp-0.1.0/tests/test_ine.py +212 -0
- uruguay_mcp-0.1.0/tests/test_meta.py +54 -0
- uruguay_mcp-0.1.0/tests/test_montevideo.py +294 -0
- uruguay_mcp-0.1.0/tests/test_server.py +70 -0
- uruguay_mcp-0.1.0/uv.lock +2055 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
.venv/
|
|
5
|
+
venv/
|
|
6
|
+
*.egg-info/
|
|
7
|
+
dist/
|
|
8
|
+
build/
|
|
9
|
+
|
|
10
|
+
# uv
|
|
11
|
+
.uv/
|
|
12
|
+
|
|
13
|
+
# Tooling
|
|
14
|
+
.pytest_cache/
|
|
15
|
+
.ruff_cache/
|
|
16
|
+
.coverage
|
|
17
|
+
htmlcov/
|
|
18
|
+
.pyright/
|
|
19
|
+
|
|
20
|
+
# Local data / cache
|
|
21
|
+
*.sqlite
|
|
22
|
+
*.db
|
|
23
|
+
.cache/
|
|
24
|
+
|
|
25
|
+
# Editors / OS
|
|
26
|
+
.DS_Store
|
|
27
|
+
.idea/
|
|
28
|
+
.vscode/
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
# Ejemplos de uso de uruguay_mcp
|
|
2
|
+
|
|
3
|
+
`uruguay_mcp` expone solo **5 meta-herramientas**. Todo dato concreto se alcanza a
|
|
4
|
+
travΓ©s de ellas: primero `discover_tools` (o `plan_query`) para encontrar la
|
|
5
|
+
herramienta adecuada y su esquema, luego `call_tool` para ejecutarla, o
|
|
6
|
+
`execute_batch` para correr varias en paralelo.
|
|
7
|
+
|
|
8
|
+
Meta-tools:
|
|
9
|
+
|
|
10
|
+
- `list_modules()` β mΓ³dulos disponibles y cuΓ‘ntas tools ofrece cada uno.
|
|
11
|
+
- `discover_tools(query, module=None, limit=8)` β busca tools por necesidad.
|
|
12
|
+
- `plan_query(goal)` β candidatos de varias fuentes para un objetivo amplio.
|
|
13
|
+
- `call_tool(name, arguments)` β ejecuta una tool por nombre.
|
|
14
|
+
- `execute_batch(calls)` β ejecuta `[{name, arguments}, ...]` en paralelo.
|
|
15
|
+
|
|
16
|
+
MΓ³dulos cargados por defecto: `catalogodatos`, `bcu`, `ine`, `gubuy`,
|
|
17
|
+
`montevideo`, `datastore` (31 data tools). Los nombres de tools usados abajo son
|
|
18
|
+
los reales registrados en cada mΓ³dulo.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 1. CotizaciΓ³n del dΓ³lar (BCU)
|
|
23
|
+
|
|
24
|
+
**Objetivo:** ΒΏA cuΓ‘nto cerrΓ³ el dΓ³lar en el ΓΊltimo dΓa hΓ‘bil?
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
discover_tools(query="cotizaciΓ³n del dΓ³lar BCU", module="bcu")
|
|
28
|
+
-> sugiere bcu_cotizacion_usd
|
|
29
|
+
|
|
30
|
+
call_tool("bcu_cotizacion_usd", {})
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Sin `fecha`, usa el ΓΊltimo cierre. Devuelve `compra` (TCC) y `venta` (TCV) del
|
|
34
|
+
dΓ³lar billete (moneda 2225) envueltos en el sobre estΓ‘ndar (`data`, `api`,
|
|
35
|
+
`url`, `cached`).
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 2. Serie de tipos de cambio en un rango (BCU)
|
|
40
|
+
|
|
41
|
+
**Objetivo:** Tipo de cambio del dΓ³lar y el real entre dos fechas.
|
|
42
|
+
|
|
43
|
+
```text
|
|
44
|
+
call_tool("bcu_listar_monedas", {"grupo": 2})
|
|
45
|
+
-> tomar los cΓ³digos (ej. 2225 = dΓ³lar billete, 1001 = real)
|
|
46
|
+
|
|
47
|
+
call_tool("bcu_cotizaciones", {
|
|
48
|
+
"monedas": [2225, 1001],
|
|
49
|
+
"fecha_desde": "2025-01-02",
|
|
50
|
+
"fecha_hasta": "2025-01-31",
|
|
51
|
+
"grupo": 2
|
|
52
|
+
})
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Primero `bcu_listar_monedas` para conocer los cΓ³digos numΓ©ricos, luego
|
|
56
|
+
`bcu_cotizaciones` con el rango. Devuelve una fila por moneda y fecha con
|
|
57
|
+
`compra`/`venta`/`arbitraje`.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 3. Buscar y abrir un dataset del catΓ‘logo nacional (catalogodatos)
|
|
62
|
+
|
|
63
|
+
**Objetivo:** Encontrar datos abiertos sobre presupuesto pΓΊblico.
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
discover_tools(query="datasets de presupuesto datos abiertos")
|
|
67
|
+
-> catalogo_search_datasets
|
|
68
|
+
|
|
69
|
+
call_tool("catalogo_search_datasets", {"query": "presupuesto", "rows": 10})
|
|
70
|
+
-> elegir un dataset y tomar su 'name'/'id'
|
|
71
|
+
|
|
72
|
+
call_tool("catalogo_get_dataset", {"id": "presupuesto-nacional"})
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`catalogo_get_dataset` devuelve los `resources`; quedate con el `resource_id`
|
|
76
|
+
de uno que tenga datastore activo para el ejemplo 4.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 4. Consultar registros tabulares de un recurso CKAN (catalogodatos)
|
|
81
|
+
|
|
82
|
+
**Objetivo:** Leer filas de un recurso con datastore, filtrando por texto.
|
|
83
|
+
|
|
84
|
+
```text
|
|
85
|
+
call_tool("catalogo_query_datastore", {
|
|
86
|
+
"resource_id": "a1b2c3d4-0000-1111-2222-333344445555",
|
|
87
|
+
"query": "Montevideo",
|
|
88
|
+
"limit": 50,
|
|
89
|
+
"offset": 0
|
|
90
|
+
})
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
`resource_id` sale del paso 3 (`catalogo_get_dataset`). Solo funciona sobre
|
|
94
|
+
recursos con datastore activo; si no lo tienen, descargΓ‘ el archivo desde su
|
|
95
|
+
`url`.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## 5. Estudios estadΓsticos del INE (INE / ANDA)
|
|
100
|
+
|
|
101
|
+
**Objetivo:** Hallar el estudio del censo y abrir su ficha.
|
|
102
|
+
|
|
103
|
+
```text
|
|
104
|
+
discover_tools(query="censo de poblaciΓ³n INE", module="ine")
|
|
105
|
+
-> ine_search_studies
|
|
106
|
+
|
|
107
|
+
call_tool("ine_search_studies", {
|
|
108
|
+
"query": "censo", "rows": 10, "sort_by": "year", "sort_order": "desc"
|
|
109
|
+
})
|
|
110
|
+
-> copiar el 'idno' ANDA (NO el id numΓ©rico)
|
|
111
|
+
|
|
112
|
+
call_tool("ine_get_study", {"idno": "URY-INE-CENSO-2023-v01"})
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Ojo: `ine_get_study` requiere el `idno` (cadena ANDA), no el id numΓ©rico que
|
|
116
|
+
aparece en la bΓΊsqueda.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## 6. Datasets CKAN del INE
|
|
121
|
+
|
|
122
|
+
**Objetivo:** Listar los datasets de datos abiertos publicados por el INE.
|
|
123
|
+
|
|
124
|
+
```text
|
|
125
|
+
call_tool("ine_list_ckan_datasets", {"query": "precios", "rows": 20})
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Devuelve los datasets CKAN del INE (complementa a los estudios ANDA del
|
|
129
|
+
ejemplo 5). Para leer las filas de uno de sus recursos, seguΓ con
|
|
130
|
+
`catalogo_query_datastore` usando el `resource_id` correspondiente.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 7. Servicios y APIs de gub.uy (gubuy)
|
|
135
|
+
|
|
136
|
+
**Objetivo:** Ver quΓ© servicios/APIs de gobierno existen sobre transporte.
|
|
137
|
+
|
|
138
|
+
```text
|
|
139
|
+
discover_tools(query="APIs de gobierno transporte gub.uy", module="gubuy")
|
|
140
|
+
-> gubuy_search_apis, gubuy_list_servicios
|
|
141
|
+
|
|
142
|
+
execute_batch([
|
|
143
|
+
{"name": "gubuy_search_apis", "arguments": {"query": "transporte", "rows": 10}},
|
|
144
|
+
{"name": "gubuy_list_servicios", "arguments": {"query": "transporte", "limit": 10}}
|
|
145
|
+
])
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
`execute_batch` corre ambas en paralelo con aislamiento de errores. TomΓ‘ un
|
|
149
|
+
`id`/`showcase_id` y profundizΓ‘ con `gubuy_get_servicio` o
|
|
150
|
+
`gubuy_servicio_datasets`.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## 8. PrΓ³ximo Γ³mnibus en una parada (montevideo, transporte)
|
|
155
|
+
|
|
156
|
+
**Objetivo:** ΒΏCuΓ‘ndo llega el 103 a una parada?
|
|
157
|
+
|
|
158
|
+
```text
|
|
159
|
+
discover_tools(query="cuΓ‘ndo llega el prΓ³ximo bus a una parada", module="montevideo")
|
|
160
|
+
-> montevideo_list_busstops, montevideo_busstop_lines, montevideo_bus_eta
|
|
161
|
+
|
|
162
|
+
call_tool("montevideo_list_busstops", {"query": "18 de Julio", "limit": 20})
|
|
163
|
+
-> tomar el busstop_id
|
|
164
|
+
|
|
165
|
+
call_tool("montevideo_busstop_lines", {"busstop_id": 4567})
|
|
166
|
+
-> confirmar quΓ© lΓneas pasan
|
|
167
|
+
|
|
168
|
+
call_tool("montevideo_bus_eta", {"busstop_id": 4567, "lines": ["103"], "amount_per_line": 2})
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
`montevideo_bus_eta` exige `busstop_id` y al menos una lΓnea. La unidad de
|
|
172
|
+
`eta` no estΓ‘ documentada (segundos o minutos): se devuelve sin transformar.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## 9. Buses cerca de un punto en tiempo real (montevideo, transporte)
|
|
177
|
+
|
|
178
|
+
**Objetivo:** Ver buses circulando cerca de una ubicaciΓ³n.
|
|
179
|
+
|
|
180
|
+
```text
|
|
181
|
+
call_tool("montevideo_buses_near", {
|
|
182
|
+
"lat": -34.9011, "lng": -56.1645, "radius_m": 500
|
|
183
|
+
})
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Devuelve posiciones GPS en tiempo real dentro del radio. Alternativa filtrada:
|
|
187
|
+
`montevideo_bus_positions` por `lines`, `company` o `busstop_id`.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## 10. CRUZADO β Transporte + multas de trΓ‘nsito de Montevideo
|
|
192
|
+
|
|
193
|
+
**Objetivo:** Para un punto del centro, mostrar quΓ© buses pasan cerca y, de
|
|
194
|
+
paso, el panorama de multas de trΓ‘nsito (SUCIVE) de ese aΓ±o.
|
|
195
|
+
|
|
196
|
+
```text
|
|
197
|
+
discover_tools(query="buses cerca y multas de trΓ‘nsito Montevideo", module="montevideo")
|
|
198
|
+
-> montevideo_buses_near, montevideo_multas_transito
|
|
199
|
+
|
|
200
|
+
execute_batch([
|
|
201
|
+
{"name": "montevideo_buses_near",
|
|
202
|
+
"arguments": {"lat": -34.9061, "lng": -56.1914, "radius_m": 400}},
|
|
203
|
+
{"name": "montevideo_multas_transito",
|
|
204
|
+
"arguments": {"year": 2018}}
|
|
205
|
+
])
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Dos fuentes distintas dentro del mismo mΓ³dulo, resueltas en paralelo:
|
|
209
|
+
tiempo real de transporte + el Γndice estadΓstico de multas. `multas_transito`
|
|
210
|
+
devuelve archivos anuales descargables y tablas de referencia (ordenanzas,
|
|
211
|
+
tipos de vehΓculo): son datos AGREGADOS, no una consulta de deuda por
|
|
212
|
+
matrΓcula.
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## 11. CRUZADO β BCU + dataset del catΓ‘logo (tipo de cambio para contextualizar)
|
|
217
|
+
|
|
218
|
+
**Objetivo:** Cruzar la cotizaciΓ³n oficial del dΓ³lar con un dataset de precios
|
|
219
|
+
o presupuesto del catΓ‘logo nacional para anΓ‘lisis monetario.
|
|
220
|
+
|
|
221
|
+
```text
|
|
222
|
+
plan_query(goal="contextualizar precios en pesos con el tipo de cambio del dΓ³lar")
|
|
223
|
+
-> candidatos: bcu_cotizacion_usd, catalogo_search_datasets, catalogo_query_datastore
|
|
224
|
+
|
|
225
|
+
execute_batch([
|
|
226
|
+
{"name": "bcu_cotizacion_usd", "arguments": {}},
|
|
227
|
+
{"name": "catalogo_search_datasets","arguments": {"query": "Γndice de precios", "rows": 5}}
|
|
228
|
+
])
|
|
229
|
+
-> con un resource_id del resultado del catΓ‘logo:
|
|
230
|
+
|
|
231
|
+
call_tool("catalogo_query_datastore", {
|
|
232
|
+
"resource_id": "ipc-0000-1111-2222-333344445555",
|
|
233
|
+
"limit": 100
|
|
234
|
+
})
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
`plan_query` propone tools de **dos mΓ³dulos** (`bcu` + `catalogodatos`). Se trae
|
|
238
|
+
el dΓ³lar y el dataset en paralelo con `execute_batch`, y luego se leen las filas
|
|
239
|
+
del recurso para convertir/contextualizar montos.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## 12. CRUZADO β Dos tablas CKAN para un JOIN lΓ³gico
|
|
244
|
+
|
|
245
|
+
**Objetivo:** Combinar dos recursos CKAN (uno del catΓ‘logo nacional, otro del
|
|
246
|
+
portal de Montevideo) para relacionar registros β un JOIN hecho del lado del
|
|
247
|
+
cliente con los resultados de dos datastores.
|
|
248
|
+
|
|
249
|
+
```text
|
|
250
|
+
discover_tools(query="consultar registros de un recurso CKAN", limit=8)
|
|
251
|
+
-> catalogo_query_datastore, montevideo_query_datastore
|
|
252
|
+
|
|
253
|
+
# Tabla A: recurso del catΓ‘logo nacional
|
|
254
|
+
call_tool("catalogo_query_datastore", {
|
|
255
|
+
"resource_id": "padron-organismos-0000-1111-2222-3333",
|
|
256
|
+
"limit": 500
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
# Tabla B: recurso del portal de Montevideo
|
|
260
|
+
call_tool("montevideo_query_datastore", {
|
|
261
|
+
"resource_id": "arbolado-publico-aaaa-bbbb-cccc-dddd",
|
|
262
|
+
"limit": 500
|
|
263
|
+
})
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Cada `*_query_datastore` devuelve registros de su portal CKAN respectivo;
|
|
267
|
+
el JOIN (por una clave comΓΊn, ej. `organismo` o `barrio`) se arma combinando
|
|
268
|
+
los dos resultados. Para empujar el JOIN a un motor SQL real usΓ‘ el mΓ³dulo
|
|
269
|
+
`datastore` (cargado por defecto): `datastore_load_ckan_resource` para subir
|
|
270
|
+
cada recurso a una tabla SQLite y `datastore_sql` para el JOIN con SELECT.
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Notas
|
|
275
|
+
|
|
276
|
+
- Toda respuesta de data tool viene en el sobre estΓ‘ndar: `data`, `api`, `url`,
|
|
277
|
+
`cached` (y `error` ante fallos).
|
|
278
|
+
- `discover_tools` devuelve el esquema de argumentos de cada candidato: leelo
|
|
279
|
+
antes de armar el `arguments` de `call_tool`.
|
|
280
|
+
- `execute_batch` aΓsla errores por llamada: una falla no aborta el resto.
|
|
281
|
+
- Los `resource_id` y `busstop_id` de los ejemplos son ilustrativos; obtenelos
|
|
282
|
+
siempre del paso de bΓΊsqueda/listado previo.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 uruguay-mcp contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: uruguay-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server for Uruguay's open government data (national catalog, BCU, INE, Montevideo, gub.uy)
|
|
5
|
+
Author: uruguay-mcp contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: bcu,ckan,ine,mcp,montevideo,open-data,uruguay
|
|
9
|
+
Requires-Python: >=3.11
|
|
10
|
+
Requires-Dist: aiocache>=0.12
|
|
11
|
+
Requires-Dist: fastmcp>=2.3.0
|
|
12
|
+
Requires-Dist: httpx>=0.27
|
|
13
|
+
Requires-Dist: pydantic-settings>=2.3
|
|
14
|
+
Requires-Dist: pydantic>=2.7
|
|
15
|
+
Requires-Dist: structlog>=24.1
|
|
16
|
+
Requires-Dist: tenacity>=8.3
|
|
17
|
+
Requires-Dist: zeep>=4.2
|
|
18
|
+
Provides-Extra: dev
|
|
19
|
+
Requires-Dist: pyright>=1.1; extra == 'dev'
|
|
20
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
21
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
22
|
+
Requires-Dist: pytest>=8.2; extra == 'dev'
|
|
23
|
+
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
24
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
<div align="center">
|
|
28
|
+
|
|
29
|
+
<img src="https://flagcdn.com/w160/uy.png" alt="Bandera de Uruguay" width="120" />
|
|
30
|
+
|
|
31
|
+
# πΊπΎ uruguay-mcp
|
|
32
|
+
|
|
33
|
+
**Structured AI-agent access to Uruguay's open government data**
|
|
34
|
+
<br>
|
|
35
|
+
*Acceso estructurado de agentes de IA a los datos abiertos del Estado uruguayo*
|
|
36
|
+
|
|
37
|
+
[](https://pypi.org/project/uruguay-mcp/)
|
|
38
|
+
[](https://www.python.org/)
|
|
39
|
+
[](https://modelcontextprotocol.io/)
|
|
40
|
+
[](#development)
|
|
41
|
+
[](#development)
|
|
42
|
+
[](LICENSE)
|
|
43
|
+
|
|
44
|
+
π **[EspaΓ±ol](README.es.md)** Β· **English**
|
|
45
|
+
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
An [MCP](https://modelcontextprotocol.io/) server that gives AI agents structured
|
|
51
|
+
access to **Uruguay's open government data** β the national data catalog, the
|
|
52
|
+
Central Bank, the statistics institute, Montevideo's city data & realtime
|
|
53
|
+
transport, and the gub.uy service catalog β behind a single **meta-discovery**
|
|
54
|
+
layer.
|
|
55
|
+
|
|
56
|
+
## β¨ Why a meta-discovery layer?
|
|
57
|
+
|
|
58
|
+
Instead of flooding the model with hundreds of tool definitions, the server
|
|
59
|
+
exposes **five meta-tools**. The model searches for what it needs, then invokes
|
|
60
|
+
the matching data tool by name. The prompt-visible surface stays constant no
|
|
61
|
+
matter how many data sources are added.
|
|
62
|
+
|
|
63
|
+
| Meta-tool | Purpose |
|
|
64
|
+
|---|---|
|
|
65
|
+
| `discover_tools(query, module?, limit?)` | Rank data tools relevant to a natural-language need (returns their argument schemas) |
|
|
66
|
+
| `call_tool(name, arguments)` | Invoke a data tool by name (validates arguments) |
|
|
67
|
+
| `list_modules()` | List data-source modules and their tool counts |
|
|
68
|
+
| `plan_query(goal)` | Surface candidate tools for a multi-step goal |
|
|
69
|
+
| `execute_batch(calls)` | Run several calls concurrently with per-call error isolation |
|
|
70
|
+
|
|
71
|
+
Every tool returns a unified envelope: `{ "_meta": { source, cached, lang, timestamp }, "data": ... }`.
|
|
72
|
+
|
|
73
|
+
> At a glance: **5 meta-tools + 31 data tools across 6 modules**, plus **17
|
|
74
|
+
> prompts** and **11 resources**.
|
|
75
|
+
|
|
76
|
+
## π Data sources (modules)
|
|
77
|
+
|
|
78
|
+
| | Module | Source | Protocol | Tools |
|
|
79
|
+
|---|---|---|---|:--:|
|
|
80
|
+
| ποΈ | `catalogodatos` | [catalogodatos.gub.uy](https://catalogodatos.gub.uy) β national CKAN catalog (~2680 datasets, 72 orgs) | CKAN REST | 5 |
|
|
81
|
+
| π΅ | `bcu` | Banco Central del Uruguay β exchange rates | SOAP (`zeep`) | 4 |
|
|
82
|
+
| π | `ine` | Instituto Nacional de EstadΓstica β ANDA / microdata | REST | 3 |
|
|
83
|
+
| π | `gubuy` | gub.uy public API / service catalog | CKAN REST | 4 |
|
|
84
|
+
| π | `montevideo` | Intendencia de Montevideo β city CKAN + realtime transport | CKAN + REST | 11 |
|
|
85
|
+
| ποΈ | `datastore` | Cross-source SQLite workspace β load CSV/CKAN data, run read-only SQL JOINs | local SQLite | 4 |
|
|
86
|
+
|
|
87
|
+
The transport surface of `montevideo` needs OAuth2 credentials
|
|
88
|
+
(`URUGUAY_MCP_MVD_CLIENT_ID` / `URUGUAY_MCP_MVD_CLIENT_SECRET`); without them the
|
|
89
|
+
transport tools return a typed `validation_error` while the CKAN tools work
|
|
90
|
+
unauthenticated.
|
|
91
|
+
|
|
92
|
+
## π§© Prompts & Resources
|
|
93
|
+
|
|
94
|
+
Each module also registers reusable **prompts** (parameterized Spanish
|
|
95
|
+
instruction templates) and **resources** (static reference docs under the
|
|
96
|
+
`uru://<module>/<path>` URI scheme), exposed natively through FastMCP.
|
|
97
|
+
|
|
98
|
+
- **17 prompts** β e.g. `bcu_cotizacion_dolar_hoy`, `catalogo_buscar_por_tema`,
|
|
99
|
+
`ine_buscar_estudios`, `montevideo_proximo_bus`, `datastore_unir_dos_fuentes`.
|
|
100
|
+
- **11 resources** β e.g. `uru://bcu/codigos-moneda`,
|
|
101
|
+
`uru://catalogodatos/guia-de-uso`, `uru://montevideo/credenciales-transporte`.
|
|
102
|
+
|
|
103
|
+
See **[EXAMPLES.md](EXAMPLES.md)** for end-to-end usage scenarios, including
|
|
104
|
+
cross-source ones via `plan_query` / `execute_batch` and SQL JOINs through the
|
|
105
|
+
`datastore` module.
|
|
106
|
+
|
|
107
|
+
## π Quick start
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# Run directly from PyPI (once published)
|
|
111
|
+
uvx uruguay-mcp
|
|
112
|
+
|
|
113
|
+
# β¦or install it
|
|
114
|
+
pip install uruguay-mcp # or: uv pip install uruguay-mcp
|
|
115
|
+
uruguay-mcp
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### One-command install into Claude
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
uruguay-mcp install
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Merges the server into Claude Desktop's config (preserving existing
|
|
125
|
+
`mcpServers` and unrelated keys) and prints a ready-to-paste snippet for Claude
|
|
126
|
+
Code / Cursor. Restart the client afterwards.
|
|
127
|
+
|
|
128
|
+
### Claude Desktop config (manual)
|
|
129
|
+
|
|
130
|
+
```json
|
|
131
|
+
{
|
|
132
|
+
"mcpServers": {
|
|
133
|
+
"uruguay-mcp": { "command": "uruguay-mcp" }
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Run options
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
uruguay-mcp # stdio (default)
|
|
142
|
+
uruguay-mcp --transport sse --port 8000
|
|
143
|
+
uruguay-mcp --modules catalogodatos,bcu # load only some modules
|
|
144
|
+
uruguay-mcp --verbose # INFO logs (--debug for DEBUG)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## βοΈ Configuration
|
|
148
|
+
|
|
149
|
+
All via `URUGUAY_MCP_*` environment variables:
|
|
150
|
+
|
|
151
|
+
| Variable | Default | Meaning |
|
|
152
|
+
|---|---|---|
|
|
153
|
+
| `URUGUAY_MCP_LANG` | `es` | Language for human-facing strings (`es`/`en`) |
|
|
154
|
+
| `URUGUAY_MCP_HTTP_TIMEOUT` | `30` | HTTP timeout (seconds) |
|
|
155
|
+
| `URUGUAY_MCP_CACHE_TTL` | `900` | Response cache TTL (seconds) |
|
|
156
|
+
| `URUGUAY_MCP_RATE_LIMIT_RPS` | `5` | Max requests/sec per host |
|
|
157
|
+
| `URUGUAY_MCP_MODULES` | _(all)_ | Comma-separated module allowlist |
|
|
158
|
+
| `URUGUAY_MCP_MVD_CLIENT_ID` | _(unset)_ | OAuth2 client id for the Montevideo transport API |
|
|
159
|
+
| `URUGUAY_MCP_MVD_CLIENT_SECRET` | _(unset)_ | OAuth2 client secret for the Montevideo transport API |
|
|
160
|
+
|
|
161
|
+
## ποΈ Architecture
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
src/uruguay_mcp/
|
|
165
|
+
βββ server.py # FastMCP wiring; meta-tools + registered prompts + resources
|
|
166
|
+
βββ cli.py # `uruguay-mcp` / `uruguay-mcp install`; -v/--debug logging
|
|
167
|
+
βββ meta/ # discovery layer
|
|
168
|
+
β βββ tools.py # the 5 meta-tools
|
|
169
|
+
β βββ search.py # BM25-lite ranking over the registry
|
|
170
|
+
βββ shared/ # reused by every module
|
|
171
|
+
β βββ config.py # env-driven settings (URUGUAY_MCP_*)
|
|
172
|
+
β βββ http.py # async client: retries (tenacity) + per-host rate limit
|
|
173
|
+
β βββ cache.py # async TTL cache
|
|
174
|
+
β βββ envelope.py # unified {_meta, data} response (+ UTC timestamp)
|
|
175
|
+
β βββ i18n.py # es/en messages
|
|
176
|
+
β βββ errors.py # typed, localized errors
|
|
177
|
+
β βββ registry.py # tool/prompt/resource registry; @tool/@prompt/@resource
|
|
178
|
+
βββ modules/ # one self-contained package per data source
|
|
179
|
+
βββ catalogodatos/ βββ bcu/ βββ ine/
|
|
180
|
+
βββ gubuy/ βββ montevideo/ βββ datastore/
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Each module package is independent (`constants` Β· `schemas` Β· `client` Β·
|
|
184
|
+
`tools` Β· optional `prompts`/`resources`). Importing the package self-registers
|
|
185
|
+
everything it offers.
|
|
186
|
+
|
|
187
|
+
## π οΈ Development
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
uv venv && uv pip install -e ".[dev]"
|
|
191
|
+
|
|
192
|
+
uv run pytest # 69 unit tests (HTTP mocked, offline) Β· 86% coverage
|
|
193
|
+
uv run pytest -m integration # hits live government APIs
|
|
194
|
+
uv run ruff check src tests
|
|
195
|
+
uv run pyright
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## π Acknowledgements
|
|
199
|
+
|
|
200
|
+
Built on data published by **AGESIC**, **BCU**, **INE** and the **Intendencia de
|
|
201
|
+
Montevideo** under Uruguay's open-data law (NΒΊ 18.381). This project is an
|
|
202
|
+
independent client and is not affiliated with those institutions.
|
|
203
|
+
|
|
204
|
+
## π License
|
|
205
|
+
|
|
206
|
+
[MIT](LICENSE)
|