Initial commit: marketplace structure with connectors.json and index.html
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
.DS_Store
|
||||
*.pyc
|
||||
__pycache__/
|
||||
.env
|
||||
secrets/
|
||||
*.egg-info/
|
||||
@@ -0,0 +1,262 @@
|
||||
# Skald Connectors Marketplace
|
||||
|
||||
_Updated: 2026-07-16_
|
||||
|
||||
## 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).
|
||||
|
||||
## Struttura directory
|
||||
|
||||
```
|
||||
connectors/
|
||||
├── connectors.json ← INDICE (radice di fiducia unica)
|
||||
├── index.html ← Catalogo UI (legge connectors.json via fetch)
|
||||
├── 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)
|
||||
└── tavily/
|
||||
├── connector.json
|
||||
├── 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.
|
||||
|
||||
```json
|
||||
{
|
||||
"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"],
|
||||
"folder": "gmail",
|
||||
"files": [
|
||||
{"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 |
|
||||
| `files` | ✅ | Array di file con sha256 (NO self-hash) |
|
||||
|
||||
## Schema — connector.json (per cartella)
|
||||
|
||||
Configurazione tecnica per l'attivazione del connector.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "gmail",
|
||||
"name": "Gmail",
|
||||
"version": "1.0.0",
|
||||
"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",
|
||||
"Create secrets/google_oauth_client.json with {\"client_id\": \"...\", \"client_secret\": \"...\"}",
|
||||
"Run: python3 gmail_oauth_setup.py (opens browser for OAuth)",
|
||||
"Set GMAIL_CREDS_PATH env var or place token at secrets/gmail_creds.json"
|
||||
],
|
||||
"docs": [
|
||||
{
|
||||
"lang": "en",
|
||||
"description": "Full description for human users...",
|
||||
"llm_short_description": "One-line description for LLM context..."
|
||||
}
|
||||
],
|
||||
"auth": {
|
||||
"type": "oauth2",
|
||||
"provider": "google",
|
||||
"scopes": [
|
||||
"https://www.googleapis.com/auth/gmail.modify",
|
||||
"https://www.googleapis.com/auth/gmail.labels"
|
||||
]
|
||||
},
|
||||
"mcp_config": {
|
||||
"command": "python3",
|
||||
"args": ["gmail_mcp_server.py"],
|
||||
"env": {
|
||||
"GMAIL_CREDS_PATH": "{secrets}/gmail_creds.json"
|
||||
}
|
||||
},
|
||||
"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` | ✅ | SemVer |
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `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` | Richiede file di credenziali in `secrets/` |
|
||||
|
||||
## Campo auth
|
||||
|
||||
Struttura che descrive come il connector gestisce l'autenticazione:
|
||||
|
||||
```json
|
||||
// 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
|
||||
{"type": "oauth2", "provider": "google", "scopes": ["...", "..."]}
|
||||
|
||||
// Nessuna autenticazione
|
||||
{"type": "none"}
|
||||
```
|
||||
|
||||
## 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.json` **non 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:
|
||||
```bash
|
||||
python3 -c "import hashlib; print(hashlib.sha256(open('file.py','rb').read()).hexdigest())"
|
||||
```
|
||||
|
||||
## Workflow locale
|
||||
|
||||
1. Lavora su file nella cartella `connectors/`
|
||||
2. Modifica `connectors.json`, `connector.json`, script, icone
|
||||
3. **Prima del deploy** aggiorna gli sha256 in `connectors.json`:
|
||||
```bash
|
||||
python3 scripts/update_hashes.py
|
||||
```
|
||||
4. Fai l'upload sul server con rsync:
|
||||
```bash
|
||||
rsync -avz --delete connectors/ dguiducci@skald-server:/var/www/connectors.skaldagent.net/
|
||||
```
|
||||
5. 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):
|
||||
|
||||
```bash
|
||||
# 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:
|
||||
```bash
|
||||
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 |
|
||||
|----|------|------|-------|
|
||||
| `tavily` | Tavily | `mcp_remote` | `global` |
|
||||
| `gmail` | Gmail | `mcp_local` | `user` |
|
||||
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"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 \u2014 full MCP integration with push notifications.",
|
||||
"requires": [
|
||||
"OAUTH",
|
||||
"PYTHON"
|
||||
],
|
||||
"tags": [
|
||||
"email",
|
||||
"mcp",
|
||||
"local",
|
||||
"google"
|
||||
],
|
||||
"folder": "gmail",
|
||||
"files": [
|
||||
{
|
||||
"path": "gmail_mcp_server.py",
|
||||
"sha256": "a50d4da9621f7a4b092f4e3c5ae85dec5f466783372f78ca7e792046dc01c673",
|
||||
"size": 46772
|
||||
},
|
||||
{
|
||||
"path": "gmail_oauth_setup.py",
|
||||
"sha256": "e488acb289c43a3e6d541140d254451ac5688c0014e8959619375b564afe8748",
|
||||
"size": 3627
|
||||
},
|
||||
{
|
||||
"path": "icon_lg.svg",
|
||||
"sha256": "93c8d9c8dae96f0206e5ae3fcfe89dff3a08553ed48badf504a5abd09f5596b0",
|
||||
"size": 254
|
||||
},
|
||||
{
|
||||
"path": "icon_sm.svg",
|
||||
"sha256": "029d7f5d81de6cf2b17bc60c878d465521c6817a313381bc71d625e93be19c8a",
|
||||
"size": 251
|
||||
},
|
||||
{
|
||||
"path": "requirements.txt",
|
||||
"sha256": "3f659cc5e5f0543f132699b06ccf9016ffe9afb40f9df4d110e0445b8d20f63e",
|
||||
"size": 82
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "tavily",
|
||||
"name": "Tavily",
|
||||
"type": "mcp_remote",
|
||||
"scope": "global",
|
||||
"icon_small": "tavily/icon_sm.png",
|
||||
"icon_large": "tavily/icon_lg.png",
|
||||
"user_description": "Web search API for LLMs \u2014 real-time results, news, and content extraction.",
|
||||
"requires": [
|
||||
"API_KEY"
|
||||
],
|
||||
"tags": [
|
||||
"search",
|
||||
"mcp",
|
||||
"remote"
|
||||
],
|
||||
"folder": "tavily",
|
||||
"files": [
|
||||
{
|
||||
"path": "icon_lg.png",
|
||||
"sha256": "92962ea1d49f272665262d55ecdea929972d3efab5261fc6ffea632803fceaf8",
|
||||
"size": 24264
|
||||
},
|
||||
{
|
||||
"path": "icon_sm.png",
|
||||
"sha256": "92962ea1d49f272665262d55ecdea929972d3efab5261fc6ffea632803fceaf8",
|
||||
"size": 24264
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"id": "gmail",
|
||||
"name": "Gmail",
|
||||
"version": "1.0.0",
|
||||
"type": "mcp_local",
|
||||
"launch_command": "python3 gmail_mcp_server.py",
|
||||
"transport": "stdio",
|
||||
"requires": [
|
||||
"OAUTH",
|
||||
"PYTHON"
|
||||
],
|
||||
"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",
|
||||
"Create secrets/google_oauth_client.json with {\"client_id\": \"...\", \"client_secret\": \"...\"}",
|
||||
"Run: python3 gmail_oauth_setup.py (opens browser for OAuth)",
|
||||
"Set GMAIL_CREDS_PATH env var or place token at secrets/gmail_creds.json"
|
||||
],
|
||||
"docs": [
|
||||
{
|
||||
"lang": "en",
|
||||
"description": "Full Gmail integration: read, send, modify, and manage emails with OAuth2 authentication. Supports push notifications via history polling.",
|
||||
"llm_short_description": "Gmail MCP server: read, send, modify, and manage Gmail messages. Requires OAuth setup with Google Cloud Console. Tools: list_messages, get_message, get_thread, send_message, modify_message, list_labels, create_label, get_profile, download_attachments."
|
||||
}
|
||||
],
|
||||
"mcp_config": {
|
||||
"command": "python3",
|
||||
"args": [
|
||||
"gmail_mcp_server.py"
|
||||
],
|
||||
"env": {
|
||||
"GMAIL_CREDS_PATH": "{secrets}/gmail_creds.json"
|
||||
}
|
||||
},
|
||||
"homepage": "https://mail.google.com",
|
||||
"icon_small": "icon_sm.svg",
|
||||
"icon_large": "icon_lg.svg",
|
||||
"scope": "user",
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate a Google OAuth token for Gmail API.
|
||||
|
||||
This script runs a local OAuth flow that:
|
||||
1. Opens your browser automatically to the Google authorization page
|
||||
2. Handles the callback via a local HTTP server
|
||||
3. Saves the resulting token to ./secrets/gmail_creds.json
|
||||
|
||||
No manual copy-paste required.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
SCOPES = [
|
||||
"https://www.googleapis.com/auth/gmail.modify",
|
||||
"https://www.googleapis.com/auth/gmail.labels",
|
||||
]
|
||||
|
||||
_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
SECRET_PATH = os.path.join(_ROOT, "secrets", "gmail_creds.json")
|
||||
_OAUTH_CLIENT_PATH = os.path.join(_ROOT, "secrets", "google_oauth_client.json")
|
||||
|
||||
|
||||
def _load_oauth_client() -> tuple[str, str]:
|
||||
if not os.path.exists(_OAUTH_CLIENT_PATH):
|
||||
print(f"Missing OAuth client file: {_OAUTH_CLIENT_PATH}")
|
||||
print("Create it with: {\"client_id\": \"...\", \"client_secret\": \"...\"}")
|
||||
sys.exit(1)
|
||||
with open(_OAUTH_CLIENT_PATH) as f:
|
||||
data = json.load(f)
|
||||
return data["client_id"], data["client_secret"]
|
||||
|
||||
|
||||
def main() -> None:
|
||||
# Lazy-import so we can show helpful errors if not installed.
|
||||
try:
|
||||
from google.auth.transport.requests import Request
|
||||
from google.oauth2.credentials import Credentials
|
||||
from google_auth_oauthlib.flow import InstalledAppFlow
|
||||
except ImportError as e:
|
||||
print(f"Missing dependencies: {e}")
|
||||
print("Install with: pip3 install google-auth google-auth-oauthlib google-api-python-client")
|
||||
sys.exit(1)
|
||||
|
||||
creds = None
|
||||
|
||||
# Try to load existing credentials first, in case they have refresh token.
|
||||
if os.path.exists(SECRET_PATH):
|
||||
print(f"Existing credentials found at {SECRET_PATH}")
|
||||
try:
|
||||
creds = Credentials.from_authorized_user_file(SECRET_PATH, SCOPES)
|
||||
except Exception:
|
||||
creds = None
|
||||
|
||||
# If creds exist and are valid, we're good.
|
||||
if creds and creds.valid:
|
||||
print("Credentials are already valid!")
|
||||
return
|
||||
|
||||
# If creds exist but expired, try to refresh.
|
||||
if creds and creds.expired and creds.refresh_token:
|
||||
print("Token expired. Attempting refresh...")
|
||||
try:
|
||||
creds.refresh(Request())
|
||||
print("Token refreshed successfully!")
|
||||
except Exception as e:
|
||||
print(f"Refresh failed: {e}")
|
||||
creds = None
|
||||
|
||||
if not creds or not creds.valid:
|
||||
client_id, client_secret = _load_oauth_client()
|
||||
# Start OAuth flow using local server (opens browser automatically).
|
||||
flow = InstalledAppFlow.from_client_config(
|
||||
{
|
||||
"installed": {
|
||||
"client_id": client_id,
|
||||
"client_secret": client_secret,
|
||||
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
||||
"token_uri": "https://oauth2.googleapis.com/token",
|
||||
"redirect_uris": ["http://localhost"],
|
||||
}
|
||||
},
|
||||
SCOPES,
|
||||
)
|
||||
|
||||
print("\nOpening browser for Google authorization...")
|
||||
creds = flow.run_local_server(
|
||||
port=0, # pick a random available port
|
||||
open_browser=True,
|
||||
prompt="consent",
|
||||
access_type="offline",
|
||||
)
|
||||
|
||||
# Save credentials.
|
||||
os.makedirs(os.path.dirname(SECRET_PATH), exist_ok=True)
|
||||
with open(SECRET_PATH, "w") as f:
|
||||
f.write(creds.to_json())
|
||||
|
||||
print(f"\n✅ Gmail OAuth token saved to {SECRET_PATH}")
|
||||
print(f" Scopes: {creds.scopes}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96">
|
||||
<rect x="8" y="16" width="80" height="64" rx="6" fill="#EA4335"/>
|
||||
<polygon points="8,16 48,48 88,16" fill="white" opacity="0.2"/>
|
||||
<polygon points="8,80 48,48 88,80" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 254 B |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
|
||||
<rect x="4" y="8" width="40" height="32" rx="3" fill="#EA4335"/>
|
||||
<polygon points="4,8 24,24 44,8" fill="white" opacity="0.2"/>
|
||||
<polygon points="4,40 24,24 44,40" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 251 B |
@@ -0,0 +1,3 @@
|
||||
google-api-python-client>=2.150.0
|
||||
google-auth>=2.35.0
|
||||
google-auth-oauthlib>=1.2.0
|
||||
@@ -0,0 +1,67 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Skald Connectors</title>
|
||||
<style>
|
||||
*{margin:0;padding:0;box-sizing:border-box}
|
||||
body{font-family:system-ui,-apple-system,sans-serif;background:#0f172a;color:#e2e8f0;padding:2rem;max-width:900px;margin:0 auto}
|
||||
h1{font-size:2rem;color:#38bdf8;margin-bottom:.5rem}
|
||||
.sub{color:#64748b;margin-bottom:2rem}
|
||||
.card{background:#1e293b;border-radius:12px;padding:1.25rem;display:flex;align-items:center;gap:1rem;margin-bottom:1rem;transition:background .2s}
|
||||
.card:hover{background:#334155}
|
||||
.card img{width:48px;height:48px;border-radius:8px;object-fit:cover}
|
||||
.card h2{font-size:1.125rem;color:#f1f5f9;margin-bottom:.25rem}
|
||||
.card p{font-size:.875rem;color:#94a3b8}
|
||||
.tags{display:flex;gap:.5rem;margin-top:.5rem;flex-wrap:wrap;align-items:center}
|
||||
.tag{font-size:.75rem;padding:2px 8px;border-radius:4px;background:#0f172a;color:#64748b}
|
||||
.tag.api-key{color:#f59e0b;border:1px solid #f59e0b}
|
||||
.tag.oauth{color:#f97316;border:1px solid #f97316}
|
||||
.tag.python{color:#22c55e;border:1px solid #22c55e}
|
||||
.tag.remote{color:#22d3ee;border:1px solid #22d3ee}
|
||||
.tag.local{color:#22d3ee;border:1px solid #22d3ee}
|
||||
.tag.search{color:#a78bfa;border:1px solid #a78bfa}
|
||||
.tag.email{color:#ef4444;border:1px solid #ef4444}
|
||||
.tag.google{color:#eab308;border:1px solid #eab308}
|
||||
.scope-global{font-size:.75rem;padding:2px 8px;border-radius:4px;background:#0f172a;color:#22c55e;border:1px solid #22c55e}
|
||||
.scope-user{font-size:.75rem;padding:2px 8px;border-radius:4px;background:#0f172a;color:#38bdf8;border:1px solid #38bdf8}
|
||||
.type-local{font-size:.75rem;padding:2px 8px;border-radius:4px;background:#0f172a;color:#a78bfa;border:1px solid #a78bfa}
|
||||
.type-remote{font-size:.75rem;padding:2px 8px;border-radius:4px;background:#0f172a;color:#f472b6;border:1px solid #f472b6}
|
||||
.footer{text-align:center;color:#475569;margin-top:3rem;font-size:.875rem}
|
||||
a{text-decoration:none;color:inherit}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🔌 Skald Connectors</h1>
|
||||
<p class="sub">Tested, ready-to-use connectors for Skald agents.</p>
|
||||
<div id="list">Loading...</div>
|
||||
<p class="footer"><a href="/connectors.json" style="color:#64748b">connectors.json</a></p>
|
||||
<script>
|
||||
fetch("/connectors.json")
|
||||
.then(r=>r.json())
|
||||
.then(d=>{
|
||||
document.getElementById("list").innerHTML = d.connectors.map(c => {
|
||||
const typeBadge = c.type === 'mcp_remote' ? '<span class="type-remote">📡 remote</span>'
|
||||
: '<span class="type-local">💻 local</span>';
|
||||
const scopeBadge = c.scope === 'global' ? '<span class="scope-global">🌐 global</span>'
|
||||
: '<span class="scope-user">👤 user</span>';
|
||||
return '<a href="/'+c.folder+'/"><div class="card">' +
|
||||
'<img src="/'+c.icon_small+'" alt="">' +
|
||||
'<div>' +
|
||||
'<h2>'+c.name+'</h2>' +
|
||||
'<p>'+c.user_description+'</p>' +
|
||||
'<div class="tags">' +
|
||||
typeBadge +
|
||||
scopeBadge +
|
||||
c.requires.map(r => '<span class="tag '+r.toLowerCase()+'">'+r+'</span>').join('') +
|
||||
c.tags.map(t => '<span class="tag '+t.toLowerCase()+'">'+t+'</span>').join('') +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div></a>'
|
||||
}).join('')
|
||||
})
|
||||
.catch(e=>{document.getElementById("list").innerHTML='<p>Error loading connectors</p>'})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"id": "tavily",
|
||||
"name": "Tavily",
|
||||
"version": "1.0.0",
|
||||
"type": "mcp_remote",
|
||||
"mcp_config": {
|
||||
"url": "https://mcp.tavily.com/mcp/?tavilyApiKey={key}",
|
||||
"transport": "streamable-http"
|
||||
},
|
||||
"requires": [
|
||||
"API_KEY"
|
||||
],
|
||||
"docs": [
|
||||
{
|
||||
"lang": "en",
|
||||
"description": "Tavily MCP: web search and content extraction server. Use for real-time search, news articles, and site scraping. Returns fresh, relevant results from the web.",
|
||||
"llm_short_description": "Web search MCP \u2014 real-time results, news, site scraping. Richiede API key Tavily."
|
||||
}
|
||||
],
|
||||
"homepage": "https://tavily.com",
|
||||
"icon_small": "icon_sm.png",
|
||||
"icon_large": "icon_lg.png",
|
||||
"scope": "global",
|
||||
"tags": [
|
||||
"search",
|
||||
"mcp",
|
||||
"remote"
|
||||
],
|
||||
"auth": {
|
||||
"type": "api_key",
|
||||
"delivery": "query",
|
||||
"param": "tavilyApiKey"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
Reference in New Issue
Block a user