Skip to main content
Glama
domustudio-mcp-server

domustudio-mcp-server

domustudio-mcp-server

CI

An MCP server that exposes the Danea Domustudio public API — condomini, persone, fornitori — as tools an agent can call.

The API is read-only: its entire public surface is three GET endpoints. This server adds no write path, because there is none to add. Domustudio remains the system of record; anything that must change, changes there, by a human, in the product.

Requirements

  • Node.js ≥ 22.12

  • One Domustudio API key per archive (X-DANEA-API-KEY)

Related MCP server: Invoice4U MCP

Install it in another repository

The package is not on any registry — it installs straight from this repository, pinned to a release tag. One global install serves every repository on the machine.

npm install -g --allow-git=all "github:juriSacchetta/Domustudio-mcp#v0.4.0"

--allow-git=all is required on npm 12 and later, which ships allow-git = "none" as a default and otherwise refuses the install with EALLOWGIT. To stop passing it every time:

npm config set allow-git all

npm builds dist/ during install, so the machine needs nothing beyond Node ≥ 22.12 and access to this repository. npm 12 also prints install scripts blocked … (prepare: npm run build) — that warning is benign here: it refers to the consumer's tree, and the build has already run inside npm's isolated step for the git dependency. Confirm the install with:

test -f "$(npm root -g)/domustudio-mcp-server/dist/index.js" && echo installed

Do not run the entry point to "check" it: it is a stdio MCP server with no CLI, so it will start and wait for JSON-RPC on stdin rather than print anything.

A global install puts domustudio-mcp on your PATH, which is the name a shell-launched client needs — confirm with command -v domustudio-mcp. A GUI client is launched without your PATH and needs the absolute path instead:

echo "$(npm root -g)/domustudio-mcp-server/dist/index.js"

See Configure below for both. To upgrade, rerun the install command with a newer tag; every repository on the machine picks it up at once, which is the trade-off of a single global install.

Develop on it here

npm install
npm run build

Configure

The server reads its credentials from the environment, and falls back to a .env file in its working directory for anything the environment does not carry. DOMUSTUDIO_ARCHIVES is a JSON array — one API key addresses exactly one archive, and several archives may coexist.

Claude Code, one project

Add it at the default local scope: Claude Code stores that in ~/.claude.json under the project's path, so the server loads in this project only, stays private to you, and nothing lands in the repository.

claude mcp add domustudio -e DOMUSTUDIO_ARCHIVES='[{"name":"desa","api_key":"..."}]' \
  -- domustudio-mcp

The -- is required: everything after it is the server's own command line. A key passed this way lands in your shell history; to avoid that, drop the -e and put the key in a .env as below — the server finds it either way.

A committed .mcp.json

.mcp.json sits at the project root and is normally checked in, which is how a team shares one server definition — and exactly why the key must not appear in it. Leave the env block out and give each developer their own gitignored .env:

// .mcp.json — committed, no credentials
{
  "mcpServers": {
    "domustudio": { "command": "domustudio-mcp" }
  }
}
# .env at the project root — one per developer, never committed
printf '.env\n' >> .gitignore
printf 'DOMUSTUDIO_ARCHIVES=[{"name":"desa","api_key":"..."}]\n' >> .env
chmod 600 .env

Ignore it before you write it. .mcp.json is committed and .env must not be; nothing in this server can stop a consuming repository from committing one.

Claude Code launches the server with the project root as its working directory, which is where it looks. If your client launches it somewhere else, name the file outright in the .mcp.json entry — DOMUSTUDIO_ENV_FILE is a path, not a secret, so it is safe in a committed file. It has to arrive this way rather than from inside a .env, since it is what picks the file to read:

{ "mcpServers": { "domustudio": {
  "command": "domustudio-mcp",
  "env": { "DOMUSTUDIO_ENV_FILE": "/absolute/path/to/.env" }
} } }

Real environment variables always win over the file. Keep the array on one line, or wrap a multi-line one in single quotes — '[ … ]'.

Claude Desktop and other GUI clients

