add playwright connector (v1 / 1.0.0) + commit pending docs reorganization
- new global mcp_local connector wrapping @playwright/mcp@0.0.79: index.js argv wrapper (--headless --isolated --no-sandbox) importing the package cli.js, postinstall downloads chromium-only browser, verify.js headless-launch probe, 24 tools with display names - also carries the pending docs work: changelog extracted to CHANGELOG.md, manifest guide, SKALD.md/CLAUDE.md updates
This commit is contained in:
@@ -1,64 +1,17 @@
|
||||
# Skald Connectors Marketplace
|
||||
|
||||
## What it is
|
||||
|
||||
### 2026-08-10 — http-fetch + firecrawl: fix `npx` (v5 / 1.1.0)
|
||||
The **marketplace** is the catalog of tested connectors for Skald. Each connector is an adapter that lets Skald interface with an external service (API, email, calendar, search, messaging, etc.).
|
||||
|
||||
Entrambi i connector erano **non funzionanti**: dichiaravano `mcp_config: {command: "npx", args: ["-y", "<package>"]}` e non spedivano nessun file di codice (solo `connector.json` + icone).
|
||||
Two types of connectors:
|
||||
|
||||
- **`npx -y <package>` non è esprimibile in skald.** Per un `type: mcp_local` skald tratta `args[0]` come il **nome del file da eseguire**, non come un argomento: all'install calcola `script_path = "<id>/" + args[0]` e azzera `args_json` (`marketplace.rs::install`), poi `global_enable` (`api/mcp.rs`) lo risolve in path assoluto e lancia `<command> <abs>`. Il comando reale diventava `npx /…/connectors/http-fetch/-y` — un path inesistente, con `-y` e il nome del package spariti. Il processo non rispondeva mai a `initialize`, `start_server` falliva.
|
||||
- **Il fallimento era silenzioso**: `global_enable` restituisce comunque HTTP 200 con un campo `error` nel body, quindi la UI mostrava il connector come abilitato mentre il runtime non aveva nessun server. E `render_mcp_list` (`loop_adapters/system.rs`) costruisce la tabella `## MCP servers` da `mcp.tools()`, cioè lo **stato vivo del runtime**, non dal DB → il connector risultava attivato e concesso all'utente ma **assente dal system context**. ⚠️ Questa combinazione (200 + `error` nel body) rende invisibile in UI qualsiasi connector che non parte: vale la pena farla emergere lato skald.
|
||||
- **Fix — wrapper di due file** per entrambi: `package.json` che pinna il package upstream (`mcp-fetch-server@1.1.2`, `firecrawl-mcp@3.23.7`) e `index.js` che lo importa per side-effect (il modulo avvia il loop JSON-RPC su stdio all'import). `mcp_config` diventa `{command: "node", args: ["index.js"], transport: "stdio"}`, cioè un vero `local_script`: `ensure_installed_host` fa `npm ci --omit=dev || npm install --omit=dev` nella cartella del connector prima del lancio, esattamente come per whatsapp. Nessun `node_modules` spedito, nessun lockfile (come whatsapp).
|
||||
- **Rimossi i campi legacy** `launch_command`, `transport` top-level e `dependencies` (`dependencies` è solo per la card, come già visto su gmaps; `transport` va dentro `mcp_config`).
|
||||
- **firecrawl: rimosso `mcp_config.env: {"FIRECRAWL_API_KEY": "{SECRET:FIRECRAWL_API_KEY}"}`** — inerte, stesso caso di gmaps del 2026-08-10: `apply_key_placeholder` sostituisce i token solo nella URL, mai nei valori di `env`. Funziona perché la form admin manda `env` e quel payload sovrascrive `entry.env_json`.
|
||||
- **firecrawl**: aggiunto `firecrawl_developer_search` a `tools[]` (27 tool live contro i 26 dichiarati, verificato su 3.23.7); `requires` `["NODE"]` → `["NODE", "API_KEY"]`.
|
||||
- **Versioni riallineate** manifest↔fragment a `5` / `1.1.0` / `2026-08-10` per entrambi: erano 2/1.0.1 (manifest) vs 4/1.0.3 (fragment), e skald preferisce il manifest — quindi `installed_version` restava 2 e il badge "Update available" non sarebbe mai comparso.
|
||||
- **Requisito host**: sono connector `scope: global`, girano sull'**host** e non nel container. `mcp-fetch-server` vuole Node ≥18, `firecrawl-mcp` vuole Node ≥**22**.
|
||||
- Testati end-to-end riproducendo il path di skald (`npm ci || npm install` + `node <abs>/index.js`): `initialize`, `tools/list` e una `tools/call` reale, stdout solo JSON-RPC, stderr pulito ✅
|
||||
- Indice rigenerato con compile.py ✅
|
||||
- **`mcp_remote`** — an already-hosted MCP server, reachable via URL (e.g. Tavily).
|
||||
- **`mcp_local`** — a Python/Node script to run client-side (e.g. Gmail, Google Calendar).
|
||||
|
||||
⚠️ **Per applicarlo su un'istanza che ha già il connector installato non basta l'Update.** `refresh_connector_after_reinstall` (`skald/accessors.rs`) aggiorna della riga `mcp_global_servers` **solo la `description`**, poi riparte da quella riga: `command` e `args_json` restano quelli snapshottati al primo `global_enable`, cioè ancora `npx` + `/…/connectors/<id>/-y`. Procedura: deploy → **Update** dal marketplace (riscrive `script_path` nel catalogo) → aprire la pagina del connector e **ri-salvare la config**, che è l'unica chiamata che ricalcola `command`/`args` e lancia `ensure_installed_host`. Stessa cura già annotata per gmaps. Da valutare lato skald se il refresh debba ri-derivare anche command/args.
|
||||
## Local references
|
||||
|
||||
### 2026-08-10 — gmaps: fix dipendenze + verify (v6 / 1.1.0)
|
||||
- **Aggiunto `requirements.txt` (`googlemaps>=4.10.0`)** — era l'unico connector python senza. Le dipendenze erano dichiarate nel campo `dependencies` del manifest, che skald usa **solo per la card**: `ensure_installed_host` guarda esclusivamente `requirements.txt` / `package.json`. Risultato: `.pydeps` vuoto e log pieno di `No module named 'googlemaps'`, con il server che rispondeva comunque a `tools/list` (→ `connected — 6 tool(s)` su un connector non funzionante).
|
||||
- **Cablato il `verify`** (`python3 verify.py`, 20s): `verify.py` era shippato ma il manifest non aveva il blocco `verify`, e skald legge `verify_command` solo da lì. Ora un'abilitazione con dipendenze rotte fallisce visibilmente invece di partire in silenzio.
|
||||
- **`verify.py` si mette `.pydeps` su `sys.path`**: skald imposta `PYTHONPATH` solo per il processo *server* (`global_row_spec`), mentre il verify gira come `sh -c "python3 verify.py"` senza. Senza questa riga il verify fallirebbe con "Missing dependency" anche su un connector installato correttamente, disabilitando la riga. ⚠️ **Stesso rischio latente per ogni connector con `verify` che importa dipendenze non-stdlib** (gcal in container): da controllare.
|
||||
- **Rimosso `mcp_config.env: {"GOOGLE_MAPS_API_KEY": "{SECRET:…}"}`** (introdotto il 2026-07-23): inerte. `apply_key_placeholder` sostituisce i token `{SECRET:}`/`{ENV:}` **solo nella URL**, mai nei valori di `env`. Funzionava perché la form admin manda `env` e quel payload sovrascrive `entry.env_json`; a form vuota il processo avrebbe ricevuto la stringa letterale. ⚠️ **La spec in questo file e in CLAUDE.md dice il contrario** — o si corregge la spec, o si estende la sostituzione ai valori di `env` lato skald.
|
||||
- **Versioni riallineate** manifest↔fragment a `6` / `1.1.0` / `2026-08-10`: erano 2/1.0.1 vs 5/1.0.4, e skald preferisce il manifest (`manifest.version.or(entry.version)`) — quindi `installed_version` restava 2 e il badge "Update available" non sarebbe mai comparso.
|
||||
- `requires`: `ENV` → `API_KEY`; messaggi d'errore del server ripuliti dai riferimenti a `secrets/gmaps_api_key.txt` (path deprecato).
|
||||
- Indice rigenerato con compile.py ✅
|
||||
|
||||
### 2026-08-07 — Nuovo connector: LinkedIn
|
||||
- Aggiunto connector `linkedin` (mcp_local, scope user): server.py + session.py + verify.py + icone PNG
|
||||
- Aggiunto a `connectors/index.json`, indice rigenerato con compile.py (17 connector totali)
|
||||
- `.gitignore` aggiornato per ignorare `.claude/`
|
||||
- Deploy su connectors.skaldagent.net via alias `skaldserver` (192.168.1.100, LAN — niente Tailscale)
|
||||
|
||||
### 2026-07-23 — gmaps: fix env var injection
|
||||
- Aggiunto `mcp_config.env` in `connector.json` per iniettare `GOOGLE_MAPS_API_KEY` nel processo MCP
|
||||
- Il connector era dichiarato come `delivery: env` ma senza `mcp_config.env` Skald non sapeva passare la variabile al processo Python
|
||||
- Version bump: fragment 4→5, connector 1→2
|
||||
- Indice rigenerato con compile.py ✅
|
||||
|
||||
### 2026-07-23 — Context7 icon update (PNG)
|
||||
- Sostituite icone Context7 da SVG a PNG (icona nuova fornita dall'utente):
|
||||
- `icon_sm.png` — 48×48 (2.7 KB)
|
||||
- `icon_lg.png` — 96×96 (4.5 KB)
|
||||
- Vecchi SVG rimossi, riferimenti aggiornati in fragment.json e connector.json
|
||||
- Version bump: fragment 3→4, connector 1→2
|
||||
- Indice rigenerato con compile.py ✅
|
||||
## Reference locale
|
||||
|
||||
### 2026-07-23 — SerpAPI Flights icon update (PNG)
|
||||
- Sostituite icone SerpAPI Flights da SVG a PNG:
|
||||
- `icon_sm.png` — 48×48 (2.9 KB)
|
||||
- `icon_lg.png` — 96×96 (6.8 KB)
|
||||
- Vecchi SVG rimossi, riferimenti aggiornati in fragment.json e connector.json
|
||||
- Version bump: fragment 4→5, connector 2→3
|
||||
- Indice rigenerato con compile.py ✅
|
||||
|
||||
- **[docs/connector.manifest_guide.md](docs/connector.manifest_guide.md)** — Guida ufficiale per generare connector corretti (copiata da `skald-circle/blueprint/`)
|
||||
|
||||
_Updated: 2026-07-23_
|
||||
- **[docs/connector.manifest_guide.md](docs/connector.manifest_guide.md)** — Official guide for producing correct connectors (copied from `skald-circle/blueprint/`)
|
||||
|
||||
**Remote**: `https://git.skaldagent.net/dguiducci/skald-connectors.git`
|
||||
**Live**: `https://connectors.skaldagent.net/`
|
||||
@@ -66,75 +19,65 @@ _Updated: 2026-07-23_
|
||||
|
||||
## 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.
|
||||
The **`main`** branch is the **release** branch. Only production-ready code ends up here.
|
||||
Development and alpha versions will live on separate branches in the future.
|
||||
|
||||
Deploy rapido con MCP SSH:
|
||||
Quick deploy with MCP SSH:
|
||||
|
||||
```bash
|
||||
mcp__ssh__exec alias=skald-home-server command="/home/dguiducci/marketplace_deploy.sh"
|
||||
```
|
||||
|
||||
Oppure via SSH classico:
|
||||
Or via classic SSH:
|
||||
|
||||
```bash
|
||||
ssh dguiducci@skald-home-server /home/dguiducci/marketplace_deploy.sh
|
||||
```
|
||||
|
||||
Lo script sul server fa:
|
||||
The script on the server does:
|
||||
1. `git pull` in `/home/dguiducci/repos/skald-connectors/`
|
||||
2. `cp -r connectors/*` in `/var/www/connectors.skaldagent.net/`
|
||||
2. `cp -r connectors/*` into `/var/www/connectors.skaldagent.net/`
|
||||
|
||||
La directory `/var/www/connectors.skaldagent.net/` è di proprietà di `dguiducci`,
|
||||
quindi non serve sudo per la copia.
|
||||
The `/var/www/connectors.skaldagent.net/` directory is owned by `dguiducci`,
|
||||
so no sudo is needed for the copy.
|
||||
|
||||
**Prima del deploy**, ricordarsi di rigenerare l'indice:
|
||||
**Before deploying**, remember to regenerate the index:
|
||||
```bash
|
||||
python3 scripts/compile.py
|
||||
```
|
||||
|
||||
## 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:
|
||||
Every MCP tool must expose a friendly name for the Skald UI. Two ways, in order of preference:
|
||||
|
||||
1. **Via script MCP (preferito)** — aggiungere `"title": "Friendly Name"` nella definizione di ogni tool dentro `tools/list`. Funziona per tutti gli script locali (Python/Node) che controlliamo.
|
||||
2. **Via manifest (fallback)** — aggiungere `"tools": [{"name": "...", "display_name": "..."}]` in `connector.json` **e** in `connectors.json`. Usato solo per connector remoti o package esterni (es. `npx -y firecrawl-mcp`).
|
||||
1. **Via the MCP script (preferred)** — add `"title": "Friendly Name"` in the definition of each tool inside `tools/list`. Works for all local scripts (Python/Node) that we control.
|
||||
2. **Via the manifest (fallback)** — add `"tools": [{"name": "...", "display_name": "..."}]` in `connector.json` **and** in `connectors.json`. Used only for remote connectors or external packages (e.g. `npx -y firecrawl-mcp`).
|
||||
|
||||
**Ordine di risoluzione** (Skald li prova in quest'ordine):
|
||||
1. `tools[].display_name` dal manifest
|
||||
2. `title` dal `tools/list` dell'MCP server
|
||||
3. Prettify automatico del nome raw (`send_message` → "Send Message")
|
||||
**Resolution order** (Skald tries them in this order):
|
||||
1. `tools[].display_name` from the manifest
|
||||
2. `title` from the MCP server's `tools/list`
|
||||
3. Automatic prettify of the raw name (`send_message` → "Send Message")
|
||||
|
||||
Stato attuale (2026-07-21): tutti i 13 connector del marketplace hanno `title` nello script o `tools[]` nel manifest.
|
||||
Current status (2026-07-21): all 13 marketplace connectors have `title` in the script or `tools[]` in the manifest.
|
||||
|
||||
|
||||
## Struttura directory
|
||||
## Directory structure
|
||||
|
||||
```
|
||||
connectors/
|
||||
├── index.json ← lista ordinata degli id dei connector (input per compile.py)
|
||||
├── connectors.json ← INDICE COMPILATO (generato da compile.py, non editare)
|
||||
├── compile.py ← genera connectors.json
|
||||
├── index.html ← Catalogo UI (legge connectors.json via fetch)
|
||||
├── index.json ← ordered list of connector ids (input for compile.py)
|
||||
├── connectors.json ← COMPILED INDEX (generated by compile.py, do not edit)
|
||||
├── compile.py ← generates connectors.json (launched by scripts/compile.py)
|
||||
├── index.html ← Catalog UI (reads connectors.json via fetch)
|
||||
├── oauth/
|
||||
│ └── show.html ← OAuth callback receiver (client-side, no backend)
|
||||
├── gmail/ ← Un connector per cartella
|
||||
│ ├── fragment.json ← Frammento dell'indice (id, name, type, ..., SENZA files[])
|
||||
│ ├── 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. 96×96)
|
||||
│ └── show.html ← OAuth callback receiver
|
||||
├── gmail/ ← one connector per folder
|
||||
│ ├── fragment.json ← index fragment (id, name, type, ..., WITHOUT files[])
|
||||
│ ├── connector.json ← technical configuration (mcp_config, auth.deliver, ...)
|
||||
│ ├── gmail_mcp_server.py ← MCP script
|
||||
│ ├── gmail_oauth_setup.py ← OAuth setup script
|
||||
│ ├── requirements.txt ← Python dependencies
|
||||
│ ├── icon_sm.svg ← small icon (48×48)
|
||||
│ └── icon_lg.svg ← large icon (e.g. 96×96)
|
||||
├── email/
|
||||
│ ├── fragment.json
|
||||
│ ├── connector.json
|
||||
@@ -143,27 +86,13 @@ connectors/
|
||||
│ ├── requirements.txt
|
||||
│ ├── icon_sm.svg
|
||||
│ └── icon_lg.svg
|
||||
├── ssh/
|
||||
│ ├── fragment.json
|
||||
│ ├── connector.json
|
||||
│ ├── ssh_mcp_server.py
|
||||
│ ├── requirements.txt
|
||||
│ ├── icon_sm.svg
|
||||
│ └── icon_lg.svg
|
||||
└── tavily/
|
||||
├── fragment.json
|
||||
├── connector.json
|
||||
├── verify.py
|
||||
├── icon_sm.png
|
||||
└── icon_lg.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.
|
||||
This is the **single root of trust**. It contains `type`, `scope`, and the sha256 of each connector's files.
|
||||
It has no hash of itself — in the future it may be digitally signed.
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -192,8 +121,6 @@ Non ha hash di sé stesso — in futuro potrà essere firmato digitalmente.
|
||||
"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},
|
||||
@@ -205,28 +132,28 @@ Non ha hash di sé stesso — in futuro potrà essere firmato digitalmente.
|
||||
}
|
||||
```
|
||||
|
||||
### Campi dell'indice
|
||||
### Index fields
|
||||
|
||||
| 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) |
|
||||
| Field | Required | Description |
|
||||
|-------|----------|-------------|
|
||||
| `id` | ✅ | Unique identifier (kebab-case) |
|
||||
| `name` | ✅ | Displayed name |
|
||||
| `type` | ✅ | `mcp_remote` or `mcp_local` |
|
||||
| `scope` | ✅ | `global` or `user` |
|
||||
| `icon_small` | ✅ | Path relative to the marketplace root |
|
||||
| `icon_large` | ✅ | Path relative to the marketplace root |
|
||||
| `user_description` | ✅ | Short description for the UI |
|
||||
| `requires` | ✅ | Array of requirement enums |
|
||||
| `tags` | ✅ | Array of tags for filtering |
|
||||
| `folder` | ✅ | Name of the connector folder |
|
||||
| `version` | ✅ | Per-connector integer, +1 on every file change |
|
||||
| `version_string` | ✅ | Semver (display only) |
|
||||
| `version_release_date` | ✅ | ISO 8601 date YYYY-MM-DD (display only) |
|
||||
| `files` | ✅ | Array of files with sha256 (NO self-hash) |
|
||||
|
||||
## Schema — connector.json (per cartella)
|
||||
## Schema — connector.json (per folder)
|
||||
|
||||
Configurazione tecnica per l'attivazione del connector.
|
||||
Technical configuration for connector activation.
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -280,63 +207,63 @@ Configurazione tecnica per l'attivazione del connector.
|
||||
}
|
||||
```
|
||||
|
||||
### Campi del connector.json
|
||||
### connector.json fields
|
||||
|
||||
| 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 descrivere COSA FA il connector, non elencare i tool (l'LLM li vede dopo `activate_tools`). Esempio: *"Weather — current conditions, 16-day forecast, and AQI data for any location."*
|
||||
| `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 |
|
||||
| Field | Required | Description |
|
||||
|-------|----------|-------------|
|
||||
| `id` | ✅ | Unique identifier (matches the folder name) |
|
||||
| `name` | ✅ | Displayed name |
|
||||
| `version` | ✅ | Per-connector integer, +1 on every file change |
|
||||
| `version_string` | ✅ | Semver (display only) |
|
||||
| `version_release_date` | ✅ | ISO 8601 date YYYY-MM-DD (display only) |
|
||||
| `type` | ✅ | `mcp_remote` or `mcp_local` |
|
||||
| `scope` | ✅ | `global` or `user` |
|
||||
| `requires` | ✅ | Array of requirement enums |
|
||||
| `tags` | ✅ | Array of tags |
|
||||
| `auth` | ✅ | Authentication configuration object |
|
||||
| `docs` | ✅ | Array of multilingual documentation. **`llm_short_description`** is the field that ends up in the LLM's system prompt — it must describe WHAT the connector DOES, not list its tools (the LLM sees them after `activate_tools`). Example: *"Weather — current conditions, 16-day forecast, and AQI data for any location."* |
|
||||
| `icon_small` | ✅ | Icon filename in the local folder |
|
||||
| `icon_large` | ✅ | Icon filename in the local folder |
|
||||
| `launch_command` | `mcp_local` only | Command to start the MCP server |
|
||||
| `transport` | `mcp_local` only | `stdio` (default) |
|
||||
| `dependencies` | recommended | Python/Node dependencies (empty array if stdlib only) |
|
||||
| `env` | if `requires` includes `ENV` | Environment variables the user must provide (schema for the UI) — see § The env field |
|
||||
| `setup_instructions` | recommended | Steps to configure the connector |
|
||||
| `mcp_config` | `mcp_local` only | Configuration for the MCP client |
|
||||
| `homepage` | optional | Service URL |
|
||||
|
||||
## Enum riservati
|
||||
## Reserved enums
|
||||
|
||||
### type (tipo di connector)
|
||||
### type (connector type)
|
||||
|
||||
| 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)* |
|
||||
| Value | Description | Examples |
|
||||
|-------|-------------|----------|
|
||||
| `mcp_remote` | Hosted MCP server, reachable via URL | Tavily, Weather |
|
||||
| `mcp_local` | Script to run locally | Gmail, Google Calendar, WhatsApp |
|
||||
| `script` | Standalone script (non-MCP) | *(future)* |
|
||||
|
||||
### scope (ambito di configurazione)
|
||||
### scope (configuration scope)
|
||||
|
||||
| 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 |
|
||||
| Value | Description | Examples |
|
||||
|-------|-------------|----------|
|
||||
| `global` | A single instance/config for the whole system | Tavily, Weather, Google Trends |
|
||||
| `user` | Each user has their own instance/authentication | Gmail, WhatsApp, Google Calendar |
|
||||
|
||||
### requires (prerequisiti)
|
||||
### requires (prerequisites)
|
||||
|
||||
| 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) |
|
||||
| Value | Description |
|
||||
|-------|-------------|
|
||||
| `API_KEY` | Requires an API key to configure |
|
||||
| `OAUTH` | Requires OAuth authentication (Google, etc.) |
|
||||
| `DOCKER` | Requires Docker Engine |
|
||||
| `NODE` | Requires Node.js runtime |
|
||||
| `PYTHON` | Requires Python 3 |
|
||||
| `SECRETS_DIR` | ❌ **Deprecated** — the `secrets/` folder is removed from the model; connectors must use `ENV`/`SECRET` (see § Placeholder syntax) |
|
||||
| `ENV` | Requires environment variables (declared in the manifest `env` field) |
|
||||
|
||||
## Campo auth
|
||||
## The auth field
|
||||
|
||||
Struttura che descrive come il connector gestisce l'autenticazione:
|
||||
Structure describing how the connector handles authentication:
|
||||
|
||||
```json
|
||||
// API key in query string
|
||||
@@ -345,112 +272,113 @@ Struttura che descrive come il connector gestisce l'autenticazione:
|
||||
// API key in header
|
||||
{"type": "api_key", "delivery": "header", "param": "X-API-Key"}
|
||||
|
||||
// OAuth2 — provider SOLO slug (Skald risolve endpoint + client secrets)
|
||||
// OAuth2 — provider is ONLY a slug (Skald resolves endpoints + client secrets)
|
||||
{"type": "oauth2", "provider": "google", "scopes": ["...", "..."]}
|
||||
|
||||
// OAuth2 con deliver (Skald inietta il JSON authorized_user via env var)
|
||||
// OAuth2 with deliver (Skald injects the authorized_user JSON 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)
|
||||
// OAuth2 with file-based deliver (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
|
||||
// Password / app-password provided via environment variables
|
||||
{"type": "password", "delivery": "env"}
|
||||
|
||||
// Nessuna autenticazione
|
||||
// No authentication
|
||||
{"type": "none"}
|
||||
```
|
||||
|
||||
### Campo deliver (solo OAuth2)
|
||||
### The deliver field (OAuth2 only)
|
||||
|
||||
Dichiara **come** Skald consegna la credenziale OAuth ottenuta al processo del server MCP.
|
||||
Declares **how** Skald delivers the obtained OAuth credential to the MCP server process.
|
||||
|
||||
| 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. |
|
||||
| Field | Required | Description |
|
||||
|-------|----------|-------------|
|
||||
| `as` | ✅ | `"file"` (on disk) or `"env"` (environment variable) |
|
||||
| `format` | ✅ | Name of the serialization — e.g. `"google_authorized_user"` (Google JSON that `from_authorized_user_file` reads), `"refresh_token"`, `"access_token"` |
|
||||
| `path` | `as=file` only | Path with the `{secrets}` placeholder (Skald expands it to a per-user dir at runtime). MUST match the path in `mcp_config.env`. |
|
||||
| `env` | `as=env` only | Name of the environment variable into which Skald injects the entire authorized_user JSON. **Must not be declared in `mcp_config.env`** — Skald injects it at 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`.
|
||||
The feed NEVER contains: `client_id`, `client_secret`, `endpoint` URL, `redirect_uri`. These are resolved on the Skald side from the `provider` name.
|
||||
|
||||
## Campo env (variabili d'ambiente)
|
||||
## The env field (environment variables)
|
||||
|
||||
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`.
|
||||
Used when `requires` includes `ENV`. It is an **array** declaring the environment
|
||||
variables the user must provide to make the connector work; **no credential
|
||||
lands on disk nor in `secrets/`** — the host collects the values, enforces
|
||||
filling in the required fields, and injects them as environment into the MCP
|
||||
server process at launch. The server reads them from `os.environ`.
|
||||
|
||||
```json
|
||||
"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_IMAP_HOST", // name of the environment variable
|
||||
"label": "IMAP host", // label for the UI
|
||||
"description": "IMAP server hostname (e.g. imap.gmail.com)",
|
||||
"required": true, // if true, the host must enforce this field
|
||||
"secret": false, // if true, the UI masks it and treats it as a secret
|
||||
"example": "imap.gmail.com" // placeholder/example (optional)
|
||||
},
|
||||
{
|
||||
"name": "EMAIL_PASSWORD",
|
||||
"label": "Password / app password",
|
||||
"description": "Password o app-password del provider",
|
||||
"description": "Password or app-password of the provider",
|
||||
"required": true,
|
||||
"secret": true,
|
||||
"default": "" // valore di default se non obbligatorio (opzionale)
|
||||
"default": "" // default value if not required (optional)
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
| 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 |
|
||||
| Field | Required | Description |
|
||||
|-------|----------|-------------|
|
||||
| `name` | ✅ | Name of the environment variable (UPPER_SNAKE_CASE) |
|
||||
| `label` | ✅ | Short label for the UI |
|
||||
| `description` | ✅ | Help text |
|
||||
| `required` | ✅ | If `true`, the host forces the user to provide a value |
|
||||
| `secret` | recommended | If `true`, sensitive value (masked, not logged) |
|
||||
| `default` | optional | Value used when not provided (non-required fields only) |
|
||||
| `example` | optional | Example placeholder for the UI |
|
||||
|
||||
## Sintassi placeholder (unificata)
|
||||
## Placeholder syntax (unified)
|
||||
|
||||
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`):
|
||||
Every value skald must fill at runtime with user-provided data uses **one of
|
||||
two tokens**, wherever it appears (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}` |
|
||||
| Token | Meaning | Example |
|
||||
|-------|---------|---------|
|
||||
| `{ENV:NAME}` | Non-sensitive variable (hostname, port, username…) | `{ENV:EMAIL_IMAP_HOST}` |
|
||||
| `{SECRET:NAME}` | Sensitive variable (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.
|
||||
`NAME` is the name declared in the `env[]` array (the `name` field). skald
|
||||
collects the values via a form (masking `{SECRET:}` fields), injects them as
|
||||
environment into the MCP server / verify process, and substitutes the tokens
|
||||
in the 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 quando
|
||||
`auth.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 cui `X` non è nell'`env[]` del manifest
|
||||
viene sostituito con stringa vuota (l'host non può indovinarlo).
|
||||
Rules:
|
||||
- Unrecognized tokens (e.g. `{secrets}/…`, legacy `{key}`, `{env:NAME}`) are
|
||||
**deprecated**: skald does not substitute them and the manifest must be updated.
|
||||
- `{SECRET:<auth.param>}` is reserved for the primary key when
|
||||
`auth.type = "api_key"` (e.g. Tavily: `{SECRET:tavilyApiKey}`). skald also
|
||||
treats that value as the API key for bearer/header routing.
|
||||
- A `{ENV:X}` or `{SECRET:X}` token whose `X` is not in the manifest's `env[]`
|
||||
is substituted with an empty string (the host cannot guess it).
|
||||
|
||||
### 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) |
|
||||
| Token | Status | Replacement |
|
||||
|-------|--------|-------------|
|
||||
| `{key}` | ❌ deprecated | `{SECRET:<auth.param>}` |
|
||||
| `{env:NAME}` | ❌ deprecated | `{ENV:NAME}` |
|
||||
| `{secrets}/…` | ❌ deprecated | The connector must declare the path as `{ENV:…}` (the `secrets/` folder is removed from the model) |
|
||||
|
||||
## Campo verify (test prima del salvataggio)
|
||||
## The verify field (test before save)
|
||||
|
||||
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.
|
||||
Declares a shell command that skald runs **after** the user has filled in the
|
||||
form and **before** persisting the activation. It checks that the credentials
|
||||
just entered actually work.
|
||||
|
||||
```json
|
||||
"verify": {
|
||||
@@ -459,174 +387,145 @@ credenziali appena inserite funzionino davvero.
|
||||
}
|
||||
```
|
||||
|
||||
| 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 |
|
||||
| Field | Required | Description |
|
||||
|-------|----------|-------------|
|
||||
| `command` | ✅ | Shell command. Runs in the same sandbox as the server: container `skald-{userid}` for `mcp_local` user, host for `mcp_remote` global. The declared env/secrets are injected |
|
||||
| `timeout_secs` | optional | Default 15. skald kills the process at expiry |
|
||||
|
||||
### Convenzione output
|
||||
### Output convention
|
||||
|
||||
Il comando deve stampare **un singolo oggetto JSON su stdout** e nient'altro:
|
||||
The command must print **a single JSON object on stdout** and nothing else:
|
||||
|
||||
```json
|
||||
{"ok": true, "message": "IMAP and SMTP authentication successful", "details": {"imap": "...", "smtp": "..."}}
|
||||
{"ok": false, "message": "IMAP login failed: INVALID_CREDENTIALS"}
|
||||
```
|
||||
|
||||
| Campo | Tipo | Descrizione |
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `ok` | bool | `true` = test passato |
|
||||
| `message` | string | Messaggio mostrato all'utente (mai loggare secret qui dentro) |
|
||||
| `details` | object | Opzionale, dettagli strutturati mostrati in `<pre>` |
|
||||
| `ok` | bool | `true` = test passed |
|
||||
| `message` | string | Message shown to the user (never log secrets inside) |
|
||||
| `details` | object | Optional, structured details shown 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
|
||||
Exit code: 0 on success, ≠ 0 on failure (skald uses the exit code as a
|
||||
fallback if the JSON parse fails). **Never print credentials** in the
|
||||
`message`/`details`.
|
||||
|
||||
### Dove mettere lo script
|
||||
### Where to put the script
|
||||
|
||||
Se `command` referenzia un file (es. `verify.py`), il file va:
|
||||
1. Aggiunto all'array `files[]` in `connectors.json` (con `sha256` e `size`)
|
||||
2. Salvato nella cartella del connector (`<id>/verify.py`)
|
||||
If `command` references a file (e.g. `verify.py`), the file must:
|
||||
1. Be added to the `files[]` array in `connectors.json` (with `sha256` and `size`)
|
||||
2. Be saved in the connector folder (`<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`).
|
||||
skald downloads it, verifies its SHA-256 against the index, and makes it
|
||||
available at the same path as the main server (container for `mcp_local` ones,
|
||||
`./scripts/<id>/` dir on the host for `mcp_remote` ones).
|
||||
|
||||
### Senza verify
|
||||
### Without 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`.
|
||||
If `verify` is absent, skald **runs no test** — activation is direct, as
|
||||
today. The connector goes to `auth_state='ready'` without verification. For
|
||||
`mcp_remote` there is no handshake fallback: the manifest author decides
|
||||
whether the test is needed by writing `verify`.
|
||||
|
||||
## Convenzioni icone
|
||||
## Icon conventions
|
||||
|
||||
- **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`)
|
||||
- **Format**: SVG for vector icons (better for retina/zoom), PNG for raster
|
||||
- **Name**: `icon_sm.{svg|png}` (small, ~48×48px), `icon_lg.{svg|png}` (large, ~96×96px)
|
||||
- **Path**: relative to the connector folder
|
||||
- **In the index** the path is `{folder}/{filename}` (e.g. `gmail/icon_sm.svg`)
|
||||
|
||||
## Integrità file (sha256)
|
||||
## File integrity (sha256)
|
||||
|
||||
- Gli SHA-256 sono generati **automaticamente** da `scripts/compile.py` a partire
|
||||
dai file fisici presenti in ogni cartella — mai manuali, mai stale.
|
||||
- L'unico file firmato (in futuro) sarà `connectors.json` (l'indice).
|
||||
- `fragment.json` e `index.json` non hanno hash — sono solo input di compilazione.
|
||||
- SHA-256 hashes are generated **automatically** by `scripts/compile.py` from
|
||||
the physical files present in each folder — never manual, never stale.
|
||||
- The only file signed (in the future) will be `connectors.json` (the index).
|
||||
- `fragment.json` and `index.json` have no hash — they are just compilation inputs.
|
||||
|
||||
## Struttura directory
|
||||
## Local workflow
|
||||
|
||||
```
|
||||
connectors/
|
||||
├── index.json ← lista ordinata degli id dei connector (input per compile.py)
|
||||
├── connectors.json ← INDICE COMPILATO (generato da compile.py, non editare)
|
||||
├── compile.py ← genera connectors.json (lanciato da scripts/compile.py)
|
||||
├── index.html ← Catalogo UI (legge connectors.json via fetch)
|
||||
├── oauth/
|
||||
│ └── show.html ← OAuth callback receiver
|
||||
├── gmail/ ← Un connector per cartella
|
||||
│ ├── fragment.json ← Frammento dell'indice (id, name, type, ..., SENZA files[])
|
||||
│ ├── connector.json ← Configurazione tecnica (mcp_config, auth.deliver, ...)
|
||||
│ ├── 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. 96×96)
|
||||
├── email/
|
||||
│ ├── fragment.json
|
||||
│ ├── connector.json
|
||||
│ ├── email_mcp_server.py
|
||||
│ ├── verify.py
|
||||
│ ├── requirements.txt
|
||||
│ ├── icon_sm.svg
|
||||
│ └── icon_lg.svg
|
||||
└── ...
|
||||
```
|
||||
1. **Adding a new connector**:
|
||||
- Create the folder `connectors/<id>/`
|
||||
- Create `fragment.json` (id, name, type, scope, icons, auth, tools, ...)
|
||||
- Create `connector.json` (technical config: mcp_config, launch_command, ...)
|
||||
- Add the MCP script, icons, verify.py, requirements.txt
|
||||
- Add the id to `connectors/index.json`
|
||||
- Run `python3 scripts/compile.py`
|
||||
|
||||
## Workflow locale
|
||||
2. **Modifying an existing connector**:
|
||||
- Edit the files in the connector folder
|
||||
- **Do not touch** `connectors.json` — it gets regenerated
|
||||
- Run `python3 scripts/compile.py`
|
||||
|
||||
1. **Aggiungere un nuovo connector**:
|
||||
- Crea cartella `connectors/<id>/`
|
||||
- Crea `fragment.json` (id, name, type, scope, icone, auth, tools, ...)
|
||||
- Crea `connector.json` (config tecnica: mcp_config, launch_command, ...)
|
||||
- Aggiungi script MCP, icone, verify.py, requirements.txt
|
||||
- Aggiungi l'id a `connectors/index.json`
|
||||
- Lancia `python3 scripts/compile.py`
|
||||
|
||||
2. **Modificare un connector esistente**:
|
||||
- Modifica i file nella cartella del connector
|
||||
- **Non toccare** `connectors.json` — viene rigenerato
|
||||
- Lancia `python3 scripts/compile.py`
|
||||
|
||||
3. **Prima del deploy**:
|
||||
3. **Before deploying**:
|
||||
```bash
|
||||
python3 scripts/compile.py # rigenera connectors.json con SHA-256 fresh
|
||||
python3 scripts/compile.py --verify # (opzionale) verifica che sia aggiornato
|
||||
python3 scripts/compile.py # regenerates connectors.json with fresh SHA-256s
|
||||
python3 scripts/compile.py --verify # (optional) verifies that it is up to date
|
||||
```
|
||||
|
||||
4. **Deploy sul server**:
|
||||
4. **Deploy to the server**:
|
||||
```bash
|
||||
ssh dguiducci@skald-home-server /home/dguiducci/marketplace_deploy.sh
|
||||
```
|
||||
oppure con MCP SSH:
|
||||
or with MCP SSH:
|
||||
```bash
|
||||
# via mcp__ssh__exec alias "skald-home-server"
|
||||
/home/dguiducci/marketplace_deploy.sh
|
||||
```
|
||||
(Il deploy script fa `git pull` + `cp -r connectors/* /var/www/connectors.skaldagent.net/`)
|
||||
(The deploy script does `git pull` + `cp -r connectors/* /var/www/connectors.skaldagent.net/`)
|
||||
|
||||
5. Verifica su `https://connectors.skaldagent.net/`
|
||||
5. Verify on `https://connectors.skaldagent.net/`
|
||||
|
||||
## Fragment.json
|
||||
|
||||
Vedi [docs/connector.manifest_guide.md](docs/connector.manifest_guide.md) per la guida completa
|
||||
alla creazione.
|
||||
See [docs/connector.manifest_guide.md](docs/connector.manifest_guide.md) for the complete
|
||||
creation guide.
|
||||
|
||||
`fragment.json` contiene tutti i campi dell'entry di `connectors.json` **tranne** `files[]`.
|
||||
Questi sono i campi obbligatori:
|
||||
`fragment.json` contains all the fields of a `connectors.json` entry **except** `files[]`.
|
||||
These are the required fields:
|
||||
|
||||
| Campo | Obbligatorio | Descrizione |
|
||||
|-------|-------------|-------------|
|
||||
| `id` | ✅ | Identificatore unico (match con folder name) |
|
||||
| `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 (match con `id`) |
|
||||
| `version` | ✅ | Intero per-connector, +1 a ogni modifica dei file |
|
||||
| `version_string` | ✅ | Semver (solo display) |
|
||||
| `version_release_date` | ✅ | Data ISO 8601 (solo display) |
|
||||
| `tools` | ✅ | Array di `{name, display_name}` per friendly names UI |
|
||||
| `auth` | opzionale | Configurazione autenticazione (se diversa da `"none"`) |
|
||||
| Field | Required | Description |
|
||||
|-------|----------|-------------|
|
||||
| `id` | ✅ | Unique identifier (matches the folder name) |
|
||||
| `name` | ✅ | Displayed name |
|
||||
| `type` | ✅ | `mcp_remote` or `mcp_local` |
|
||||
| `scope` | ✅ | `global` or `user` |
|
||||
| `icon_small` | ✅ | Path relative to the marketplace root |
|
||||
| `icon_large` | ✅ | Path relative to the marketplace root |
|
||||
| `user_description` | ✅ | Short description for the UI |
|
||||
| `requires` | ✅ | Array of requirement enums |
|
||||
| `tags` | ✅ | Array of tags for filtering |
|
||||
| `folder` | ✅ | Name of the connector folder (matches `id`) |
|
||||
| `version` | ✅ | Per-connector integer, +1 on every file change |
|
||||
| `version_string` | ✅ | Semver (display only) |
|
||||
| `version_release_date` | ✅ | ISO 8601 date (display only) |
|
||||
| `tools` | ✅ | Array of `{name, display_name}` for friendly UI names |
|
||||
| `auth` | optional | Authentication configuration (if other than `"none"`) |
|
||||
|
||||
Nota: l'array `files[]` (con SHA-256 e size) viene aggiunto **automaticamente**
|
||||
da `compile.py` scansionando i file presenti nella cartella — non va mai scritto
|
||||
a mano.
|
||||
Note: the `files[]` array (with SHA-256 and size) is added **automatically**
|
||||
by `compile.py` by scanning the files present in the folder — it must never be
|
||||
written by hand.
|
||||
|
||||
## Deploy su server remoto
|
||||
## Deploy to remote server
|
||||
|
||||
Il server remoto è:
|
||||
The remote server is:
|
||||
|
||||
- **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/`
|
||||
- **Owner**: `caddy:caddy`
|
||||
- **Sudo**: required to write in `/var/www/`
|
||||
|
||||
## Connector attuali
|
||||
## Current connectors
|
||||
|
||||
| ID | Nome | Tipo | Scope | Auth | Verify |
|
||||
| ID | Name | Type | 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 |
|
||||
| `gmail` | Gmail | `mcp_local` | `user` | oauth2 (Google) + deliver: `env/google_authorized_user` (via `GMAIL_CREDS_JSON`) | ⏳ Phase 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) |
|
||||
@@ -637,6 +536,8 @@ Il server remoto è:
|
||||
| `google-trends` | Google Trends | `mcp_local` | `global` | none | `verify.py` (trendspyg import probe) |
|
||||
| `wikipedia` | Wikipedia | `mcp_local` | `global` | none | — |
|
||||
| `whatsapp` | 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.
|
||||
| `playwright` | Playwright | `mcp_local` | `global` | none | `verify.js` (headless Chromium launch probe) |
|
||||
|
||||
**verify-before-save status in skald**: `exa`, `drive`, `email`, `tavily`, and `gcal` have a complete `verify`
|
||||
(script + JSON output); `gmail` awaits Phase 2 (OAuth via loopback listener).
|
||||
A connector without `verify` is activated without any test — see § Without verify.
|
||||
|
||||
Reference in New Issue
Block a user