@prontosite/mcp 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +117 -0
  2. package/api.mjs +137 -0
  3. package/index.mjs +76 -0
  4. package/package.json +18 -0
package/README.md ADDED
@@ -0,0 +1,117 @@
1
+ # ProntoSite MCP-Server
2
+
3
+ Deploy, Export und Status deiner ProntoSite-Website **direkt aus einem KI-Assistenten** (z. B. Claude Desktop) –
4
+ ohne Terminal. Der Server ist ein **dünner Client** um die bestehende [ProntoSite Deploy-API](../../docs/deploy-api.md);
5
+ er hat keine eigene Logik und keinen Zustand. Die HTTP-API (curl/Terminal, Skripte) bleibt der primäre Weg –
6
+ dies ist nur ein zusätzlicher Client obendrauf.
7
+
8
+ ## Voraussetzungen
9
+
10
+ - Node.js ≥ 18
11
+ - Ein ProntoSite **API-Token** (`psk_…`) – im Dashboard unter **Konto → API-Zugang** (oder vom Betreuer erzeugt).
12
+
13
+ ## Einrichtung (empfohlen: npx)
14
+
15
+ Kein Klonen, kein Node-Projekt nötig – Claude Desktop startet den Server per `npx`:
16
+
17
+ ```jsonc
18
+ // claude_desktop_config.json → "mcpServers"
19
+ {
20
+ "prontosite": {
21
+ "command": "npx",
22
+ "args": ["-y", "@prontosite/mcp"],
23
+ "env": { "PRONTOSITE_TOKEN": "psk_…", "PRONTOSITE_SITE": "meine-firma" }
24
+ }
25
+ }
26
+ ```
27
+
28
+ (Repo-Variante ohne npx: `"command": "node", "args": ["/pfad/promptsite/mcp/prontosite/index.mjs"]`. Vorher
29
+ `cd mcp/prontosite && npm install`.)
30
+
31
+ ## Konfiguration (Token nie im Chat)
32
+
33
+ Umgebungsvariablen **oder** `~/.prontosite.json`. Zwei Formen:
34
+
35
+ **Wichtig zum Site-Bezeichner:** Nutze die **stabile Site-ID** (`s-…`, steht im Dashboard) oder `"active"` –
36
+ **nicht** den Slug. Der Slug/die Adresse kann sich ändern (eigene Domain, Umbenennung); die Site-ID nie.
37
+ Für ein Konto mit nur einer Website ist `"active"` der robusteste Wert.
38
+
39
+ **Einzelne Website (ein Kunde):**
40
+ ```jsonc
41
+ { "token": "psk_…", "site": "active", "host": "app.prontosite.io" }
42
+ ```
43
+ Env-Äquivalent: `PRONTOSITE_TOKEN`, `PRONTOSITE_SITE`, `PRONTOSITE_HOST`.
44
+
45
+ **Mehrere Websites (Betreuer/Agentur) – zwei gleichwertige Wege:** Bis ~5 Kunden am einfachsten je Kunde ein
46
+ eigener `mcpServers`-Eintrag (Token in `env`, terminalfrei) – siehe „Einrichtung (empfohlen: npx)" oben, den
47
+ Block je Kunde mit eigenem Server-Namen wiederholen. Ab ~5 wird die folgende `sites`-Map übersichtlicher (ein
48
+ Server, alle Kunden in einer Datei; `sites_list` zeigt alle auf einmal). Die Map – Schlüssel = frei wählbares
49
+ **Label** (das du im Chat sagst), `site` = **stabile Site-ID**, ein Token je Website:
50
+ ```jsonc
51
+ {
52
+ "host": "app.prontosite.io",
53
+ "defaultSite": "mas-monti",
54
+ "sites": {
55
+ "mas-monti": { "site": "s-mth…", "token": "psk_…" },
56
+ "wallboxservice24": { "site": "s-msz…", "token": "psk_…" }
57
+ }
58
+ }
59
+ ```
60
+ Jedes Tool nimmt einen optionalen Parameter `site` (Label **oder** Site-ID). Ohne Angabe gilt `defaultSite`
61
+ bzw. die Einzel-Site. So verwaltest du mit **einem** Claude Desktop beliebig viele Kundenseiten – und die
62
+ Zuordnung bricht nicht, wenn ein Kunde seine Domain/Adresse ändert.
63
+
64
+ - `site` – **Site-ID** (`s-…`, empfohlen) oder `"active"`; ein Slug wird zwar noch akzeptiert, kann aber
65
+ veralten. Erst-Import: `site:"new"`.
66
+ - `host` – Standard `app.prontosite.io`.
67
+
68
+ **Datei anlegen/erweitern:** Existiert `~/.prontosite.json` noch nicht, lege sie mit obigem Inhalt an
69
+ (z. B. `cat > ~/.prontosite.json <<'JSON' … JSON` – ⚠ **überschreibt** eine vorhandene Datei!). Ist sie schon
70
+ da, NICHT überschreiben: die Datei öffnen und unter `"sites"` einen weiteren Eintrag ergänzen (vorher
71
+ sichern: `cp ~/.prontosite.json ~/.prontosite.json.bak`).
72
+
73
+ ## Tools
74
+
75
+ | Tool | Wirkung |
76
+ |------|---------|
77
+ | `sites_list` | Alle erreichbaren Websites (Slug, Plan, letzte Änderung, Publish-Bedarf). **Lesend.** |
78
+ | `site_status({ site? })` | Plan, Slug, Live-URL, Publish-Bedarf. **Lesend.** |
79
+ | `site_export({ site?, which, outDir })` | ZIP (`published`\|`draft`\|`original`) lokal speichern. **Lesend.** |
80
+ | `site_deploy({ site?, zipPath, publish?, resetOverlay?, legal? })` | ZIP importieren, optional live. **Schreibend.** |
81
+ | `site_publish({ site? })` | Aktuellen Stand erneut veröffentlichen. **Schreibend.** |
82
+
83
+ `site_deploy` und `site_publish` sind **schreibende** Aktionen (verändern die Live-Website). Die
84
+ Tool-Beschreibungen weisen den Assistenten an, vorher rückzufragen. Jeder Aufruf wird serverseitig auditiert.
85
+
86
+ ## Arbeitsweise: ein Chat je Kunde
87
+
88
+ Für saubere Trennung: pro Kundenwebsite **einen eigenen Claude-Chat**. So bezieht sich der ganze Verlauf auf
89
+ genau diese Website, und Verwechslungen sind ausgeschlossen. `sites_list` zeigt alle Slugs – so kannst du im
90
+ Chat einfach sagen „deploye mas-monti", ohne Slugs auswendig zu kennen. (Alternativ ein Chat für alles und die
91
+ Website je Aufruf über `site` wählen – die getrennten Chats sind aber übersichtlicher.)
92
+
93
+ ## Sicherheit
94
+
95
+ - Tokens liegen nur lokal (Config/Env), werden nie geloggt und nie in Antworten zurückgegeben.
96
+ - **Behandle Tokens wie Passwörter.** Ein durchgesickertes Token erlaubt Deploy/Publish der betroffenen Website;
97
+ im Dashboard jederzeit widerrufbar, dann ein neues erzeugen.
98
+ - Es gelten dieselben serverseitigen Grenzen wie bei der HTTP-API (Plan/Operator-Gate, Rate-Limit, Audit-Log,
99
+ 100 MB ZIP, kein Server-Code).
100
+
101
+ ## Betreuer-Token (ein Token für ALLE betreuten Kunden)
102
+
103
+ Für Agenturen/Partner: **ein** Betreuer-Token deckt alle betreuten Kunden ab – **ohne `sites`-Map**. Erzeugt im
104
+ Admin/Partner-Bereich (Panel „Betreuer-Token"). Config dann einfach:
105
+
106
+ ```jsonc
107
+ { "token": "psk_…", "host": "app.prontosite.io" } // KEIN sites, KEIN site
108
+ ```
109
+
110
+ `sites_list` liefert automatisch alle betreuten Kunden-Sites (Neukunde erscheint, sobald er dem Partner
111
+ zugeordnet ist – keine Config-Pflege). Website je Aufruf per Slug **oder stabiler Site-ID** angeben (kein
112
+ `"active"` – es sind mehrere Konten). Erst-Import: `site_deploy({ site: "new", customer: "k-…" })`.
113
+
114
+ Sicherheit: der Zugriff wird bei JEDEM Aufruf serverseitig geprüft (`assertCanManage` – nur betreute Konten,
115
+ Rollenentzug wirkt sofort); nur Deploy/Status/Export/Veröffentlichen, kein Konto-/Zahlungszugriff; optionaler
116
+ Ablauf (Default 90 Tage) und optionale IP-Bindung; Widerruf sperrt alle betreuten Sites auf einmal.
117
+ Alternative bleibt die `sites`-Map oben (kleinere Angriffsfläche je Token, aber Pflege je Kunde).
package/api.mjs ADDED
@@ -0,0 +1,137 @@
1
+ /**
2
+ * ProntoSite MCP – HTTP-Kern (ohne MCP-SDK, damit testbar). Dünner Client um die bestehende Deploy-API;
3
+ * KEINE eigene Logik. Token/Site/Host aus lokaler Config (Env > ~/.prontosite.json), nie aus dem Chat.
4
+ * Tokens werden nie geloggt und nie zurückgegeben.
5
+ *
6
+ * Config-Formen:
7
+ * Einzel (ein Kunde): { "token":"psk_…", "site":"active", "host":"app.prontosite.io" }
8
+ * Multi (Betreuer): { "host":"app.prontosite.io", "defaultSite":"mas-monti", "sites": {
9
+ * "mas-monti": { "site":"s-mth…", "token":"psk_…" },
10
+ * "wallboxservice24": { "site":"s-msz…", "token":"psk_…" } } }
11
+ * `site` ist die STABILE Site-ID (s-…) oder "active" – NICHT der Slug (der kann sich ändern: Kundendomain,
12
+ * Umbenennung). Der Map-Schlüssel ist ein frei wählbares Label (das man im Chat sagt). Jedes Tool nimmt
13
+ * optional `site` (Label ODER Site-ID); ohne Angabe gilt defaultSite bzw. die Einzel-Site (sonst „active").
14
+ */
15
+ import fs from "node:fs";
16
+ import os from "node:os";
17
+ import path from "node:path";
18
+
19
+ const DEFAULT_HOST = "app.prontosite.io";
20
+
21
+ /** Config laden: Umgebungsvariablen haben Vorrang (Einzel-Werte), plus optionale sites-Map aus der Datei. */
22
+ export function loadConfig(env = process.env, home = os.homedir()) {
23
+ let file = {};
24
+ try {
25
+ const p = path.join(home, ".prontosite.json");
26
+ if (fs.existsSync(p)) file = JSON.parse(fs.readFileSync(p, "utf8")) || {};
27
+ } catch { /* defekte Datei ignorieren – Env kann trotzdem reichen */ }
28
+ const host = (env.PRONTOSITE_HOST || file.host || DEFAULT_HOST).replace(/^https?:\/\//, "").replace(/\/+$/, "");
29
+ const sites = (file.sites && typeof file.sites === "object") ? file.sites : null;
30
+ return {
31
+ host,
32
+ token: env.PRONTOSITE_TOKEN || file.token || "",
33
+ site: env.PRONTOSITE_SITE || file.site || "active",
34
+ sites,
35
+ defaultSite: env.PRONTOSITE_SITE || file.defaultSite || null,
36
+ };
37
+ }
38
+
39
+ const needToken = (token, ref) => {
40
+ if (!token || !String(token).startsWith("psk_"))
41
+ throw new Error(`Kein API-Token für „${ref}" konfiguriert. Setze PRONTOSITE_TOKEN oder trage die Site in ~/.prontosite.json (sites) ein.`);
42
+ return token;
43
+ };
44
+
45
+ /** Ziel (Token + Site-Bezeichner für die API) bestimmen. Bei einer sites-Map wird der Eintrag per Label-
46
+ * Schlüssel ODER per stabiler Site-ID (entry.site) gefunden; die API bekommt die stabile ID (entry.site)
47
+ * bzw. den Schlüssel. Sonst der Einzel-Token mit dem übergebenen Bezeichner (ID/„active"). */
48
+ export function resolveTarget(cfg, site) {
49
+ const want = site || cfg.defaultSite || cfg.site || "active";
50
+ if (cfg.sites) {
51
+ const key = cfg.sites[want] ? want : Object.keys(cfg.sites).find(k => cfg.sites[k] && cfg.sites[k].site === want);
52
+ if (key) { const e = cfg.sites[key]; return { token: needToken(e.token, key), slug: e.site || key }; }
53
+ }
54
+ return { token: needToken(cfg.token, want), slug: want };
55
+ }
56
+
57
+ const baseUrl = cfg => `https://${cfg.host}/api`;
58
+ const authHeaders = token => ({ authorization: `Bearer ${token}` });
59
+
60
+ /** Antwort als JSON lesen; bei Fehlerstatus eine sprechende Exception (ohne Token). */
61
+ async function asJson(res) {
62
+ let body = {};
63
+ try { body = await res.json(); } catch { /* kein JSON */ }
64
+ if (!res.ok) throw new Error(body.error || `HTTP ${res.status}`);
65
+ return body;
66
+ }
67
+
68
+ /** GET /api/status/<site> – Status der Site (Plan, Slug, Live-URL, Publish-Bedarf, letzte Änderung). */
69
+ export async function apiStatus(cfg, { site } = {}, fetchImpl = fetch) {
70
+ const { token, slug } = resolveTarget(cfg, site);
71
+ const res = await fetchImpl(`${baseUrl(cfg)}/status/${encodeURIComponent(slug)}`, { headers: authHeaders(token) });
72
+ return asJson(res);
73
+ }
74
+
75
+ /** GET /api/export/<site>?which= – ZIP in einen lokalen Ordner speichern. */
76
+ export async function apiExport(cfg, { site, which = "published", outDir = "." } = {}, fetchImpl = fetch) {
77
+ const { token, slug } = resolveTarget(cfg, site);
78
+ const res = await fetchImpl(`${baseUrl(cfg)}/export/${encodeURIComponent(slug)}?which=${encodeURIComponent(which)}`, { headers: authHeaders(token) });
79
+ if (!res.ok) return asJson(res); // Fehler kommen als JSON
80
+ const buf = Buffer.from(await res.arrayBuffer());
81
+ const cd = res.headers.get("content-disposition") || "";
82
+ const m = /filename="?([^"]+)"?/.exec(cd);
83
+ const name = (m && m[1]) || `prontosite-${slug}-${which}.zip`;
84
+ fs.mkdirSync(outDir, { recursive: true });
85
+ const outPath = path.join(outDir, name.replace(/[^a-zA-Z0-9._-]+/g, "_"));
86
+ fs.writeFileSync(outPath, buf);
87
+ return { path: outPath, bytes: buf.length, which, site: slug };
88
+ }
89
+
90
+ /** POST /api/deploy/<site> – ZIP übergeben (+ optional publish/resetOverlay/legal). site="new" = anlegen. */
91
+ export async function apiDeploy(cfg, { site, customer = null, zipPath, publish = false, resetOverlay = false, legal = null } = {}, fetchImpl = fetch) {
92
+ if (!zipPath || !fs.existsSync(zipPath)) throw new Error(`ZIP nicht gefunden: ${zipPath}`);
93
+ const { token, slug } = resolveTarget(cfg, site);
94
+ const q = new URLSearchParams();
95
+ if (publish) q.set("publish", "1");
96
+ if (resetOverlay) q.set("resetOverlay", "1");
97
+ if (legal === true) q.set("legal", "1");
98
+ if (legal === false) q.set("legal", "0");
99
+ if (customer) q.set("customer", customer); // Betreuer-Token + site="new": Ziel-Konto für den Erst-Import
100
+ const qs = q.toString() ? `?${q}` : "";
101
+ const res = await fetchImpl(`${baseUrl(cfg)}/deploy/${encodeURIComponent(slug)}${qs}`, {
102
+ method: "POST", headers: { ...authHeaders(token), "content-type": "application/zip" }, body: fs.readFileSync(zipPath) });
103
+ return asJson(res);
104
+ }
105
+
106
+ /** POST /api/publish/<site> – erneut veröffentlichen (ohne ZIP). force=true = „trotzdem veröffentlichen"
107
+ * (auch ohne erkannte Änderung), analog zum Dashboard-Menüpunkt „Erneut veröffentlichen". */
108
+ export async function apiPublish(cfg, { site, force = false } = {}, fetchImpl = fetch) {
109
+ const { token, slug } = resolveTarget(cfg, site);
110
+ const qs = force ? "?force=1" : "";
111
+ const res = await fetchImpl(`${baseUrl(cfg)}/publish/${encodeURIComponent(slug)}${qs}`, { method: "POST", headers: authHeaders(token) });
112
+ return asJson(res);
113
+ }
114
+
115
+ /** Alle erreichbaren Sites auflisten (Slug, Plan, Publish-Bedarf, letzte Änderung). Bei einer sites-Map wird
116
+ * jeder Eintrag per Status abgefragt; sonst die eine konfigurierte Site. Fehler je Site werden mitgeführt. */
117
+ export async function sitesList(cfg, fetchImpl = fetch) {
118
+ if (cfg.sites) {
119
+ // Per-Kunde-Token-Map: jeden Eintrag einzeln abfragen (jeder Token → seine Site).
120
+ const out = await Promise.all(Object.keys(cfg.sites).map(async label => {
121
+ try {
122
+ const s = await apiStatus(cfg, { site: label }, fetchImpl);
123
+ return { label, slug: s.slug || null, id: s.site || null, name: s.name || null, plan: s.plan,
124
+ everPublished: s.everPublished, publishNeeded: s.publishNeeded, lastChangeAt: s.lastChangeAt || null, liveUrl: s.liveUrl || null };
125
+ } catch (e) { return { label, error: e.message }; }
126
+ }));
127
+ return { count: out.length, sites: out };
128
+ }
129
+ // Einzel-/Betreuer-Token: der Server liefert alle erreichbaren Sites (Betreuer-Token: ALLE betreuten Kunden,
130
+ // ohne Config-Pflege). GET /api/sites.
131
+ const { token } = resolveTarget(cfg, cfg.defaultSite || cfg.site || "active");
132
+ const body = await asJson(await fetchImpl(`${baseUrl(cfg)}/sites`, { headers: authHeaders(token) }));
133
+ const sites = (body.sites || []).map(s => ({ label: s.slug || s.site, slug: s.slug || null, id: s.site || null,
134
+ name: s.name || null, plan: s.plan, everPublished: s.everPublished, publishNeeded: s.publishNeeded,
135
+ lastChangeAt: s.lastChangeAt || null, liveUrl: s.liveUrl || null, customer: s.customer }));
136
+ return { count: sites.length, manager: !!body.manager, sites };
137
+ }
package/index.mjs ADDED
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ProntoSite MCP-Server (stdio) für Claude Desktop u. a. KI-Assistenten.
4
+ * DÜNNER Client um die bestehende ProntoSite-Deploy-API – keine eigene Logik, kein Zustand. Die HTTP-API
5
+ * (curl/Terminal) bleibt der primäre Weg; dies ist nur ein zusätzlicher Client. Token/Site/Host aus lokaler
6
+ * Config (Env > ~/.prontosite.json), nie aus dem Chat.
7
+ *
8
+ * Multi-Site (Betreuer): trägt die Config eine `sites`-Map, adressiert jedes Tool per `site`-Parameter die
9
+ * jeweilige Website (eigenes Token je Site). Arbeitsweise: EIN Chat je Kunde. `sites_list` zeigt alle Sites.
10
+ */
11
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
12
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
13
+ import { z } from "zod";
14
+ import { loadConfig, apiStatus, apiExport, apiDeploy, apiPublish, sitesList } from "./api.mjs";
15
+
16
+ const cfg = loadConfig();
17
+ const server = new McpServer({ name: "prontosite", version: "0.2.0" });
18
+ const asText = obj => ({ content: [{ type: "text", text: JSON.stringify(obj, null, 2) }] });
19
+ const asError = e => ({ isError: true, content: [{ type: "text", text: "Fehler: " + (e?.message || String(e)) }] });
20
+ const SITE_DESC = "Website-Slug (z. B. 'mas-monti'). Ohne Angabe die Standard-Site der Config. Bei Betreuer-"
21
+ + "Config (mehrere Sites) hier die gewünschte Website angeben.";
22
+
23
+ server.tool(
24
+ "sites_list",
25
+ "Listet alle erreichbaren ProntoSite-Websites auf (Slug, Plan, letzte Änderung, offener Veröffentlichungs"
26
+ + "bedarf). Nützlich, um im Chat eine Website beim Namen zu nennen. LESEND.",
27
+ {},
28
+ async () => { try { return asText(await sitesList(cfg)); } catch (e) { return asError(e); } }
29
+ );
30
+
31
+ server.tool(
32
+ "site_status",
33
+ "Status einer ProntoSite-Website: Plan, Slug, Live-URL, letzte Änderungen und offener Veröffentlichungs"
34
+ + "bedarf. LESEND – ändert nichts.",
35
+ { site: z.string().optional().describe(SITE_DESC) },
36
+ async ({ site }) => { try { return asText(await apiStatus(cfg, { site })); } catch (e) { return asError(e); } }
37
+ );
38
+
39
+ server.tool(
40
+ "site_export",
41
+ "Exportiert eine Website als ZIP in ein lokales Verzeichnis. which: 'published' (Live-Stand, Standard), "
42
+ + "'draft' (Entwurf) oder 'original' (flacher Stand ohne ProntoSite-Zutaten). LESEND.",
43
+ { site: z.string().optional().describe(SITE_DESC),
44
+ which: z.enum(["published", "draft", "original"]).default("published"),
45
+ outDir: z.string().default(".").describe("Zielordner für die ZIP-Datei (lokal).") },
46
+ async ({ site, which, outDir }) => { try { return asText(await apiExport(cfg, { site, which, outDir })); } catch (e) { return asError(e); } }
47
+ );
48
+
49
+ server.tool(
50
+ "site_deploy",
51
+ "SCHREIBEND: Übergibt eine lokale ZIP-Datei an ProntoSite (Import). Mit publish=true wird die Website "
52
+ + "DIREKT LIVE geschaltet und der bestehende Live-Stand überschrieben. site='new' legt beim Erst-Import "
53
+ + "(Konto-Token) eine neue Website an. Bitte VOR dem Aufruf bestätigen lassen – und die richtige Website prüfen.",
54
+ { site: z.string().optional().describe(SITE_DESC + " Oder 'new' zum Anlegen (Erst-Import)."),
55
+ customer: z.string().optional().describe("Nur mit Betreuer-Token + site='new': Ziel-Konto-ID (k-…) für die neue Website."),
56
+ zipPath: z.string().describe("Pfad zur ZIP-Datei der Website (lokal)."),
57
+ publish: z.boolean().default(false).describe("true = nach dem Import direkt veröffentlichen (LIVE)."),
58
+ resetOverlay: z.boolean().default(false).describe("true = bisherige Dashboard-Änderungen verwerfen."),
59
+ legal: z.boolean().optional().describe("Rechts-Automatik (Impressum/Datenschutz): true erzwingt, false schaltet ab.") },
60
+ async ({ site, customer, zipPath, publish, resetOverlay, legal }) => {
61
+ try { return asText(await apiDeploy(cfg, { site, customer, zipPath, publish, resetOverlay, legal })); } catch (e) { return asError(e); }
62
+ }
63
+ );
64
+
65
+ server.tool(
66
+ "site_publish",
67
+ "SCHREIBEND: Veröffentlicht den aktuellen Stand einer Website ERNEUT (ohne ZIP) – z. B. um Plattform-"
68
+ + "Aktualisierungen live zu bringen. Meldet die Antwort nothing:true (keine erkannte Änderung), aber du "
69
+ + "willst trotzdem neu ausliefern, setze force:true. Bitte VOR dem Aufruf bestätigen lassen – und die richtige Website prüfen.",
70
+ { site: z.string().optional().describe(SITE_DESC),
71
+ force: z.boolean().default(false).describe("true = trotzdem veröffentlichen, auch ohne erkannte Änderung (wie der Menüpunkt Erneut veröffentlichen).") },
72
+ async ({ site, force }) => { try { return asText(await apiPublish(cfg, { site, force })); } catch (e) { return asError(e); } }
73
+ );
74
+
75
+ const transport = new StdioServerTransport();
76
+ await server.connect(transport);
package/package.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "@prontosite/mcp",
3
+ "version": "0.2.0",
4
+ "description": "MCP-Server für ProntoSite – Website-Status, Export, Deploy und Veröffentlichen direkt aus einem KI-Assistenten (Claude Desktop). Dünner Client um die ProntoSite Deploy-API.",
5
+ "type": "module",
6
+ "bin": { "prontosite-mcp": "index.mjs" },
7
+ "main": "index.mjs",
8
+ "files": ["index.mjs", "api.mjs", "README.md"],
9
+ "engines": { "node": ">=18" },
10
+ "keywords": ["prontosite", "mcp", "model-context-protocol", "claude", "deploy", "website"],
11
+ "license": "UNLICENSED",
12
+ "homepage": "https://prontosite.io/mcp-anleitung",
13
+ "publishConfig": { "access": "public" },
14
+ "dependencies": {
15
+ "@modelcontextprotocol/sdk": "^1.0.0",
16
+ "zod": "^3.23.0"
17
+ }
18
+ }