- CLAUDE.md: developer guide for the marketplace repo - email/: generic IMAP+SMTP connector (stdlib only, config via env) - env[] array, auth.type: password, verify.py (IMAP+SMTP probe) - ssh/: SSH Remote Access connector ported from Skald, modified - ALIASES_FILE → ~/.ssh_aliases.json (was ./secrets/...) - optional env[] (TTL/timeout tunables) - auth.type: none (per-alias auth at runtime via elicitation) - tavily/: restructured with env[] (tavilyApiKey), verify.py, auth block - connectors.json: added email, ssh, and tavily verify.py entries - SKALD.md: documented env[], verify, password auth, SSH connector
8.1 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
What this is
The Skald Connectors Marketplace — a catalog of tested connectors (adapters that let Skald agents talk to external services). It is not an application: there is no build system, package manager, or test suite. The repo is a set of JSON manifests, static HTML, icons, and standalone Python MCP scripts that are served as-is over HTTP.
SKALD.md is the authoritative spec (written in Italian) — read it before changing any schema or manifest.
Architecture
Two-tier manifest model with a single trust root:
connectors/connectors.json— the index and single root of trust. Lists every connector and, per connector, afiles[]array with thesha256+sizeof each shipped file. It has no hash of itself, and thefiles[]arrays do not includeconnector.json— both are treated as unsigned manifests. Intended to be digitally signed in the future.connectors/<id>/connector.json— per-connector technical manifest for activation (launch command, transport,mcp_config,auth,dependencies,docs). One folder per connector; folder name matches the connectorid.connectors/index.html— catalog UI. Fetches/connectors.jsonat absolute path and links to/<folder>/, so it only works when served from the site root (not opened as afile://).
Connector taxonomy (see SKALD.md for full enums):
type:mcp_remote(hosted MCP server reached by URL, e.g. Tavily) vsmcp_local(script run client-side, e.g. Gmail).scope:global(one shared config) vsuser(per-user auth/instance).requires: prerequisites likeAPI_KEY,OAUTH,PYTHON,NODE,DOCKER,ENV. (SECRETS_DIRis deprecated — thesecrets/folder is gone from the model; connectors must declare their inputs asENV/SECRET.)
Unified placeholder syntax. Every value skald must fill at runtime uses one of two tokens, anywhere it appears (URL, mcp_config.env, verify.command):
{ENV:NAME}— a non-sensitive value (host, port, username…), declared in the top-levelenv[]array.{SECRET:NAME}— a sensitive value (password, API key, token), also declared inenv[]withsecret: true.
Legacy tokens {key}, {env:NAME}, {secrets}/… are deprecated and skald no longer substitutes them. The api-key of an auth.type = "api_key" connector is exposed as {SECRET:<auth.param>} (e.g. Tavily: ?tavilyApiKey={SECRET:tavilyApiKey}). See SKALD.md § Sintassi placeholder.
Config via env[] (env manifest field): a connector whose requires includes ENV declares its required environment variables in a top-level env array in connector.json — each entry has name, label, description, required, secret, and optional default/example. skald renders a real form from this schema (masking secret: true fields, marking required, using example as placeholder) and injects the collected values into the process environment at launch; the server reads os.environ. Nothing is written to disk. The email connector is the reference example (see SKALD.md § Campo env). tavily now also declares env[] (its API key as a secret field).
Verify-before-save (verify manifest field). A connector may declare a verify step — a shell command skald runs after the user fills the form and before persisting the activation, to confirm the credentials actually work:
"verify": { "command": "python3 verify.py", "timeout_secs": 20 }
The command runs in the same sandbox as the server (container skald-{userid} for mcp_local user, host for mcp_remote global), with the collected env/secret injected. It must print one JSON object on stdout — {"ok": bool, "message": string, "details"?: object} — and exit 0 on success. If verify is absent, no test runs and the activation is direct. See SKALD.md § Campo verify. email and tavily ship a verify.py; gmail will get one in the OAuth phase.
MCP servers (connectors/gmail/gmail_mcp_server.py, connectors/email/email_mcp_server.py) are hand-rolled JSON-RPC 2.0 servers over stdio — no MCP SDK / FastMCP. Shared conventions: stdout is reserved for JSON-RPC, all logging goes to stderr, a lock guards stdout writes, a background thread emits event/new_email push notifications, and each server exposes the same tool shape (TOOLS manifest + TOOL_DISPATCH + handle_request). Mirror this structure for new local connectors.
- Gmail uses the Gmail API with OAuth; today it still reads its token from
./secrets/gmail_creds.json(overrideGMAIL_CREDS_PATH) generated bygmail_oauth_setup.py. Thesecrets/path is deprecated — the OAuth phase (skald Fase 2) will migrate Gmail to{ENV:}/{SECRET:}and a loopback-listener flow. Push = History API polling.verifyis not yet wired for Gmail. - Email is a generic IMAP+SMTP connector, stdlib-only (no dependencies), configured entirely from
{ENV:}/{SECRET:}env vars (nosecrets/), works with any provider. Push = IMAP IDLE with a 60s polling fallback; the request thread and the watcher thread each hold their own IMAP connection (imaplib is not thread-safe). Note: imaplib does not quote SEARCH arguments, so values with spaces must be wrapped via_q(). Ships averify.py(IMAP+SMTP login probe). - Tavily is
mcp_remote/global; its API key is declared as anenv[]secret (tavilyApiKey) and templated into the URL as{SECRET:tavilyApiKey}. Ships averify.py(minimal/searchPOST probe). - SSH is
mcp_local/user; stores aliases in~/.ssh_aliases.json(auto-managed, 0600). Auth per-alias: key/agent (default) or elicited password. Sudo vianopasswdor elicited password (sudo -S). No setup-time credentials:auth.type: "none"with noverify(activation is direct). All env vars (SSH_MCP_*) are optional with defaults. Shipsrequirements.txt(paramiko).
Critical invariants
- Editing any shipped connector file (script, icon, requirements,
verify.py) requires updating itssha256ANDsizeinconnectors.json. A stale hash breaks integrity verification on the client.connector.jsonandconnectors.jsonthemselves are not hashed, so editing them needs no hash update. - Adding a connector means changes in two places: a new entry in
connectors.json(with all file hashes) and a newconnectors/<id>/connector.json. Keepid,scope,type,tags, andrequiresconsistent between the two. - A new
verify.pymust be added tofiles[](withsha256+size) exactly like any other shipped file — skald refuses to run an unverified script. - The
secrets/directory is deprecated in the model (credentials flow through{ENV:}/{SECRET:}). Gmail still uses it locally pending the OAuth migration; that path is gitignored — never commit credentials or OAuth tokens.
Commands
Recompute a file hash (do this before deploy for every changed file; SKALD.md mentions a scripts/update_hashes.py helper that does not exist in the repo yet):
python3 -c "import hashlib; print(hashlib.sha256(open('connectors/gmail/gmail_mcp_server.py','rb').read()).hexdigest())"
Preview the catalog locally (must serve from the connectors/ root so /connectors.json resolves):
cd connectors && python3 -m http.server 8000 # then open http://localhost:8000/
Set up and run the Gmail connector:
pip install -r connectors/gmail/requirements.txt
python3 connectors/gmail/gmail_oauth_setup.py # opens browser for OAuth, writes token
python3 connectors/gmail/gmail_mcp_server.py # speaks JSON-RPC on stdin/stdout
Deploy to the production server (Caddy, path /var/www/connectors.skaldagent.net/):
rsync -avz --delete ./connectors/ dguiducci@skald-server:/var/www/connectors.skaldagent.net/
# then fix ownership/permissions on the server:
sudo chown -R caddy:caddy /var/www/connectors.skaldagent.net/
sudo find /var/www/connectors.skaldagent.net/ -type f -exec chmod 644 {} \;
Remote git: https://git.skaldagent.net/dguiducci/skald-connectors.git (branch main). Live site: https://connectors.skaldagent.net/.