Datalake-mcp
Servidor MCP (Model Context Protocol) para conectar un Azure Data Lake Storage Gen2 a Claude Desktop u otros agentes
Documentación
Datalake-MCP
Servidor MCP (Model Context Protocol) que expone un Azure Data Lake Storage Gen2 a Claude, permitiendo navegar por los archivos, leer su contenido en formato CSV, Parquet, texto (.txt), markdown (.md) o XML, y guardar archivos (texto, markdown, CSV, Parquet, PDF) con un índice buscable.
Cómo funciona
Arquitectura
flowchart LR
U([Utente]) <--> C["Claude<br/>(Desktop / Code)"]
C <-->|"MCP via stdio"| T
subgraph S["Datalake.Mcp.Server (.NET 9)"]
direction TB
T["Tools/DataLakeTools<br/>tool MCP"]
subgraph R["Lettura (streaming)"]
direction LR
CSV[CsvReaderService]
PQ[ParquetReaderService]
TXT[TextReaderService]
XML[XmlReaderService]
F[RowFilter]
end
subgraph W["Scrittura"]
direction LR
FW[FileWriterService]
IDX[FileIndex]
end
DL[DataLakeService]
T --> R
T --> W
R --> DL
W --> DL
R -.-> F
end
DL <-->|"Azure.Storage.Files.DataLake<br/>AccountName + AccountKey"| ADLS[("Azure Data Lake<br/>Storage Gen2")]
IDX -.->|"saved-files-index.csv<br/>(ETag)"| ADLS
Flujo de lectura y consulta
sequenceDiagram
actor U as Utente
participant C as Claude
participant M as Server MCP
participant L as Data Lake
U->>C: "Mostrami gli ordini APERTI > 1000"
C->>M: list_filesystems / list_directory
M->>L: navigazione
L-->>C: filesystem, cartelle, file
C->>M: get_parquet_schema (colonne e tipi)
C->>M: read_parquet(filter, columns, maxRows, offset)
M->>L: OpenReadAsync (download lazy)
loop un row-group / riga alla volta
L-->>M: byte necessari
M->>M: filtro → offset → maxRows
end
M-->>C: righe + truncated / nextOffset
C-->>U: risposta in linguaggio naturale
Note over C,M: se truncated=true si richiama con offset=nextOffset
Flujo de guardado y búsqueda
sequenceDiagram
actor U as Utente
participant C as Claude
participant M as Server MCP
participant L as Data Lake
U->>C: "Salva il report in report/2026"
C->>M: save_csv / save_parquet / save_pdf / ...
M->>M: validazione formato ed estensione
M->>L: upload condizionale (If-None-Match: *)
alt esiste già e overwrite=false
L-->>M: errore, nessuna sovrascrittura
else
L-->>M: file salvato
loop fino a 5 tentativi
M->>L: leggi indice (ETag)
M->>L: scrivi indice (If-Match: ETag)
end
end
M-->>C: esito
U->>C: "Trova il report"
C->>M: search_file(nomeFile, tipoFile)
M->>L: legge l'indice
M-->>C: voci trovate (+ contenuto se un solo file)
Estructura de la solución
Datalake-MCP.sln
src/Datalake.Mcp.Server/ Server MCP (host .NET Generic Host, trasporto stdio)
Configuration/DataLakeOptions.cs Opzioni di connessione (AccountName/AccountKey/DefaultFileSystem)
Services/DataLakeService.cs Wrapper su Azure.Storage.Files.DataLake (navigazione, streaming)
Services/CsvReaderService.cs Lettura CSV con limite di righe (streaming)
Services/ParquetReaderService.cs Lettura Parquet a row-group, con proiezione colonne
Services/TextReaderService.cs Lettura file di testo/markdown riga per riga (streaming)
Services/XmlReaderService.cs Lettura XML tabellare elemento per elemento, con proiezione colonne
Services/FileWriterService.cs Costruzione/validazione dei byte da salvare (testo, CSV, Parquet, PDF)
Services/FileIndex.cs Indice CSV dei file salvati (upsert, parsing, ricerca)
Tools/DataLakeTools.cs Tool MCP esposti al client (list_filesystems, read_csv, ...)
tests/Datalake.Mcp.Server.Tests/ Test xUnit per reader CSV/Parquet/testo/XML, writer e indice
Herramientas MCP expuestas
| Herramienta | Descripción |
|---|---|
list_filesystems | Lista los filesystems (contenedores) de la cuenta |
list_directory | Lista archivos y directorios en una ruta (opcionalmente recursivo) |
get_file_info | Metadatos de un archivo (tamaño, fecha de última modificación) |
read_csv | Lee un CSV, con límite maxRows, delimitador configurable y offset para la paginación; filtro opcional filter |
get_parquet_schema | Devuelve el esquema (columnas/tipos) de un Parquet sin leer sus datos |
read_parquet | Lee un Parquet, con maxRows, proyección opcional de columnas y offset para la paginación; filtro opcional filter |
read_text | Lee un archivo de texto (.txt), con maxLines y offset para la paginación; filtro opcional contains |
read_markdown | Lee un archivo markdown (.md), con maxLines y offset para la paginación; filtro opcional contains |
get_xml_schema | Devuelve el esquema (columnas, entre atributos y elementos) del elemento record de un XML tabular sin leer sus datos |
read_xml | Lee un XML tabular (raíz con elementos record repetidos), con maxRows, elemento record y proyección de columnas opcionales, y offset para la paginación; filtro opcional filter |
create_folder | Crea una carpeta en la raíz del filesystem o como subcarpeta de una carpeta existente (el padre debe existir; error si la carpeta ya existe) |
save_text | Guarda un archivo .txt en una carpeta/subcarpeta del datalake |
save_markdown | Guarda un archivo .md |
save_csv | Guarda un archivo .csv desde texto CSV (validado: mismo número de campos por línea) |
save_parquet | Guarda un archivo .parquet desde una lista de filas (objetos columna → valor); los tipos se deducen (bool, entero, decimal, cadena) |
save_pdf | Guarda un archivo .pdf desde contenido base64 (verificada la cabecera %PDF-) |
search_file | Busca en el índice de archivos guardados por nomeFile (contiene, case-insensitive) y/o tipoFile (csv, parquet, pdf, md, txt) y devuelve las entradas encontradas (path utilizable con las herramientas read_*). Si el resultado es un solo archivo, devuelve también su contenido (csv, parquet, txt, md; primeras maxRows líneas, luego se continúa con read_* y nextOffset; para los pdf solo los metadatos) |
Las lecturas están diseñadas para no cargar archivos completos en memoria: CSV, texto/markdown y XML
se leen línea/elemento por línea/elemento deteniéndose en el límite solicitado, mientras que Parquet se
lee un row-group a la vez. Los archivos remotos se abren con streaming de carga diferida
(DataLakeFileClient.OpenReadAsync), que descarga solo los bytes realmente necesarios. La lectura
XML deshabilita el procesamiento de DTD y la resolución de entidades externas para evitar ataques XXE.
Filtros
read_csv, read_parquet y read_xml aceptan un parámetro opcional filter: una lista de
condiciones { column, operator, value } combinadas en AND, por ejemplo:
[{"column": "stato", "operator": "eq", "value": "APERTO"},
{"column": "importo", "operator": "gt", "value": "1000"}]
Operadores: eq (=), ne (!=), gt (>), gte (>=), lt (<), lte (<=), contains,
startswith, endswith, isnull, isnotnull (estos últimos dos no requieren value; una cadena
vacía cuenta como null). Las comparaciones son numéricas cuando celda y valor son números (formato
invariante, ej. 12.5), sobre timestamps Parquet cuando el valor es una fecha, de lo contrario textuales
case-insensitive. El filtro puede referirse también a columnas no proyectadas; una columna inexistente o un
operador no válido producen un error que enumera los valores permitidos.
El filtro se aplica durante la lectura en streaming, antes de offset y maxRows: la
paginación (truncated/nextOffset) cuenta por tanto solo las filas que satisfacen el filtro.
read_text y read_markdown aceptan en cambio contains, que selecciona las filas que contienen el texto
indicado (case-insensitive).
Paginación
Las respuestas de read_csv, read_parquet, read_text, read_markdown y read_xml están sujetas
al límite de tamaño de las llamadas a herramientas (aproximadamente 1 MB). Para archivos que exceden maxRows/maxLines (o
el límite de tamaño), el resultado reporta
truncated: true y un nextOffset: llamando nuevamente a la herramienta con offset = nextOffset se
obtiene la página siguiente, saltando las filas ya devueltas sin tener que releerlas por completo.
Cuando no hay más que leer, truncated es false y nextOffset es null.
Guardado de archivos e índice
Las herramientas save_* aceptan path (carpetas y subcarpetas se crean si faltan), fileSystem,
description y overwrite. Sin overwrite=true un archivo existente nunca se reemplaza (carga
condicional If-None-Match: *). La extensión de la ruta debe corresponder al formato.
Cada guardado registra el archivo en el índice DataLake:SavedFilesIndexPath (por defecto
saved-files-index.csv, en la raíz del filesystem), un CSV legible con read_csv con columnas
path,name,format,sizeBytes,savedAt,description. Guardar de nuevo la misma ruta actualiza la fila
existente. El índice se actualiza con concurrencia optimista (ETag, hasta 5 intentos).
Configuración
Las credenciales de la cuenta (accountName y accountKey) nunca deben commitearse. En desarrollo,
usa los .NET user-secrets desde la carpeta src/Datalake.Mcp.Server:
dotnet user-secrets init
dotnet user-secrets set "DataLake:AccountName" "<nome-account>"
dotnet user-secrets set "DataLake:AccountKey" "<chiave-account>"
dotnet user-secrets set "DataLake:DefaultFileSystem" "<container-predefinito>"
Alternativamente, mediante variables de entorno (útil para el despliegue):
$env:DataLake__AccountName = "<nome-account>"
$env:DataLake__AccountKey = "<chiave-account>"
$env:DataLake__DefaultFileSystem = "<container-predefinito>"
appsettings.json contiene solo los placeholders vacíos y puede permanecer en el repositorio.
Build, test y ejecución
dotnet build
dotnet test
dotnet run --project src/Datalake.Mcp.Server
El servidor se comunica vía stdio según el protocolo MCP: por tanto debe registrarse como servidor MCP en el
cliente (ej. Claude Desktop / Claude Code), apuntando al ejecutable del proyecto
Datalake.Mcp.Server, no lanzarse manualmente en una terminal interactiva.
Registro en Claude Desktop y uso del MCP
Un ejemplo de configuración está disponible en
claude_desktop_config.example.json. Para usarlo:
- Ejecuta
dotnet build src/Datalake.Mcp.Server -c Releasepara generar el DLL enbin/Release/net9.0. O usa el contenido de la carpeta si ya está compilado. - Copia el contenido del archivo de ejemplo en el archivo de configuración de Claude Desktop
(
%APPDATA%\Claude\claude_desktop_config.json), uniéndolo a la clavemcpServerssi ya existe. - Actualiza la ruta en
argssi el repositorio está en otro lugar, y valoraAccountName/AccountKey/DefaultFileSystemenenvcon las credenciales reales (nunca las commitees en el repositorio). - Reinicia Claude Desktop: el servidor "datalake" aparecerá entre las herramientas disponibles.