Ogni connector.json ora ha una llm_short_description che elenca i tool specifici che espone, così l'LLM sa esattamente cosa attivare (lazy load). SKALD.md: documentata criticità del campo llm_short_description nel system prompt dell'LLM.
21 KiB
Skald Connectors Marketplace
Reference locale
- docs/connector.manifest_guide.md — Guida ufficiale per generare connector corretti (copiata da
skald-circle/blueprint/)
Updated: 2026-07-22
Remote: https://git.skaldagent.net/dguiducci/skald-connectors.git
Live: https://connectors.skaldagent.net/
OAuth callback: https://connectors.skaldagent.net/oauth/show.html
Deploy
Il branch main è il branch di release. Solo codice pronto per produzione finisce qui.
Sviluppo e versioni alfa-staranno su branch separati in futuro.
Per deployare l'ultima versione del marketplace sul server:
ssh dguiducci@skald-home-server /home/dguiducci/marketplace_deploy.sh
Lo script sul server fa:
git pullin/home/dguiducci/repos/skald-connectors/cp -r connectors/*in/var/www/connectors.skaldagent.net/
La directory /var/www/connectors.skaldagent.net/ è di proprietà di dguiducci,
quindi non serve sudo per la copia.
Cos'è
Il marketplace è il catalogo dei connector testati per Skald. Ogni connector è un adattatore che permette a Skald di interfacciarsi con un servizio esterno (API, email, calendario, ricerca, messaggistica, ecc.).
Due tipi di connector:
mcp_remote— un MCP server già hosted, accessibile via URL (es. Tavily).mcp_local— script Python/Node da eseguire lato client (es. Gmail, Google Calendar).
Friendly tool names (2026-07-21)
Ogni tool MCP deve esporre un nome friendly per la UI di Skald. Due modi, in ordine di preferenza:
- Via script MCP (preferito) — aggiungere
"title": "Friendly Name"nella definizione di ogni tool dentrotools/list. Funziona per tutti gli script locali (Python/Node) che controlliamo. - Via manifest (fallback) — aggiungere
"tools": [{"name": "...", "display_name": "..."}]inconnector.jsone inconnectors.json. Usato solo per connector remoti o package esterni (es.npx -y firecrawl-mcp).
Ordine di risoluzione (Skald li prova in quest'ordine):
tools[].display_namedal manifesttitledaltools/listdell'MCP server- Prettify automatico del nome raw (
send_message→ "Send Message")
Stato attuale (2026-07-21): tutti i 13 connector del marketplace hanno title nello script o tools[] nel manifest.
Struttura directory
connectors/
├── connectors.json ← INDICE (radice di fiducia unica)
├── index.html ← Catalogo UI (legge connectors.json via fetch)
├── oauth/
│ └── show.html ← OAuth callback receiver (client-side, no backend)
├── gmail/ ← Un connector per cartella
│ ├── connector.json ← Configurazione tecnica
│ ├── gmail_mcp_server.py ← Script MCP
│ ├── gmail_oauth_setup.py ← Script setup OAuth
│ ├── requirements.txt ← Dipendenze Python
│ ├── icon_sm.svg ← Icona piccola (48×48)
│ └── icon_lg.svg ← Icona grande (es. preview)
├── email/
│ ├── connector.json
│ ├── email_mcp_server.py
│ ├── verify.py
│ ├── requirements.txt
│ ├── icon_sm.svg
│ └── icon_lg.svg
├── ssh/
│ ├── connector.json
│ ├── ssh_mcp_server.py
│ ├── requirements.txt
│ ├── icon_sm.svg
│ └── icon_lg.svg
└── tavily/
├── connector.json
├── verify.py
├── icon_sm.png
└── icon_lg.png
Schema — connectors.json (root)
Questo è l'unico punto di fiducia. Contiene type, scope e gli sha256 dei file di ogni connector.
Non ha hash di sé stesso — in futuro potrà essere firmato digitalmente.
{
"version": 1,
"connectors": [
{
"id": "gmail",
"name": "Gmail",
"type": "mcp_local",
"scope": "user",
"icon_small": "gmail/icon_sm.svg",
"icon_large": "gmail/icon_lg.svg",
"user_description": "Read, send, and manage Gmail emails via OAuth...",
"requires": ["OAUTH", "PYTHON"],
"tags": ["email", "mcp", "local", "google"],
"auth": {
"type": "oauth2",
"provider": "google",
"scopes": [
"https://www.googleapis.com/auth/gmail.modify",
"https://www.googleapis.com/auth/gmail.labels"
]
},
"folder": "gmail",
"version": 1,
"version_string": "1.0.0",
"version_release_date": "2026-07-19",
"files": [
| `auth` | per OAuth | Oggetto con `type`, `provider`, `scopes` per badge UI (NO `deliver` qui, è nel manifest) |
{"path": "gmail_mcp_server.py", "sha256": "a50d4da9621f7a4b092f...", "size": 46772},
{"path": "gmail_oauth_setup.py", "sha256": "e488acb289c43a3e6d54...", "size": 3627},
{"path": "icon_lg.svg", "sha256": "93c8d9c8dae96f0206e5...", "size": 254},
{"path": "icon_sm.svg", "sha256": "029d7f5d81de6cf2b17b...", "size": 251},
{"path": "requirements.txt", "sha256": "3f659cc5e5f0543f1326...", "size": 82}
]
}
]
}
Campi dell'indice
| Campo | Obbligatorio | Descrizione |
|---|---|---|
id |
✅ | Identificatore unico (kebab-case) |
name |
✅ | Nome visualizzato |
type |
✅ | mcp_remote o mcp_local |
scope |
✅ | global o user |
icon_small |
✅ | Path relativo dalla root del marketplace |
icon_large |
✅ | Path relativo dalla root del marketplace |
user_description |
✅ | Descrizione breve per la UI |
requires |
✅ | Array di enum requisiti |
tags |
✅ | Array di tag per filtraggio |
folder |
✅ | Nome della cartella del connector |
version |
✅ | Intero per-connector, +1 a ogni modifica dei file |
version_string |
✅ | Semver (solo display) |
version_release_date |
✅ | Data ISO 8601 YYYY-MM-DD (solo display) |
files |
✅ | Array di file con sha256 (NO self-hash) |
Schema — connector.json (per cartella)
Configurazione tecnica per l'attivazione del connector.
{
"id": "gmail",
"name": "Gmail",
"version": 1,
"version_string": "1.0.0",
"version_release_date": "2026-07-19",
"type": "mcp_local",
"scope": "user",
"launch_command": "python3 gmail_mcp_server.py",
"transport": "stdio",
"requires": ["OAUTH", "PYTHON"],
"tags": ["email", "mcp", "local", "google"],
"dependencies": [
"google-api-python-client>=2.150.0",
"google-auth>=2.35.0",
"google-auth-oauthlib>=1.2.0"
],
"setup_instructions": [
"Install dependencies: pip install -r requirements.txt",
"Run: python3 gmail_oauth_setup.py (optional, for standalone use — Skald handles OAuth)"
],
"docs": [
{
"lang": "en",
"description": "Full description for human users...",
"llm_short_description": "Google Calendar MCP: full CRUD for events, RSVP management, push polling. Tools: status, list_calendars, list_events, get_event, create_event, update_event, delete_event, respond_to_event. Requires Google OAuth."
}
],
"auth": {
"type": "oauth2",
"provider": "google",
"scopes": [
"https://www.googleapis.com/auth/gmail.modify",
"https://www.googleapis.com/auth/gmail.labels"
],
"deliver": {
"as": "env",
"format": "google_authorized_user",
"env": "GMAIL_CREDS_JSON"
}
},
"mcp_config": {
"command": "python3",
"args": ["gmail_mcp_server.py"]
},
"homepage": "https://mail.google.com",
"icon_small": "icon_sm.svg",
"icon_large": "icon_lg.svg"
}
Campi del connector.json
| Campo | Obbligatorio | Descrizione |
|---|---|---|
id |
✅ | Identificatore unico (match con folder name) |
name |
✅ | Nome visualizzato |
version |
✅ | Intero per-connector, +1 a ogni modifica dei file |
version_string |
✅ | Semver (solo display) |
version_release_date |
✅ | Data ISO 8601 YYYY-MM-DD (solo display) |
type |
✅ | mcp_remote o mcp_local |
scope |
✅ | global o user |
requires |
✅ | Array di enum requisiti |
tags |
✅ | Array di tag |
auth |
✅ | Oggetto configurazione autenticazione |
docs |
✅ | Array di documentazione multilingua. llm_short_description è il campo che finisce nel system prompt dell'LLM — deve elencare i tool specifici e cosa fanno, non solo una descrizione generica. |
icon_small |
✅ | Nome file icona nella cartella locale |
icon_large |
✅ | Nome file icona nella cartella locale |
launch_command |
solo mcp_local |
Comando per avviare il server MCP |
transport |
solo mcp_local |
stdio (default) |
dependencies |
consigliato | Dipendenze Python/Node (array vuoto se solo stdlib) |
env |
se requires include ENV |
Variabili d'ambiente che l'utente deve fornire (schema per la UI) — vedi § Campo env |
setup_instructions |
consigliato | Passi per configurare il connector |
mcp_config |
solo mcp_local |
Configurazione per l'MCP client |
homepage |
opzionale | URL del servizio |
Enum riservati
type (tipo di connector)
| Valore | Descrizione | Esempi |
|---|---|---|
mcp_remote |
Server MCP hosted, accessibile via URL | Tavily, Weather |
mcp_local |
Script da eseguire localmente | Gmail, Google Calendar, WhatsApp |
script |
Script standalone (non MCP) | (futuro) |
scope (ambito di configurazione)
| Valore | Descrizione | Esempi |
|---|---|---|
global |
Una singola istanza/config per tutto il sistema | Tavily, Weather, Google Trends |
user |
Ogni utente ha la propria istanza/autenticazione | Gmail, WhatsApp, Google Calendar |
requires (prerequisiti)
| Valore | Descrizione |
|---|---|
API_KEY |
Richiede una chiave API da configurare |
OAUTH |
Richiede autenticazione OAuth (Google, ecc.) |
DOCKER |
Richiede Docker Engine |
NODE |
Richiede Node.js runtime |
PYTHON |
Richiede Python 3 |
SECRETS_DIR |
❌ Deprecato — la cartella secrets/ è rimossa dal modello; i connector devono usare ENV/SECRET (vedi § Sintassi placeholder) |
ENV |
Richiede variabili d'ambiente (dichiarate nel campo env del manifest) |
Campo auth
Struttura che descrive come il connector gestisce l'autenticazione:
// API key in query string
{"type": "api_key", "delivery": "query", "param": "tavilyApiKey"}
// API key in header
{"type": "api_key", "delivery": "header", "param": "X-API-Key"}
// OAuth2 — provider SOLO slug (Skald risolve endpoint + client secrets)
{"type": "oauth2", "provider": "google", "scopes": ["...", "..."]}
// OAuth2 con deliver (Skald inietta il JSON authorized_user via env var)
{"type": "oauth2", "provider": "google", "scopes": ["..."],
"deliver": {"as": "env", "format": "google_authorized_user", "env": "GMAIL_CREDS_JSON"}}
// OAuth2 con deliver su file (legacy)
{"type": "oauth2", "provider": "google", "scopes": ["..."],
"deliver": {"as": "file", "format": "google_authorized_user", "path": "{secrets}/gmail_creds.json"}}
// Password / app-password fornita via variabili d'ambiente
{"type": "password", "delivery": "env"}
// Nessuna autenticazione
{"type": "none"}
Campo deliver (solo OAuth2)
Dichiara come Skald consegna la credenziale OAuth ottenuta al processo del server MCP.
| Campo | Obbligatorio | Descrizione |
|---|---|---|
as |
✅ | "file" (su disco) o "env" (variabile d'ambiente) |
format |
✅ | Nome della serializzazione — es. "google_authorized_user" (JSON Google che from_authorized_user_file legge), "refresh_token", "access_token" |
path |
solo as=file |
Path con placeholder {secrets} (Skald lo espande a dir per-utente a runtime). DEVE matchare il path in mcp_config.env. |
env |
solo as=env |
Nome della variabile d'ambiente in cui Skald inietta l'intero JSON authorized_user. Non va dichiarata in mcp_config.env — Skald la inietta a runtime. |
Il feed NON contiene MAI: client_id, client_secret, endpoint URL, redirect_uri. Questi sono risolti lato Skald a partire dal nome del provider.
Campo env (variabili d'ambiente)
Usato quando requires include ENV. È un array che dichiara le variabili
d'ambiente che l'utente deve fornire per far funzionare il connector; nessuna
credenziale finisce su disco né in secrets/ — l'host raccoglie i valori,
obbliga la compilazione dei campi obbligatori, e li inietta come environment
nel processo del server MCP al lancio. Il server le legge da os.environ.
"env": [
{
"name": "EMAIL_IMAP_HOST", // nome della variabile d'ambiente
"label": "IMAP host", // etichetta per la UI
"description": "IMAP server hostname (es. imap.gmail.com)",
"required": true, // se true, l'host deve obbligare la compilazione
"secret": false, // se true, la UI la maschera e la tratta come segreto
"example": "imap.gmail.com" // placeholder/esempio (opzionale)
},
{
"name": "EMAIL_PASSWORD",
"label": "Password / app password",
"description": "Password o app-password del provider",
"required": true,
"secret": true,
"default": "" // valore di default se non obbligatorio (opzionale)
}
]
| Campo | Obbligatorio | Descrizione |
|---|---|---|
name |
✅ | Nome della variabile d'ambiente (UPPER_SNAKE_CASE) |
label |
✅ | Etichetta breve per la UI |
description |
✅ | Testo di aiuto |
required |
✅ | Se true, l'host obbliga l'utente a fornire un valore |
secret |
consigliato | Se true, valore sensibile (mascherato, non loggato) |
default |
opzionale | Valore usato se non fornito (solo per non obbligatorie) |
example |
opzionale | Placeholder di esempio per la UI |
Sintassi placeholder (unificata)
Ogni valore che skald deve riempire a runtime con un dato fornito dall'utente
usa uno di due token, ovunque compaia (URL, mcp_config.env, verify.command):
| Token | Significato | Esempio |
|---|---|---|
{ENV:NAME} |
Variabile non sensibile (hostname, porta, username…) | {ENV:EMAIL_IMAP_HOST} |
{SECRET:NAME} |
Variabile sensibile (password, API key, token) | {SECRET:EMAIL_PASSWORD} |
NAME è il nome dichiarato nell'array env[] (campo name). skald raccoglie
i valori tramite un form (maschera i {SECRET:}), li inietta come environment
nel processo del server MCP / verify, e sostituisce i token nel manifest.
Regole:
- I token non riconosciuti (es.
{secrets}/…, legacy{key},{env:NAME}) sono deprecati: skald non li sostituisce e il manifest va aggiornato. {SECRET:<auth.param>}è riservato alla chiave primaria quandoauth.type = "api_key"(es. Tavily:{SECRET:tavilyApiKey}). skald tratta quel valore anche come API key per il routing bearer/header.- Un token
{ENV:X}o{SECRET:X}la cuiXnon è nell'env[]del manifest viene sostituito con stringa vuota (l'host non può indovinarlo).
Deprecations
| Token | Stato | Sostituzione |
|---|---|---|
{key} |
❌ deprecato | {SECRET:<auth.param>} |
{env:NAME} |
❌ deprecato | {ENV:NAME} |
{secrets}/… |
❌ deprecato | Il connector deve dichiarare il path come {ENV:…} (la cartella secrets/ è rimossa dal modello) |
Campo verify (test prima del salvataggio)
Dichiara un comando shell che skald esegue dopo che l'utente ha compilato il form e prima di persistere l'attivazione. Serve a verificare che le credenziali appena inserite funzionino davvero.
"verify": {
"command": "python3 verify.py",
"timeout_secs": 20
}
| Campo | Obbligatorio | Descrizione |
|---|---|---|
command |
✅ | Comando shell. Gira nello stesso sandbox del server: container skald-{userid} per mcp_local user, host per mcp_remote global. Le env/secret dichiarati sono iniettate |
timeout_secs |
opzionale | Default 15. skald killa il processo allo scadere |
Convenzione output
Il comando deve stampare un singolo oggetto JSON su stdout e nient'altro:
{"ok": true, "message": "IMAP and SMTP authentication successful", "details": {"imap": "...", "smtp": "..."}}
{"ok": false, "message": "IMAP login failed: INVALID_CREDENTIALS"}
| Campo | Tipo | Descrizione |
|---|---|---|
ok |
bool | true = test passato |
message |
string | Messaggio mostrato all'utente (mai loggare secret qui dentro) |
details |
object | Opzionale, dettagli strutturati mostrati in <pre> |
Exit code: 0 su successo, ≠ 0 su fallimento (skald usa l'exit code come
fallback se il parse JSON fallisce). Mai stampare credenziali nel
message/details.
Dove mettere lo script
Se command referenzia un file (es. verify.py), il file va:
- Aggiunto all'array
files[]inconnectors.json(consha256esize) - Salvato nella cartella del connector (
<id>/verify.py)
skald lo scarica, ne verifica lo SHA-256 contro l'indice, e lo rende disponibile
nello stesso path del server principale (container per i mcp_local, dir
./scripts/<id>/ sull'host per i mcp_remote).
Senza verify
Se verify manca, skald non esegue nessun test — l'attivazione è diretta
come oggi. Il connector va in auth_state='ready' senza verifica. Per i
mcp_remote non c'è fallback handshake: l'autore del manifest decide se vuole
il test scrivendo verify.
Convenzioni icone
- Formato: SVG per icone vettoriali (meglio per retina/zoom), PNG per raster
- Nome:
icon_sm.{svg|png}(small, ~48×48px),icon_lg.{svg|png}(large, ~96×96px) - Path: relativo alla cartella del connector
- Nell'indice la path è
{folder}/{filename}(es.gmail/icon_sm.svg)
Integrità file (sha256)
- Gli hash sha256 sono solo in
connectors.json(l'indice) connector.jsonnon contiene hash di sé stesso — è il file manifesto- L'indice è l'unica radice di fiducia; in futuro si può firmare digitalmente solo l'indice
- Sui file locali, per calcolare/aggiornare gli hash:
python3 -c "import hashlib; print(hashlib.sha256(open('file.py','rb').read()).hexdigest())"
Workflow locale
- Lavora su file nella cartella
connectors/ - Modifica
connectors.json,connector.json, script, icone - Prima del deploy aggiorna gli sha256 in
connectors.json:python3 scripts/update_hashes.py - Fai l'upload sul server con rsync:
rsync -avz --delete connectors/ dguiducci@skald-server:/var/www/connectors.skaldagent.net/ - Verifica su
https://connectors.skaldagent.net/
Deploy su server remoto
Il server remoto è:
- Host: skald-home-server (192.168.1.100 / 145.40.169.107)
- User: dguiducci
- Path:
/var/www/connectors.skaldagent.net/ - Proprietario:
caddy:caddy - Sudo: richiesto per scrivere in
/var/www/
Comando di deploy (da eseguire con sudo o con rsync):
# Sync se il server permette rsync via SSH
rsync -avz --delete ./connectors/ dguiducci@skald-server:/var/www/connectors.skaldagent.net/
Dopo il deploy, verificare i permessi:
sudo chown -R caddy:caddy /var/www/connectors.skaldagent.net/
sudo find /var/www/connectors.skaldagent.net/ -type f -exec chmod 644 {} \;
Connector attuali
| ID | Nome | Tipo | Scope | Auth | Verify |
|---|---|---|---|---|---|
context7 |
Context7 | mcp_remote |
global |
none | verify.py (MCP initialize probe) |
gmaps |
Google Maps | mcp_local |
global |
api_key (env: GOOGLE_MAPS_API_KEY) |
verify.py (Geocoding API probe) |
exa |
Exa | mcp_remote |
global |
api_key ({SECRET:exaApiKey} in URL — optional, free tier) |
verify.py (MCP initialize probe) |
tavily |
Tavily | mcp_remote |
global |
api_key ({SECRET:tavilyApiKey} in URL) |
verify.py (HTTP probe /search) |
serpapi-flights |
SerpAPI Flights | mcp_remote |
global |
api_key ({SECRET:serpapiApiKey} in URL) |
verify.py (MCP initialize probe) |
gmail |
Gmail | mcp_local |
user |
oauth2 (Google) + deliver: env/google_authorized_user (via GMAIL_CREDS_JSON) |
⏳ Fase 2 — OAuth via loopback listener |
gcal |
Google Calendar | mcp_local |
user |
oauth2 (Google) + deliver: env/google_authorized_user (via GCAL_CREDS_JSON) |
verify.py (creds load + API probe) |
drive |
Google Drive | mcp_local |
user |
oauth2 (Google) + deliver: env/google_authorized_user (via DRIVE_CREDS_JSON) |
verify.py (creds load + Drive API probe) |
email |
Email (IMAP/SMTP) | mcp_local |
user |
password (env) | verify.py (IMAP+SMTP probe) |
firecrawl |
Firecrawl | mcp_local |
global |
api_key (env) | — |
http-fetch |
HTTP Fetch | mcp_local |
global |
none | — |
ssh |
SSH Remote Access | mcp_local |
user |
none (auth runtime per-alias) | — |
weather |
Weather (Open-Meteo) | mcp_local |
global |
none | — |
wikipedia |
Wikipedia | mcp_local |
global |
none | — |
whatsapp |
mcp_local |
user |
qr | — | |
Stato del verify-before-save in skald: exa, drive, email, tavily, e gcal hanno verify completo |
|||||
(script + JSON output); gmail aspetta la Fase 2 (OAuth via loopback listener). |
|||||
Un connector senza verify viene attivato senza test — vedi § Senza verify. |