Skip to main content
Glama
bolesk

crypto-transactions-mcp

by bolesk
README.md
# crypto-transactions-mcp

MCP server per tracciare transazioni e ordini eseguiti nel mercato crypto.
Espone tool MCP che permettono a un agent LLM (Claude, agy, ecc.) di
registrare trade, swap e trasferimenti con precisione `Decimal` esatta, e di
interrogare lo storico — con persistenza intercambiabile su un file JSON
locale o su un database Notion.

## Quick Start

1. `uv sync` per installare le dipendenze.
2. Scegli un backend:
   - **JSON** (default, zero config) — salta al punto 3.
   - **Notion** — segui prima la sezione ["Setup Notion da zero"](#setup-notion-da-zero) qui sotto.
3. Aggiungi il server alla configurazione del tuo host MCP: vedi
   ["Configurazione Claude Desktop"](#configurazione-claude-desktop) o
   ["Configurazione agy"](#configurazione-agy).
4. Riavvia l'host e prova, ad esempio: *"Ho comprato 0.1 BTC su Binance a
   65000 USDT, fee 0.0001 BTC, registralo con log_transaction."*

Architettura onion:

- `domain/` — entità `CryptoTransaction` (Pydantic v2, campi monetari in `Decimal`). `side` è `BUY`/`SELL` (trade, incluso uno swap crypto/crypto) oppure `TRANSFER` (spostamento dello stesso asset tra due custodie); i due casi hanno requisiti diversi validati nel modello stesso (BUY/SELL richiedono `price`/`value`, TRANSFER richiede `to_exchange`)
- `interfaces/` — port `TransactionRepository` e `TransactionFilters`
- `usecases/` — `LogTransactionUseCase` (trade, upsert con merge parziale), `LogTransferUseCase` (transfer, stessa semantica di upsert), `GetTransactionHistoryUseCase`
- `infrastructure/` — adapter concreti del port `TransactionRepository`:
  - `JsonTransactionRepository`, file JSON locale, path fisso `data/crypto_transactions.json`
  - `NotionTransactionRepository`, database Notion (vedi sotto)
- `delivery/` — server `FastMCP`, tool MCP (`log_transaction`, `log_transfer`, `get_transaction_history`), wiring delle dipendenze in `factories.py`

## Tool MCP

- **`log_transaction`** — trade BUY/SELL, incluso uno swap tra due crypto (es. ETH/BTC): stessa forma di un trade normale, non serve un tool a parte. Supporta `fiat_value`/`fiat_currency` opzionali per fissare il controvalore fiat al momento dello scambio — fortemente consigliato per gli swap crypto/crypto, dove il solo `price` (cambio relativo tra le due crypto) non permette di calcolare a posteriori il P&L realizzato né il cost-basis fiat del nuovo asset ricevuto.
- **`log_transfer`** — spostamento dello stesso asset tra due custodie (`exchange` → `to_exchange`), non un trade: niente `side`/`price`, ma richiede `to_exchange`.
- **`get_transaction_history`** — storico di entrambi, con filtri opzionali (`symbol`, `exchange`, `side` incluso `"TRANSFER"`, `date_from`/`date_to`), ordinato per data decrescente (le più recenti prima) e paginato (`limit` default 50, `offset`); la risposta è `{"transactions": [...], "has_more": bool}`.
- **`get_portfolio_summary`** — per ogni asset (escluso `vs_currency` stesso): quanto ne possiedi, quanto hai investito in totale e in media per unità (cost-basis), valore di mercato, plus/minusvalenza non realizzata. `as_of_date` opzionale per una situazione a una data passata (prezzo storico) invece che live — pensato per la rendicontazione fiscale. `cost_basis_method` (`weighted_average` o `fifo`) è una scelta esplicita, non automatica: possono dare plusvalenze diverse sullo stesso storico, verifica quale ti serve con un commercialista.
- **`get_asset_price`** — quotazione di un singolo asset, corrente o storica a una data, senza calcolare tutto il portafoglio.

## Setup

```bash
uv sync
```

Copia `.env.example` in `.env` e valorizzalo: viene caricato automaticamente
all'avvio (`main.py` chiama `load_dotenv()`). Questo funziona anche quando il
server è lanciato da un host esterno (Claude Desktop, agy, ...) con
`uv run --directory <path> main.py`, perché `--directory` imposta la working
directory del processo sulla root del progetto, dove `load_dotenv()` cerca
`.env` di default — **non serve duplicare i secret nel JSON di config
dell'host**, che spesso è meno protetto di un `.env` locale gitignored.

## Backend di persistenza

Selezionabile con la variabile d'ambiente `TRANSACTION_REPOSITORY_BACKEND`:

- `json` (default) — nessuna configurazione aggiuntiva richiesta.
- `notion` — richiede `NOTION_TOKEN` (token dell'integration Notion) e `NOTION_DATABASE_ID`.

### Setup Notion da zero

Se non hai già un'integration Notion collegata a questo progetto, segui questi
passi in ordine (richiede un account Notion con permessi di creare
integration nel workspace):

1. **Crea l'integration** — vai su [notion.so/my-integrations](https://www.notion.so/my-integrations)
   → "+ New integration" → dai un nome (es. "Crypto Transactions MCP") →
   scegli il workspace → tipo "Internal".
2. **Abilita le capabilities** — nella pagina di configurazione
   dell'integration, sotto "Capabilities", abilita: **Read content**,
   **Update content**, **Insert content**.
3. **Copia il token** — tab "Secrets" → copia il valore di "Internal
   Integration Secret" (inizia con `secret_` o `ntn_`). Questo è il tuo
   `NOTION_TOKEN`.
4. **Crea il database** — in Notion, crea un nuovo database (full-page o
   inline) e configuralo con esattamente le property elencate nella tabella
   sotto. La colonna "title" che Notion crea di default (di solito "Name")
   va rinominata in `Transaction ID`: ogni database Notion ne ha
   obbligatoriamente una, non è il nome del database ma la colonna che
   identifica ogni riga.
5. **Condividi il database con l'integration** — apri la pagina del
   database → menu "..." in alto a destra → "Connections" → cerca e collega
   l'integration creata al punto 1. **Senza questo passaggio l'API risponde
   404 anche con un token valido**, perché i permessi Notion sono per-pagina,
   non workspace-wide.
6. **Trova il database ID** — è nell'URL della pagina del database:
   `https://www.notion.so/<ID-senza-trattini>?v=...`. Ad esempio, da
   `https://www.notion.so/abcdef0123456789abcdef0123456789?v=...` l'ID è
   `abcdef0123456789abcdef0123456789`. Questo è il tuo `NOTION_DATABASE_ID`.
7. **Configura le variabili** — nel tuo `.env` (vedi sopra), o nel blocco
   `env` dell'host MCP:
   ```
   TRANSACTION_REPOSITORY_BACKEND=notion
   NOTION_TOKEN=<il secret del punto 3>
   NOTION_DATABASE_ID=<l'id del punto 6>
   ```

Il database Notion deve avere queste property (nome esatto e tipo):

| Property        | Tipo      |
|-----------------|-----------|
| `Transaction ID`| title     |
| `Tx Hash`       | rich_text |
| `Exchange`      | select    |
| `To Exchange`   | select    |
| `Symbol`        | select    |
| `Side`          | select (`BUY`, `SELL`, `TRANSFER`) |
| `Timestamp`     | date (con orario, UTC) |
| `Amount`        | number    |
| `Price`         | number    |
| `Value`         | number    |
| `Fee Amount`    | number    |
| `Fee Asset`     | select    |
| `Fiat Value`    | number    |
| `Fiat Currency` | select    |
| `Notes`         | rich_text |

`To Exchange`, `Price`, `Value`, `Fiat Value`, `Fiat Currency` possono essere
vuoti (non si applicano a un `TRANSFER`, o sono opzionali su un trade).

Nota: i campi numerici su Notion sono `number` nativi (float64), non `Decimal`
esatti come nel backend JSON — sufficiente per gli importi reali di trading,
ma soggetto ai limiti di precisione di un float a doppia precisione.

## Test

```bash
uv run pytest
```

## Esecuzione manuale

```bash
uv run main.py
```

## Configurazione Claude Desktop

Aggiungi al file `claude_desktop_config.json` (su macOS:
`~/Library/Application Support/Claude/claude_desktop_config.json`). Con un
`.env` già configurato alla root del progetto (vedi sopra) basta questo,
nessun secret nel JSON:

```json
{
  "mcpServers": {
    "crypto-transactions": {
      "command": "uv",
      "args": [
        "run",
        "--directory",
        "/path/to/crypto-transactions-mcp",
        "main.py"
      ]
    }
  }
}
```

In alternativa, se preferisci non usare un file `.env`, puoi passare le
variabili direttamente nel blocco `env` (sovrascrivono quelle di `.env` se
presenti entrambe):

```json
{
  "mcpServers": {
    "crypto-transactions": {
      "command": "uv",
      "args": [
        "run",
        "--directory",
        "/path/to/crypto-transactions-mcp",
        "main.py"
      ],
      "env": {
        "TRANSACTION_REPOSITORY_BACKEND": "notion",
        "NOTION_TOKEN": "secret_...",
        "NOTION_DATABASE_ID": "abcdef0123456789abcdef0123456789"
      }
    }
  }
}
```

## Configurazione agy

Stesso schema `mcpServers`, ma nel file `.agents/mcp_config.json` alla root del
progetto in cui usi agy (aggiungi la voce `crypto-transactions` accanto agli
altri server già presenti, senza rimuoverli). Vale la stessa cosa: con `.env`
configurato non serve il blocco `env`:

```json
{
  "mcpServers": {
    "crypto-transactions": {
      "command": "uv",
      "args": [
        "run",
        "--directory",
        "/path/to/crypto-transactions-mcp",
        "main.py"
      ]
    }
  }
}
```

TDQS

A4.1/5.0

Scored across 3 tools

Disambiguation5/5

Each tool has a distinct and clear purpose: one for querying history, one for logging trades/swaps, and one for logging asset transfers. There is no overlap in functionality.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern with lowercase and underscores: get_transaction_history, log_transaction, log_transfer. This is predictable and easy to understand.

Tool Count4/5

With only 3 tools, the set is minimal but covers the core actions of reading and writing transactions and transfers. It feels slightly thin but is still well-scoped for a focused server.

Completeness4/5

The server covers basic CRUD operations for transactions and transfers, including upsert and filtering. Missing a dedicated tool for single transaction retrieval or deletion, but the existing tools can accommodate those needs.

Maintenance

ActivityMaintained
ResponsivenessNo issues