A GUI client is not launched from a shell and does not inherit your PATH, so the domustudio-mcp bin may not resolve. Give it Node and an absolute path. This file lives in your user profile, outside any repository, so the key may sit in it:

// claude_desktop_config.json — per user, never committed
{
  "mcpServers": {
    "domustudio": {
      "command": "node",
      // output of: echo "$(npm root -g)/domustudio-mcp-server/dist/index.js"
      "args": ["/absolute/path/to/domustudio-mcp-server/dist/index.js"],
      "env": {
        "DOMUSTUDIO_ARCHIVES": "[{\"name\":\"desa\",\"api_key\":\"...\"}]"
      }
    }
  }
}

Why ${DOMUSTUDIO_ARCHIVES} does not work

.mcp.json does expand ${VAR}, but supplying the value through .claude/settings.local.json env did not work: the server never started, and the failure reproduced with a control confirming the variable was present in the session. That is what #4 observed. Why the expansion missed it — an ordering question between settings env and .mcp.json loading — was not established, so treat the recipe as "this does not work", not as a mechanism you can reason around.

The symptom is worth recognising: an unexpanded variable is passed through as the literal text ${DOMUSTUDIO_ARCHIVES}, which is not JSON, so the server exits 1 and the client reports nothing more useful than domustudio (CONNECTION_CLOSED): "Connection closed". From 0.2.0 the server names that literal on stderr instead.

Exporting the variable from a shell rc file does make the expansion work, but it puts the key in a dotfile — no better than committing it. Prefer .env.

Variables

Variable

Required

Default

DOMUSTUDIO_ARCHIVES

yes

—

DOMUSTUDIO_BASE_URL

no

https://domustudioapi.danea.it/api/external

DOMUSTUDIO_ENV_FILE

no

.env in the working directory; set it empty to skip the lookup. Read from the client's environment only — it chooses the file, so setting it inside one has no effect

DOMUSTUDIO_BASE_URL exists for tests and for pointing at a mock; production should leave it unset. The default is HTTPS even though the OpenAPI servers entry says http://.

The key is never echoed in a tool result: an authentication failure names the archive and the header, not the value. The startup line on stderr names the .env it read, never its contents.

Tools

Tool

What it does

domustudio_list_archivi

Names of the configured archives

domustudio_list_condomini

Compact pages of condomini; optional local ricerca filter

domustudio_get_condominio

One condominio by id

domustudio_list_persone

Persone for a required condominio; paginated

domustudio_list_fornitori

Fornitori of the whole archive; paginated

All five are annotated readOnlyHint: true, destructiveHint: false.

Every tool takes an optional archivio. With a single archive configured it may be omitted; with several, omitting it returns an error listing the names.

Every tool takes response_format: markdown (default) renders the main fields for reading, json returns the full API record except for condominium lists, which are compact in both formats. Integer enums are decoded to { codice, etichetta }.

What the API does not give you

Persone and fornitori have no identifier. Neither schema contains an id, and no endpoint accepts one, so there is no get_persona and no get_fornitore — a person or supplier is reached by filtering a list. The condominio id is the only stable handle the API exposes, and it is what domustudio_list_persone takes as condominio_id.

condominio_id is required and is sent to the API as CondGenID. For an archive-wide person search, follow all condominium list pages first, then call domustudio_list_persone for each id and follow each building's pagination.

Compact condominium lists (0.5.0 contract change)

domustudio_list_condomini defaults to pagina: 1, dimensione_pagina: 25 (maximum 100). If the building name or address is known, pass ricerca first. The case-insensitive local filter runs before pagination. Danea still returns the complete list upstream; only the selected compact page enters the MCP result.

Both text and structuredContent contain only id, intestazione, indirizzo, cap, citta, prov, and codFisc where present, with archivio in the envelope. conteggio counts the current page, totale counts all matches, and ha_altre_pagine is exact. When true, pass prossima_pagina as the next pagina with the same archive, filter and page size. Empty and out-of-range pages contain no elements and no next page. Each call reads the archive afresh; enumeration assumes the upstream list stays unchanged between calls.

