ticorates-mcp 1.0.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.
Files changed (53) hide show
  1. ticorates_mcp-1.0.0/.dockerignore +11 -0
  2. ticorates_mcp-1.0.0/.env.example +2 -0
  3. ticorates_mcp-1.0.0/.github/workflows/deploy.yml +25 -0
  4. ticorates_mcp-1.0.0/.github/workflows/publish.yml +66 -0
  5. ticorates_mcp-1.0.0/.gitignore +8 -0
  6. ticorates_mcp-1.0.0/Dockerfile +16 -0
  7. ticorates_mcp-1.0.0/LICENSE +21 -0
  8. ticorates_mcp-1.0.0/PKG-INFO +397 -0
  9. ticorates_mcp-1.0.0/README.md +377 -0
  10. ticorates_mcp-1.0.0/bruno/bccr/download-cuadros.bru +16 -0
  11. ticorates_mcp-1.0.0/bruno/bccr/get-cuadro-metadata.bru +16 -0
  12. ticorates_mcp-1.0.0/bruno/bccr/get-indicator-series.bru +17 -0
  13. ticorates_mcp-1.0.0/bruno/bruno.json +5 -0
  14. ticorates_mcp-1.0.0/bruno/environments/production.bru +3 -0
  15. ticorates_mcp-1.0.0/bruno/get-by-date.bru +16 -0
  16. ticorates_mcp-1.0.0/bruno/get-latest.bru +15 -0
  17. ticorates_mcp-1.0.0/bruno/get-range.bru +17 -0
  18. ticorates_mcp-1.0.0/docker-compose.yml +23 -0
  19. ticorates_mcp-1.0.0/mcp_server/__init__.py +0 -0
  20. ticorates_mcp-1.0.0/mcp_server/server.py +175 -0
  21. ticorates_mcp-1.0.0/pyproject.toml +38 -0
  22. ticorates_mcp-1.0.0/tests/__init__.py +0 -0
  23. ticorates_mcp-1.0.0/tests/api/__init__.py +0 -0
  24. ticorates_mcp-1.0.0/tests/api/test_routes.py +158 -0
  25. ticorates_mcp-1.0.0/tests/clients/__init__.py +0 -0
  26. ticorates_mcp-1.0.0/tests/clients/test_bccr_client.py +199 -0
  27. ticorates_mcp-1.0.0/tests/mcp/__init__.py +0 -0
  28. ticorates_mcp-1.0.0/tests/mcp/test_server.py +279 -0
  29. ticorates_mcp-1.0.0/tests/repository/__init__.py +0 -0
  30. ticorates_mcp-1.0.0/tests/repository/test_rates_repository.py +101 -0
  31. ticorates_mcp-1.0.0/tests/services/__init__.py +0 -0
  32. ticorates_mcp-1.0.0/tests/services/test_rates_service.py +202 -0
  33. ticorates_mcp-1.0.0/ticorates/__init__.py +0 -0
  34. ticorates_mcp-1.0.0/ticorates/api/__init__.py +0 -0
  35. ticorates_mcp-1.0.0/ticorates/api/routes.py +41 -0
  36. ticorates_mcp-1.0.0/ticorates/clients/__init__.py +0 -0
  37. ticorates_mcp-1.0.0/ticorates/clients/bccr_client.py +156 -0
  38. ticorates_mcp-1.0.0/ticorates/clients/indicators.json +260 -0
  39. ticorates_mcp-1.0.0/ticorates/clients/indicators.py +21 -0
  40. ticorates_mcp-1.0.0/ticorates/core/__init__.py +0 -0
  41. ticorates_mcp-1.0.0/ticorates/core/config.py +11 -0
  42. ticorates_mcp-1.0.0/ticorates/core/database.py +14 -0
  43. ticorates_mcp-1.0.0/ticorates/core/dependencies.py +21 -0
  44. ticorates_mcp-1.0.0/ticorates/core/exceptions.py +8 -0
  45. ticorates_mcp-1.0.0/ticorates/main.py +50 -0
  46. ticorates_mcp-1.0.0/ticorates/models/__init__.py +0 -0
  47. ticorates_mcp-1.0.0/ticorates/models/database.py +12 -0
  48. ticorates_mcp-1.0.0/ticorates/models/domain.py +12 -0
  49. ticorates_mcp-1.0.0/ticorates/repository/__init__.py +0 -0
  50. ticorates_mcp-1.0.0/ticorates/repository/rates_repository.py +59 -0
  51. ticorates_mcp-1.0.0/ticorates/services/__init__.py +0 -0
  52. ticorates_mcp-1.0.0/ticorates/services/rates_service.py +80 -0
  53. ticorates_mcp-1.0.0/uv.lock +778 -0
