Add Email (IMAP/SMTP) and SSH Remote Access connectors

- 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
This commit is contained in:
2026-07-16 22:29:33 +01:00
parent dedd09d7c7
commit 1caba6946c
15 changed files with 3420 additions and 8 deletions
+17 -2
View File
@@ -4,12 +4,27 @@
"version": "1.0.0",
"type": "mcp_remote",
"mcp_config": {
"url": "https://mcp.tavily.com/mcp/?tavilyApiKey={key}",
"url": "https://mcp.tavily.com/mcp/?tavilyApiKey={SECRET:tavilyApiKey}",
"transport": "streamable-http"
},
"requires": [
"API_KEY"
"API_KEY",
"ENV"
],
"env": [
{
"name": "tavilyApiKey",
"label": "Tavily API key",
"description": "Your Tavily API key (find it at https://app.tavily.com). Used both as the MCP URL query parameter and for the verification request.",
"required": true,
"secret": true,
"example": "tvly-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
],
"verify": {
"command": "python3 verify.py",
"timeout_secs": 15
},
"docs": [
{
"lang": "en",
+65
View File
@@ -0,0 +1,65 @@
#!/usr/bin/env python3
"""Verify the Tavily API key by making a minimal search request.
Reads TAVILY_API_KEY from the environment (the key skald collected from the
user and substituted into the {SECRET:tavilyApiKey} placeholder), POSTs a
minimal search to https://api.tavily.com/search, and prints a single JSON
object on stdout:
{"ok": true, "message": "Tavily API key is valid"}
{"ok": false, "message": "Tavily API key is invalid or unauthorized"}
Exit code is 0 on success, 1 on any failure. The API key is never printed.
stdlib only (urllib).
"""
import json
import os
import sys
import urllib.error
import urllib.request
def _result(ok, message):
print(json.dumps({"ok": ok, "message": message}))
sys.exit(0 if ok else 1)
def main():
api_key = os.environ.get("tavilyApiKey", "").strip()
if not api_key:
# Also accept the UPPER_SNAKE form some users may type.
api_key = os.environ.get("TAVILY_API_KEY", "").strip()
if not api_key:
_result(False, "No Tavily API key provided (tavilyApiKey env var is empty)")
body = json.dumps({
"api_key": api_key,
"query": "skald connectivity check",
"max_results": 1,
"search_depth": "basic",
}).encode("utf-8")
req = urllib.request.Request(
"https://api.tavily.com/search",
data=body,
headers={"Content-Type": "application/json"},
method="POST",
)
try:
with urllib.request.urlopen(req, timeout=15) as resp:
if 200 <= resp.status < 300:
_result(True, "Tavily API key is valid")
_result(False, f"Tavily returned HTTP {resp.status}")
except urllib.error.HTTPError as e:
if e.code in (401, 403):
_result(False, "Tavily API key is invalid or unauthorized "
f"(HTTP {e.code})")
_result(False, f"Tavily returned HTTP {e.code}: {e.reason}")
except Exception as e:
_result(False, f"Tavily verify request failed: {e}")
if __name__ == "__main__":
main()