Existing clients must follow pages instead of treating one response as a full inventory. Use domustudio_get_condominio for the complete record, including administrator and other details. There is no all-pages bypass for condominium lists. Other tools retain their existing contracts.

Persone and fornitori have no total count. Responses are bare JSON arrays. ha_altre_pagine is therefore a heuristic — true when the last page read was full — and is documented as such in the tool descriptions.

A page is reported to hold at most 100 records. The API caps PageSize there and says nothing when it does, so dimensione_pagina is refused above that rather than sent and silently truncated. One tutte_le_pagine call therefore reaches 5 000 records — 100 per page, 50 pages. npm run test:live measures the cap against a real archive. See docs/adr/0002.

Development

Command

npm run build

compile to dist/

npm run typecheck

tsc --noEmit

npm run lint

eslint over src and test

npm test

typecheck + full suite

npm run test:unit

suite only, no build

npm run test:live

suite against the real API (see below)

npm run inspector

MCP Inspector against dist/index.js

The suite runs against a local HTTP mock of the Domustudio API and needs no credentials. It covers the client (headers, 1-based paging, retry policy, the truncated-401), configuration and archive resolution, enum decoding, markdown rendering, every tool over an in-memory MCP transport, and the built binary over real stdio.

Running against the real API

Put real credentials in a .env at the repo root — it is gitignored:

printf 'DOMUSTUDIO_ARCHIVES=[{"name":"desa","api_key":"..."}]\n' > .env
chmod 600 .env
npm run test:live

Without DOMUSTUDIO_LIVE=1 those tests skip, so npm test stays green on a machine with no credentials. They only read, and assert on shapes and counts — no record content is printed.

Documentation

Trademarks

Danea and Domustudio are trademarks of their respective owners. This project is an independent client of the Domustudio public API, is not affiliated with or endorsed by them, and is not supported by them.

License

MIT — see LICENSE. Copyright (c) 2026 Juri Sacchetta.

Available Tools

5 tools
domustudio_get_condominioLeggi un condominio per idA
Read-onlyIdempotent

Restituisce il singolo condominio con l'id indicato.

L'API non espone una lettura per id: lo strumento scarica la lista completa dei condomini e seleziona la voce corrispondente.

Args:

  • condominio_id (number): id Domustudio del condominio

  • archivio (string, opzionale): archivio da interrogare

  • response_format ('markdown' | 'json'): default 'markdown'

Restituisce (json): { "archivio": string, "trovato": boolean, "condominio": object | null }

Errori:

  • "trovato": false se nessun condominio ha quell'id nell'archivio indicato.

L'API Domustudio è di sola lettura: espone solo tre endpoint GET. Domustudio resta il sistema di registrazione — ogni correzione va fatta da un operatore dentro il gestionale.

Inputs: condominio_id (required), archivio, response_format. Returns: trovato, archivio, condominio

domustudio_list_archiviElenca archivi Domustudio configuratiA
Read-onlyIdempotent

Elenca i nomi degli archivi Domustudio configurati su questo server.

Una chiave API indirizza esattamente un archivio; ogni altro strumento accetta un parametro "archivio" che seleziona quale interrogare. Se ne è configurato uno solo, quel parametro può essere omesso.

Nessun parametro.