@@ -0,0 +1,11 @@
1
+ .venv/
2
+ .git/
3
+ .github/
4
+ tests/
5
+ __pycache__/
6
+ .pytest_cache/
7
+ *.pyc
8
+ *.db
9
+ .env
10
+ .DS_Store
11
+ bruno/
@@ -0,0 +1,2 @@
1
+ BCCR_API_KEY=your_token_here
2
+ BCCR_BASE_URL=https://apim.bccr.fi.cr/SDDE/api/Bccr.GE.SDDE.Publico.Indicadores.API
@@ -0,0 +1,25 @@
1
+ name: Deploy to Raspberry Pi
2
+
3
+ on:
4
+ workflow_run:
5
+ workflows: ["Publish Docker image"]
6
+ types:
7
+ - completed
8
+ workflow_dispatch:
9
+
10
+ jobs:
11
+ deploy:
12
+ runs-on: ubuntu-latest
13
+ if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
14
+ steps:
15
+ - name: Deploy to Raspberry Pi
16
+ uses: appleboy/ssh-action@v1
17
+ with:
18
+ host: ${{ secrets.PI_HOST }}
19
+ port: ${{ secrets.PI_SSH_PORT }}
20
+ username: ${{ secrets.PI_USER }}
21
+ key: ${{ secrets.PI_SSH_KEY }}
22
+ script: |
23
+ docker compose -f /home/jonach/docker-composers/ticorates/docker-compose.yml pull
24
+ docker compose -f /home/jonach/docker-composers/ticorates/docker-compose.yml up -d
25
+ docker image prune -f
@@ -0,0 +1,66 @@
1
+ name: Publish Docker image
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*.*.*"
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+
14
+ - name: Set up uv
15
+ uses: astral-sh/setup-uv@v5
16
+
17
+ - name: Run tests
18
+ run: |
19
+ uv sync --extra dev
20
+ uv run pytest tests/
21
+
22
+ publish-pypi:
23
+ needs: test
24
+ runs-on: ubuntu-latest
25
+ environment:
26
+ name: pypi
27
+ url: https://pypi.org/p/ticorates-mcp
28
+ permissions:
29
+ id-token: write
30
+ steps:
31
+ - uses: actions/checkout@v4
32
+
33
+ - name: Set up uv
34
+ uses: astral-sh/setup-uv@v5
35
+
36
+ - name: Build package
37
+ run: uv build
38
+
39
+ - name: Publish to PyPI
40
+ uses: pypa/gh-action-pypi-publish@release/v1
41
+
42
+ publish:
43
+ needs: test
44
+ runs-on: ubuntu-latest
45
+ steps:
46
+ - uses: actions/checkout@v4
47
+
48
+ - name: Log in to Docker Hub
49
+ uses: docker/login-action@v3
50
+ with:
51
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
52
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
53
+
54
+ - name: Set up Docker Buildx
55
+ uses: docker/setup-buildx-action@v3
56
+
57
+ - name: Build and push
58
+ uses: docker/build-push-action@v6
59
+ with:
60
+ context: .
61
+ push: true
62
+ platforms: linux/amd64,linux/arm64
63
+ tags: |
64
+ jonach1998/ticorates:latest
65
+ jonach1998/ticorates:${{ github.ref_name }}
66
+
@@ -0,0 +1,8 @@
1
+ .env
2
+ __pycache__/
3
+ .pytest_cache/
4
+ *.pyc
5
+ .venv/
6
+ *.db
7
+ .DS_Store
8
+ bruno/environments/local.bru
@@ -0,0 +1,16 @@
1
+ FROM python:3.13-slim
2
+
3
+ WORKDIR /app
4
+
5
+ RUN apt-get update && apt-get install -y --no-install-recommends curl tzdata && rm -rf /var/lib/apt/lists/*
6
+
7
+ COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
8
+
9
+ COPY pyproject.toml uv.lock README.md ./
10
+ RUN uv sync --no-dev --frozen
11
+
12
+ COPY . .
13
+
14
+ EXPOSE 8000
15
+
16
+ CMD ["uv", "run", "uvicorn", "ticorates.main:app", "--host", "0.0.0.0", "--port", "8000"]
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jonathan Chavarria
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,397 @@
1
+ Metadata-Version: 2.4
2
+ Name: ticorates-mcp
3
+ Version: 1.0.0
4
+ Summary: Public exchange rate API for Costa Rica, powered by BCCR
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.13
8
+ Requires-Dist: fastapi>=0.115.0
9
+ Requires-Dist: httpx>=0.28.0
10
+ Requires-Dist: mcp>=1.0.0
11
+ Requires-Dist: pydantic-settings>=2.6.0
12
+ Requires-Dist: sqlmodel>=0.0.22
13
+ Requires-Dist: uvicorn>=0.32.0
14
+ Provides-Extra: dev
15
+ Requires-Dist: httpx>=0.28.0; extra == 'dev'
16
+ Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
17
+ Requires-Dist: pytest-env>=1.0.0; extra == 'dev'
18
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
19
+ Description-Content-Type: text/markdown
20
+
21
+ # TicoRates
22
+
23
+ Public exchange rate API for Costa Rica, powered by [BCCR](https://www.bccr.fi.cr/) (Banco Central de Costa Rica). Supports 43 currencies with on-demand caching and a built-in MCP server for AI assistants.
24
+
25
+ - **REST API** — Simple HTTP endpoints, no authentication required
26
+ - **43 currencies** — USD, EUR, GBP, JPY, CAD, AUD, CHF, and more
27
+ - **On-demand caching** — rates are fetched from BCCR on first request and served instantly after
28
+ - **Historical rates** — query any date or date range going back years
29
+ - **MCP server** — native integration with Claude, Cursor, Windsurf, and other AI tools
30
+ - **Self-hosted** — Docker image available for `linux/amd64` and `linux/arm64`
31
+
32
+ ---
33
+
34
+ ## Table of Contents
35
+
36
+ - [API Reference](#api-reference)
37
+ - [MCP Server](#mcp-server)
38
+ - [Self-Hosting](#self-hosting)
39
+ - [Development](#development)
40
+
41
+ ---
42
+
43
+ ## API Reference
44
+
45
+ **Base URL:** `https://ticorates.dev`
46
+ **Interactive docs:** `https://ticorates.dev/docs`
47
+
48
+ ---
49
+
50
+ ### `GET /currencies`
51
+
52
+ Returns all supported currency codes and their full names.
53
+
54
+ ```
55
+ GET /currencies
56
+ ```
57
+
58
+ **Response**
59
+
60
+ ```json
61
+ {
62
+ "USD": "United States Dollar",
63
+ "EUR": "Euro (European Union)",
64
+ "GBP": "British Pound Sterling (United Kingdom)",
65
+ "JPY": "Japanese Yen (Japan)"
66
+ }
67
+ ```
68
+
69
+ ---
70
+
71
+ ### `GET /rates/latest`
72
+
73
+ Returns today's exchange rates from BCCR. Omit `currency` to get all 43 currencies at once.
74
+
75
+ | Parameter | Type | Required | Description |
76
+ |------------|--------|----------|---------------------------------------|
77
+ | `currency` | string | No | Filter to a single code (e.g. `USD`) |
78
+
79
+ ```
80
+ GET /rates/latest
81
+ GET /rates/latest?currency=USD
82
+ ```
83
+
84
+ **Response**
85
+
86
+ ```json
87
+ {
88
+ "date": "2025-05-27",
89
+ "rates": {
90
+ "USD": {
91
+ "purchase": 512.50,
92
+ "sale": 519.75,
93
+ "description": "United States Dollar"
94
+ }
95
+ }
96
+ }
97
+ ```
98
+
99
+ ---
100
+
101
+ ### `GET /rates`
102
+
103
+ Returns rates for a specific date or date range. Provide either `date` or both `from` + `to`.
104
+
105
+ | Parameter | Type | Required | Description |
106
+ |------------|--------|----------|-----------------------------------------|
107
+ | `date` | string | No* | Single date in `YYYY-MM-DD` format |
108
+ | `from` | string | No* | Start of range in `YYYY-MM-DD` format |
109
+ | `to` | string | No* | End of range in `YYYY-MM-DD` format |
110
+ | `currency` | string | No | Filter to a single code (e.g. `EUR`) |
111
+
112
+ *Either `date` or both `from` + `to` must be provided.
113
+
114
+ ```
115
+ GET /rates?date=2025-01-15
116
+ GET /rates?date=2025-01-15&currency=EUR
117
+ GET /rates?from=2025-01-01&to=2025-01-31
118
+ GET /rates?from=2025-01-01&to=2025-01-31&currency=USD
119
+ ```
120
+
121
+ A single-date request returns an object. A date-range request returns an array sorted by date.
122
+
123
+ **Response — single date**
124
+
125
+ ```json
126
+ {
127
+ "date": "2025-01-15",
128
+ "rates": {
129
+ "USD": { "purchase": 510.25, "sale": 517.50, "description": "United States Dollar" },
130
+ "EUR": { "purchase": 552.00, "sale": 560.30, "description": "Euro (European Union)" }
131
+ }
132
+ }
133
+ ```
134
+
135
+ **Response — date range**
136
+
137
+ ```json
138
+ [
139
+ {
140
+ "date": "2025-01-01",
141
+ "rates": { "USD": { "purchase": 508.00, "sale": 515.00, "description": "United States Dollar" } }
142
+ },
143
+ {
144
+ "date": "2025-01-02",
145
+ "rates": { "USD": { "purchase": 509.50, "sale": 516.75, "description": "United States Dollar" } }
146
+ }
147
+ ]
148
+ ```
149
+
150
+ ---
151
+
152
+ ### `GET /health`
153
+
154
+ Health check endpoint. Returns `200 OK` when the service is running.
155
+
156
+ ```json
157
+ { "status": "ok" }
158
+ ```
159
+
160
+ ---
161
+
162
+ ### Error responses
163
+
164
+ | Status | Condition |
165
+ |--------|-----------------------------------------------------|
166
+ | `400` | Missing required parameters or unsupported currency |
167
+ | `422` | Invalid date format (must be `YYYY-MM-DD`) |
168
+ | `502` | BCCR upstream error |
169
+
170
+ ---
171
+
172
+ ## MCP Server
173
+
174
+ TicoRates includes an [MCP](https://modelcontextprotocol.io/) server that gives AI assistants direct access to Costa Rican exchange rates. No API key required — it connects to `https://ticorates.dev` by default.
175
+
176
+ ### Available tools
177
+
178
+ | Tool | Description |
179
+ |----------------------------|--------------------------------------------------|
180
+ | `get_supported_currencies` | List all available currency codes and names |
181
+ | `get_latest_rates` | Today's rates for one or all currencies |
182
+ | `get_rates_for_date` | Historical rates for a specific date |
183
+ | `get_rates_for_date_range` | Rates for a date range, one entry per day |
184
+ | `convert_amount` | Convert between any two currencies |
185
+ | `get_rate_change` | Absolute and percentage change between two dates |
186
+ | `get_historical_average` | Average purchase/sale rate over a period |
187
+
188
+ ---
189
+
190
+ ### Setup
191
+
192
+ The same config block works across all MCP-compatible clients. Pick yours below.
193
+
194
+ #### Claude Desktop
195
+
196
+ Edit `claude_desktop_config.json` and restart Claude Desktop.
197
+
198
+ - **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
199
+ - **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
200
+
201
+ ```json
202
+ {
203
+ "mcpServers": {
204
+ "ticorates": {
205
+ "command": "uvx",
206
+ "args": ["ticorates-mcp"]
207
+ }
208
+ }
209
+ }
210
+ ```
211
+
212
+ ---
213
+
214
+ #### Claude Code
215
+
216
+ Run once in your terminal:
217
+
218
+ ```bash
219
+ claude mcp add ticorates -- uvx ticorates-mcp
220
+ ```
221
+
222
+ Or add it manually to `~/.claude/settings.json`:
223
+
224
+ ```json
225
+ {
226
+ "mcpServers": {
227
+ "ticorates": {
228
+ "command": "uvx",
229
+ "args": ["ticorates-mcp"]
230
+ }
231
+ }
232
+ }
233
+ ```
234
+
235
+ ---
236
+
237
+ #### Cursor
238
+
239
+ Add to `~/.cursor/mcp.json`:
240
+
241
+ ```json
242
+ {
243
+ "mcpServers": {
244
+ "ticorates": {
245
+ "command": "uvx",
246
+ "args": ["ticorates-mcp"]
247
+ }
248
+ }
249
+ }
250
+ ```
251
+
252
+ Or go to **Settings → Cursor Settings → Features → MCP → Add MCP Server**.
253
+
254
+ ---
255
+
256
+ #### Windsurf
257
+
258
+ Open the Cascade panel → **⚙ Settings → MCP Servers → Add**, then enter:
259
+
260
+ - **Command:** `uvx`
261
+ - **Args:** `ticorates-mcp`
262
+
263
+ ---
264
+
265
+ #### OpenAI Codex CLI
266
+
267
+ Run once in your terminal:
268
+
269
+ ```bash
270
+ codex mcp add ticorates -- uvx ticorates-mcp
271
+ ```
272
+
273
+ Or add it manually to `~/.codex/config.toml` (global) or `.codex/config.toml` (project-scoped):
274
+
275
+ ```toml
276
+ [mcp_servers.ticorates]
277
+ command = "uvx"
278
+ args = ["ticorates-mcp"]
279
+ ```
280
+
281
+ ---
282
+
283
+ #### Other clients
284
+
285
+ Any MCP-compatible client that supports `stdio` transport works with TicoRates. Use the same config structure shown above.
286
+
287
+ ---
288
+
289
+ ### Pointing the MCP at a self-hosted instance
290
+
291
+ By default, `ticorates-mcp` connects to `https://ticorates.dev`. If you're running your own instance, override it with the `TICORATES_BASE_URL` environment variable in your client's config.
292
+
293
+ **Claude Desktop / Claude Code / Cursor / Windsurf** (`JSON`):
294
+
295
+ ```json
296
+ {
297
+ "mcpServers": {
298
+ "ticorates": {
299
+ "command": "uvx",
300
+ "args": ["ticorates-mcp"],
301
+ "env": {
302
+ "TICORATES_BASE_URL": "http://your-server:8000"
303
+ }
304
+ }
305
+ }
306
+ }
307
+ ```
308
+
309
+ **OpenAI Codex CLI** (`config.toml`):
310
+
311
+ ```toml
312
+ [mcp_servers.ticorates]
313
+ command = "uvx"
314
+ args = ["ticorates-mcp"]
315
+ env = { TICORATES_BASE_URL = "http://your-server:8000" }
316
+ ```
317
+
318
+ Or via CLI:
319
+
320
+ ```bash
321
+ codex mcp add ticorates --env TICORATES_BASE_URL=http://your-server:8000 -- uvx ticorates-mcp
322
+ ```
323
+
324
+ ---
325
+
326
+ ## Self-Hosting
327
+
328
+ ### Prerequisites
329
+
330
+ - Docker and Docker Compose
331
+ - A BCCR API key — register at the [BCCR developer portal](https://www.bccr.fi.cr/)
332
+
333
+ ### Setup
334
+
335
+ **1. Create a `docker-compose.yml`:**
336
+
337
+ ```yaml
338
+ services:
339
+ ticorates:
340
+ image: jonach1998/ticorates:latest
341
+ ports:
342
+ - "8000:8000"
343
+ env_file: .env
344
+ volumes:
345
+ - ticorates_data:/app/ticorates.db
346
+ restart: always
347
+ healthcheck:
348
+ test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
349
+ interval: 30s
350
+ timeout: 5s
351
+ retries: 3
352
+ start_period: 10s
353
+
354
+ volumes:
355
+ ticorates_data:
356
+ ```
357
+
358
+ **2. Create a `.env` file:**
359
+
360
+ ```env
361
+ BCCR_API_KEY=your_token_here
362
+ BCCR_BASE_URL=https://apim.bccr.fi.cr/SDDE/api/Bccr.GE.SDDE.Publico.Indicadores.API
363
+ ```
364
+
365
+ **3. Start the service:**
366
+
367
+ ```bash
368
+ docker compose up -d
369
+ ```
370
+
371
+ The API will be available at `http://localhost:8000`.
372
+ Interactive docs at `http://localhost:8000/docs`.
373
+
374
+ ---
375
+
376
+ ## Development
377
+
378
+ **Requirements:** Python 3.13+, [uv](https://docs.astral.sh/uv/)
379
+
380
+ ```bash
381
+ # Clone the repo and install dependencies
382
+ git clone https://github.com/jonach1998/ticorates
383
+ cd ticorates
384
+ uv sync --extra dev
385
+
386
+ # Copy and fill in your BCCR credentials
387
+ cp .env.example .env
388
+
389
+ # Run the API
390
+ uv run uvicorn ticorates.main:app --reload
391
+
392
+ # Run the MCP server
393
+ uv run ticorates-mcp
394
+
395
+ # Run tests
396
+ uv run pytest
397
+ ```