Restituisce: { "archivi": string[], // nomi configurati, nell'ordine di configurazione "conteggio": number }

L'API Domustudio è di sola lettura: espone solo tre endpoint GET. Domustudio resta il sistema di registrazione — ogni correzione va fatta da un operatore dentro il gestionale.

Inputs: no named parameters. Returns: archivi, conteggio

domustudio_list_condominiElenca condominiA
Read-onlyIdempotent

Elenca i condomini presenti in un archivio Domustudio.

L'endpoint restituisce sempre la lista completa: non è paginato e non accetta filtri lato API. Il parametro "ricerca" filtra i risultati localmente su intestazione, città, provincia, indirizzo e codice fiscale.

L'"id" di un condominio è l'unico identificatore stabile esposto dall'API Domustudio, ed è il valore da passare come "condominio_id" a domustudio_list_persone.

Args:

  • archivio (string, opzionale): archivio da interrogare

  • ricerca (string, opzionale): sottostringa, confronto case-insensitive

  • response_format ('markdown' | 'json'): default 'markdown'

Restituisce (json): { "archivio": string, "conteggio": number, "elementi": [ { "id": number, "intestazione": string, "indirizzo": string, "cap": string, "citta": string, "prov": string, "codFisc": string, "amministratore": { "nome": string, "email": string, ... } } ] } In formato markdown vengono mostrati intestazione, id, indirizzo, codice fiscale e amministratore; il formato json riporta il record completo restituito dall'API.

Usare quando: serve l'id di un condominio, o l'anagrafica dello stabile. Non usare per: elencare le persone di un condominio (domustudio_list_persone) o i fornitori (domustudio_list_fornitori).

L'API Domustudio è di sola lettura: espone solo tre endpoint GET. Domustudio resta il sistema di registrazione — ogni correzione va fatta da un operatore dentro il gestionale.

Inputs: ricerca, archivio, response_format. Returns: ricerca, archivio, elementi, conteggio

domustudio_list_fornitoriElenca fornitoriA
Read-onlyIdempotent

Elenca i fornitori di un archivio Domustudio.

L'anagrafica fornitori è globale sull'archivio, non per condominio: non esiste un parametro che la restringa a uno stabile. Come per le persone, il record non contiene alcun id, quindi non esiste una lettura per id; per trovare un fornitore si filtra questo elenco.

Args:

  • archivio (string, opzionale): archivio da interrogare

  • attivi (boolean, opzionale): solo attivi (true) o solo non attivi (false); omesso, entrambi

  • dati_incompleti (boolean, opzionale): filtra sui fornitori con dati incompleti

  • attivita (string, opzionale): attività del fornitore (parametro Attivita)

  • impianto_servizio_id (number, opzionale): id del servizio collegato (ImpiantoServizioID)

  • esercizi_id (number[], opzionale): esercizi del fornitore (EserciziID)

  • search_query (string, opzionale): passato all'API come SearchQuery, semantica non documentata

  • order_by (string, opzionale): passato all'API come OrderBy, semantica non documentata

  • pagina (number): pagina 1-based, default 1

  • dimensione_pagina (number): elementi per pagina, default 50

  • tutte_le_pagine (boolean): se true scorre le pagine fino a 50, default false

  • response_format ('markdown' | 'json'): default 'markdown'

Restituisce (json): stesso involucro paginato di domustudio_list_persone, con elementi della forma { "descr": string, "fornitoreAttivita": string, "codFisc": string, "piva": string, "iban": string, "email": string[], "pec": string[], "tel1": string, "fornitoreInattivo": boolean, "descStatoDurc": string, "dataScadenzaDurc": string, "f24CodTributo": { "codice": number, "etichetta": string }, ... } "ha_altre_pagine" è un'euristica: l'API non restituisce un totale, quindi è vero quando l'ultima pagina letta era piena.

Usare quando: serve un fornitore per attività, partita IVA, IBAN o stato DURC. Non usare per: i condòmini e i soggetti dell'anagrafica persone (domustudio_list_persone).

L'API Domustudio è di sola lettura: espone solo tre endpoint GET. Domustudio resta il sistema di registrazione — ogni correzione va fatta da un operatore dentro il gestionale.

Inputs: attivi, pagina, archivio, attivita, +8 more. Returns: pagina, archivio, elementi, conteggio, +5 more

domustudio_list_personeElenca persone (anagrafica soggetti)A
Read-onlyIdempotent

Elenca le persone dell'anagrafica Domustudio, opzionalmente ristrette a un condominio.

L'API non espone un identificatore per le persone: il record non contiene alcun id, quindi non esiste una lettura per id e non c'è modo di indirizzare una persona singola. Per trovare qualcuno si filtra questo elenco (search_query, oppure condominio_id più una lettura dei risultati).

Args:

  • archivio (string, opzionale): archivio da interrogare

  • condominio_id (number, opzionale): restringe a un condominio (parametro CondGendID). Ottenerlo da domustudio_list_condomini

  • filtro_subentri (1|2|3|4|5): 1 tutti, 2 attivi, 3 ex, 4 contabilità, 5 destinatari comunicazioni. Default 2

  • esercizio_id (number, opzionale): parametro EsercizioID

  • tags_id (number[], opzionale): parametro TagsID

  • search_query (string, opzionale): passato all'API come SearchQuery; la semantica è decisa dall'API, non documentata nello spec

  • order_by (string, opzionale): passato all'API come OrderBy, semantica non documentata

  • pagina (number): pagina 1-based, default 1

  • dimensione_pagina (number): elementi per pagina, default 50

  • tutte_le_pagine (boolean): se true scorre le pagine fino a 50, default false

  • response_format ('markdown' | 'json'): default 'markdown'

Restituisce (json): { "archivio": string, "conteggio": number, // elementi in questa risposta "pagina": number, // prima pagina letta "dimensione_pagina": number, "pagine_lette": number, "ha_altre_pagine": boolean, // euristica: l'API non restituisce un totale, quindi è vero quando l'ultima pagina era piena "prossima_pagina": number, // presente solo se ha_altre_pagine "troncato_al_limite_pagine": boolean, "elementi": [ { "descr": string, "codFisc": string, "piva": string, "indirizzo": string, "cap": string, "citta": string, "prov": string, "email": string[], "pec": string[], "tel1": string, "note": string, "preferenzaPagamentoRate": { "codice": number, "etichetta": string }, ... } ] } I campi enumerativi sono restituiti come { codice, etichetta }. Il formato markdown mostra denominazione, codice fiscale, indirizzo, recapiti; json riporta il record completo.

Usare quando: servono i condòmini di uno stabile, i recapiti di un soggetto, o un conteggio per condominio. Non usare per: i fornitori, che stanno in un'anagrafica separata (domustudio_list_fornitori).

L'API Domustudio è di sola lettura: espone solo tre endpoint GET. Domustudio resta il sistema di registrazione — ogni correzione va fatta da un operatore dentro il gestionale.

Inputs: pagina, tags_id, archivio, order_by, +7 more. Returns: pagina, archivio, elementi, conteggio, +5 more

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.5/5.0

    Scored across 5 tools

    Disambiguation5/5

    Each tool targets a distinct resource: archives, condomini, a single condominio, persone, or fornitori. The descriptions explicitly state when to use each tool and when not to use it, preventing overlap confusion. The only minor redundancy is get_condominio versus list_condomini, but the signature and intent are clearly separated.

    Naming Consistency5/5

    All tools share the domustudio_ prefix and use consistent snake_case with a verb_noun pattern: list_archivi, list_condomini, get_condominio, list_persone, list_fornitori. There are no mixed conventions or ambiguous verb styles. The naming is entirely predictable.

    Tool Count5/5

    Five tools is well-scoped for a read-only integration wrapping three GET endpoints plus archive discovery and a convenience single-condominio lookup. Each tool earns its place, with no thin or bloated surface. The count matches the domain perfectly.

    Completeness5/5

    The Domustudio API is explicitly read-only and exposes only three GET endpoints; the server covers all of them plus archive listing and single-condominio retrieval. Missing update/delete operations are by design, not gaps. Agents have workarounds for person and supplier lookup via filtering, and no obvious read path is absent.

    Maintenance

    ActivityNo data
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables MCP-compatible agents to securely access an Invoice4U account for searching documents and customers and creating receipts linked to paid invoices, with read-only behavior by default.
      1
      MIT
    • A
      license
      A
      quality
      C
      maintenance
      It enables MCP clients such as Claude and ChatGPT to read an estate agency's Dezrez Rezi CRM data, including properties and their timelines, people, groups, and offers. It is read-only and applies privacy redactions by default.
      10
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables AI agents to read-only query an MSP's Action1 data — organizations, managed endpoints, missing OS and third-party updates, and scheduled automations — using per-request credentials that are never stored. Supports multi-tenant, stateless access over Streamable HTTP with paginated list results and structured error responses.
      Apache 2.0