@prontosite/mcp 0.11.2 → 0.12.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.
- package/CHANGELOG.md +19 -1
- package/api.mjs +13 -3
- package/index.mjs +3 -2
- package/package.json +1 -1
- package/tools.mjs +65 -22
package/CHANGELOG.md
CHANGED
|
@@ -3,12 +3,30 @@
|
|
|
3
3
|
Alle nennenswerten Änderungen am MCP-Server für ProntoSite. Der Server ist ein dünner Client um die
|
|
4
4
|
ProntoSite Deploy-API; die eigentliche Logik lebt serverseitig.
|
|
5
5
|
|
|
6
|
-
## 0.
|
|
6
|
+
## 0.12.0 – 2026-10-19
|
|
7
|
+
|
|
8
|
+
- **Arbeitsweise im Server verankert:** Der MCP-Server liefert beim Verbinden `instructions` – Standard ist das Arbeiten direkt
|
|
9
|
+
am Server-Entwurf (`site_status` → `site_edit_text`/`site_file_*` → `site_status`, Vorschau-Link nennen). Export + Deploy nur
|
|
10
|
+
auf ausdrücklichen Wunsch bzw. für größere Umbauten; `site_publish` nur nach Bestätigung im Chat; bei 409 erst `site_status`.
|
|
11
|
+
- **Neu `site_edit_text`** (`find`, `replace`, optional `pages`, `baseRev`): Text über alle Seiten suchen und ersetzen –
|
|
12
|
+
Formatierung bleibt, ein Rückgängig-Schritt, Rechtstexte ausgenommen; bei mehreren Fundstellen ohne `pages` nur eine Liste.
|
|
13
|
+
- Beschreibungen: Datei-Tools „STANDARDWEG", Export/Deploy „AUSNAHME"; `site_publish` ausdrücklich „live für Besucher, nur nach
|
|
14
|
+
Bestätigung".
|
|
15
|
+
- **`baseRev` beim Deploy-Update Pflicht** (F4a): ZIPs aus `site_export` tragen ihn automatisch, sonst `baseRev` aus `site_status`
|
|
16
|
+
mitgeben (neuer Parameter an `site_deploy`); ohne → `428`.
|
|
17
|
+
|
|
18
|
+
## 0.11.3 – 2026-10-19
|
|
19
|
+
|
|
20
|
+
- Beschreibungen von `site_file_put`/`site_file_delete`/`site_file_rename`: Schreiben geht jetzt auch mit Kunden-Token
|
|
21
|
+
(serverseitig seit 19.10.); Skriptdateien (JavaScript) bleiben Betreuern vorbehalten – Kunden bekommen dort 403.
|
|
22
|
+
|
|
23
|
+
## 0.11.2 – 2026-10-19 (veröffentlicht 19.10.2026)
|
|
7
24
|
|
|
8
25
|
- **Intern, kein Verhaltenswechsel:** die Tool-Definitionen liegen jetzt in `tools.mjs` – dieselbe Quelle nutzt der neue
|
|
9
26
|
Remote-Endpunkt `https://app.prontosite.io/mcp` (Streamable HTTP, Bearer-Token; für Cursor und Claude Code ohne lokale
|
|
10
27
|
Installation). Lokal bleiben `site_export`/`site_deploy` mit Dateipfaden; remote heißen sie Download-Link bzw.
|
|
11
28
|
`deploy_upload_begin` + `deploy_commit`.
|
|
29
|
+
- Die Config-Prüfung akzeptiert neben `psk_` auch `pso_` (OAuth-Zugriff – nur der Remote-Endpunkt reicht ihn intern weiter; lokal ändert sich nichts).
|
|
12
30
|
- MCP-SDK mindestens 1.31.0 (Sicherheitshinweis GHSA-6qxp-vccf-f47h betrifft nur die OAuth-Client-Seite, hier ungenutzt).
|
|
13
31
|
|
|
14
32
|
## 0.11.1 – 2026-10-18
|
package/api.mjs
CHANGED
|
@@ -43,7 +43,8 @@ export function loadConfig(env = process.env, home = os.homedir()) {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
const needToken = (token, ref) => {
|
|
46
|
-
|
|
46
|
+
// psk_ = API-Token; pso_ = OAuth-Zugriff (nur der Remote-Endpunkt /mcp reicht ihn an seinen internen Loopback weiter).
|
|
47
|
+
if (!token || !/^(psk_|pso_)/.test(String(token)))
|
|
47
48
|
throw new Error(`Kein API-Token für „${ref}" konfiguriert. Setze PRONTOSITE_TOKEN oder trage die Site in ~/.prontosite.json (sites) ein.`);
|
|
48
49
|
return token;
|
|
49
50
|
};
|
|
@@ -155,7 +156,7 @@ export async function apiExport(cfg, { site, which = "published", outDir = "." }
|
|
|
155
156
|
}
|
|
156
157
|
|
|
157
158
|
/** POST /api/deploy/<site> – ZIP übergeben (+ optional publish/resetOverlay/legal). site="new" = anlegen. */
|
|
158
|
-
export async function apiDeploy(cfg, { site, customer = null, slug: wishSlug = null, name = null, zipPath, publish = false, resetOverlay = false, legal = null, company = null, siteOverride = false } = {}, fetchImpl = fetch) {
|
|
159
|
+
export async function apiDeploy(cfg, { site, customer = null, slug: wishSlug = null, name = null, zipPath, baseRev = null, publish = false, resetOverlay = false, legal = null, company = null, siteOverride = false } = {}, fetchImpl = fetch) {
|
|
159
160
|
if (!zipPath || !fs.existsSync(zipPath)) throw new Error(`ZIP nicht gefunden: ${zipPath}`);
|
|
160
161
|
const target = resolveTarget(cfg, site);
|
|
161
162
|
const { token, slug } = target;
|
|
@@ -172,7 +173,8 @@ export async function apiDeploy(cfg, { site, customer = null, slug: wishSlug = n
|
|
|
172
173
|
if (legal === false) q.set("legal", "0");
|
|
173
174
|
if (effCustomer) q.set("customer", effCustomer); // Betreuer-Token + „new": Ziel-Konto für den Erst-Import
|
|
174
175
|
if (wishSlug) q.set("slug", String(wishSlug)); // Erst-Import: Wunsch-Adresse (sonst wird sie abgeleitet)
|
|
175
|
-
if (isNew && name) q.set("name", String(name));
|
|
176
|
+
if (isNew && name) q.set("name", String(name));
|
|
177
|
+
if (!isNew && Number.isInteger(baseRev)) q.set("baseRev", String(baseRev)); // F4a: ab 0.12 Pflicht bei Updates (ZIP aus site_export trägt ihn selbst) // Erst-Import: Site-Name explizit setzen (sonst aus der Startseite abgeleitet)
|
|
176
178
|
const qs = q.toString() ? `?${q}` : "";
|
|
177
179
|
const res = await fetchImpl(`${baseUrl(cfg)}/deploy/${encodeURIComponent(slug)}${qs}`, {
|
|
178
180
|
method: "POST", headers: { ...authHeaders(token, cfg), "content-type": "application/zip" }, body: fs.readFileSync(zipPath) });
|
|
@@ -386,6 +388,14 @@ export async function apiFileRename(cfg, { site, from, to, baseRev } = {}, fetch
|
|
|
386
388
|
method: "POST", headers: { ...authHeaders(token, cfg), "content-type": "application/json" }, body: JSON.stringify({ from, to, baseRev }) }));
|
|
387
389
|
}
|
|
388
390
|
|
|
391
|
+
/** POST /api/edit-text/<site> – Text über alle Seiten suchen + ersetzen (Entwurf, eine Undo-Einheit). 409 kommt als Objekt zurück. */
|
|
392
|
+
export async function apiEditText(cfg, { site, find, replace, pages = null, baseRev = null } = {}, fetchImpl = fetch) {
|
|
393
|
+
const { token, slug } = resolveTarget(cfg, site);
|
|
394
|
+
const body = { find, replace, ...(Array.isArray(pages) && pages.length ? { pages } : {}), ...(Number.isInteger(baseRev) ? { baseRev } : {}) };
|
|
395
|
+
return asJsonOr409(await fetchImpl(`${baseUrl(cfg)}/edit-text/${encodeURIComponent(slug)}`, {
|
|
396
|
+
method: "POST", headers: { ...authHeaders(token, cfg), "content-type": "application/json" }, body: JSON.stringify(body) }));
|
|
397
|
+
}
|
|
398
|
+
|
|
389
399
|
/** GET/POST /api/lock/<site> – sanfte Sperre („in Bearbeitung durch …"); blockiert niemanden. action: lock|unlock|takeover. */
|
|
390
400
|
export async function apiLock(cfg, { site, action = null, note = null } = {}, fetchImpl = fetch) {
|
|
391
401
|
const { token, slug } = resolveTarget(cfg, site);
|
package/index.mjs
CHANGED
|
@@ -14,13 +14,14 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
14
14
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
15
15
|
import { z } from "zod";
|
|
16
16
|
import { loadConfig } from "./api.mjs";
|
|
17
|
-
import { registerTools } from "./tools.mjs";
|
|
17
|
+
import { registerTools, SERVER_INSTRUCTIONS } from "./tools.mjs";
|
|
18
18
|
|
|
19
19
|
const cfg = loadConfig();
|
|
20
20
|
// Deutliche Startwarnung bei ambiger Konfiguration (Top-Level-Token NEBEN sites-Map). Nach stderr, damit
|
|
21
21
|
// es im MCP-Log/Terminal auffällt; die endgültige Auflösung (Betreuer-Token-Vorrang vs. Map) klärt sites_list.
|
|
22
22
|
for (const w of (cfg.warnings || [])) process.stderr.write("[prontosite-mcp] ⚠ " + w + "\n");
|
|
23
|
-
|
|
23
|
+
// instructions: Arbeitsweise (Weg B = Standard) – gleicher Text wie am Remote-Endpunkt.
|
|
24
|
+
const server = new McpServer({ name: "prontosite", version: "0.12.0" }, { instructions: SERVER_INSTRUCTIONS });
|
|
24
25
|
// Tool-Definitionen: gemeinsame Quelle mit dem Remote-Endpunkt (tools.mjs, Konzept KI-Anbindung §4.2).
|
|
25
26
|
registerTools(server, { z, cfg, mode: "local" });
|
|
26
27
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prontosite/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
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
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/tools.mjs
CHANGED
|
@@ -9,7 +9,24 @@
|
|
|
9
9
|
* site_domain_connect nur mit Betreuer-Token (Entscheidung 9a).
|
|
10
10
|
* `z` wird vom Aufrufer übergeben (dieselbe zod-Instanz wie sein MCP-SDK).
|
|
11
11
|
*/
|
|
12
|
-
import { apiStatus, apiExport, apiExportFetch, apiDeploy, apiPublish, sitesList, apiAccountCreate, apiCompanyGet, apiCompanySet, apiLegalOptions, apiDomainStatus, apiDomainConnect, apiEmbeds, apiEmbedConsent, apiWidgets, apiWidgetSet, apiFiles, apiFileGet, apiFilePut, apiFileDelete, apiFileRename, apiLock } from "./api.mjs";
|
|
12
|
+
import { apiEditText, apiStatus, apiExport, apiExportFetch, apiDeploy, apiPublish, sitesList, apiAccountCreate, apiCompanyGet, apiCompanySet, apiLegalOptions, apiDomainStatus, apiDomainConnect, apiEmbeds, apiEmbedConsent, apiWidgets, apiWidgetSet, apiFiles, apiFileGet, apiFilePut, apiFileDelete, apiFileRename, apiLock } from "./api.mjs";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Arbeitsweise (MCP-`instructions` beim Initialize – lokal UND /mcp): Weg B ist der Standard (Nick 19.10.).
|
|
16
|
+
* Direkt am Server-Entwurf arbeiten; Export + Deploy nur auf ausdrücklichen Wunsch bzw. für Komplett-Umbauten.
|
|
17
|
+
*/
|
|
18
|
+
export const SERVER_INSTRUCTIONS = [
|
|
19
|
+
"ProntoSite – so arbeitest du mit einer Website:",
|
|
20
|
+
"1. STANDARD: direkt am Server-Entwurf arbeiten. Ablauf: site_status (Stand, draftRev, wer zuletzt geändert hat) → Änderung mit "
|
|
21
|
+
+ "site_edit_text (Text suchen und ersetzen über alle Seiten) bzw. site_file_get/site_file_put (HTML/CSS) → site_status (Kontrolle). "
|
|
22
|
+
+ "Danach dem Nutzer sagen, was geändert wurde, und den Vorschau-Link (previewUrl aus site_status) nennen.",
|
|
23
|
+
"2. AUSNAHME: site_export + Deploy (site_deploy bzw. deploy_upload_begin/deploy_commit) NUR, wenn der Nutzer ausdrücklich lokal arbeiten "
|
|
24
|
+
+ "möchte oder die Website komplett umgebaut wird.",
|
|
25
|
+
"3. Veröffentlichen (site_publish, publish=true) NUR nach ausdrücklicher Bestätigung des Nutzers im Chat – bis dahin bleibt alles im Entwurf.",
|
|
26
|
+
"4. Konflikt (409, conflict:true): nicht erzwingen. Erst site_status aufrufen (zeigt, wer was geändert hat), Inhalt neu holen, Änderung "
|
|
27
|
+
+ "neu anwenden und mit der neuen draftRev erneut senden.",
|
|
28
|
+
"5. Jede Änderung lässt sich rückgängig machen; Rechtstexte (Impressum/Datenschutz) nicht umformulieren – sie kommen aus den Firmendaten.",
|
|
29
|
+
].join("\n");
|
|
13
30
|
|
|
14
31
|
const asText = obj => ({ content: [{ type: "text", text: JSON.stringify(obj, null, 2) }] });
|
|
15
32
|
const asError = e => ({ isError: true, content: [{ type: "text", text: "Fehler: " + (e?.message || String(e)) }] });
|
|
@@ -80,18 +97,36 @@ export function registerTools(server, { z, cfg, mode = "local", remote = null })
|
|
|
80
97
|
}
|
|
81
98
|
server.tool(
|
|
82
99
|
"site_status",
|
|
83
|
-
"Status einer ProntoSite-Website: Plan, Slug, Live-URL,
|
|
84
|
-
+ "
|
|
85
|
-
+ "
|
|
100
|
+
"LESEND – SCHRITT 1 und letzter Schritt jeder Änderung: Status einer ProntoSite-Website: Plan, Slug, Live-URL, Vorschau-Link "
|
|
101
|
+
+ "(previewUrl), letzte Änderungen, offener Veröffentlichungsbedarf (publishNeeded), draftRev und recentEdits (wer hat zuletzt "
|
|
102
|
+
+ "wo geändert). draftRev ist die baseRev für site_edit_text/site_file_put. Ändert nichts.",
|
|
86
103
|
{ site: z.string().optional().describe(SITE_DESC) },
|
|
87
104
|
async ({ site }) => { try { return asText(await apiStatus(cfg, { site })); } catch (e) { return asError(e); } }
|
|
88
105
|
);
|
|
89
106
|
|
|
107
|
+
server.tool(
|
|
108
|
+
"site_edit_text",
|
|
109
|
+
"SCHREIBEND (Entwurf, nicht live) – STANDARDWEG für Textänderungen (Telefonnummer, Öffnungszeiten, Preise, Sätze): sucht `find` "
|
|
110
|
+
+ "im sichtbaren Text ALLER Seiten und ersetzt ihn durch `replace` – als eine rückgängig machbare Änderung, Formatierung bleibt. "
|
|
111
|
+
+ "Gibt es mehr als eine Fundstelle und du hast keine pages angegeben, ändert der Server NICHTS und liefert nur die Liste "
|
|
112
|
+
+ "(Seite, Knoten, Kontext): zeig sie dem Nutzer bzw. entscheide anhand des Kontexts und ruf das Tool erneut mit pages:[…] auf "
|
|
113
|
+
+ "(alle Fundstellen dieser Seiten werden ersetzt). Rechtstexte und geschützte Bereiche werden nie geändert (skipped). "
|
|
114
|
+
+ "linkHints nennt tel:/mailto:-Links mit dem alten Wert – die ändert dieses Tool nicht (ggf. site_file_put). "
|
|
115
|
+
+ "Vorher site_status, danach site_status; live erst nach Bestätigung mit site_publish.",
|
|
116
|
+
{ site: z.string().optional().describe(SITE_DESC),
|
|
117
|
+
find: z.string().describe("Genau der Text, wie er auf der Website steht (z. B. '02242 123456')."),
|
|
118
|
+
replace: z.string().describe("Neuer Text (reiner Text)."),
|
|
119
|
+
pages: z.array(z.string()).optional().describe("Optional: Seiten-IDs (aus der Fundstellen-Liste), auf denen ersetzt werden soll. Ohne Angabe nur bei genau EINER Fundstelle."),
|
|
120
|
+
baseRev: z.number().int().optional().describe("Optional: draftRev aus site_status (Konfliktschutz, 409 bei zwischenzeitlicher Änderung derselben Seite).") },
|
|
121
|
+
async ({ site, find, replace, pages, baseRev }) => { try { return asText(await apiEditText(cfg, { site, find, replace, pages, baseRev })); } catch (e) { return asError(e); } }
|
|
122
|
+
);
|
|
123
|
+
|
|
90
124
|
if (mode === "local") {
|
|
91
125
|
server.tool(
|
|
92
126
|
"site_export",
|
|
93
|
-
"
|
|
94
|
-
+ "
|
|
127
|
+
"AUSNAHME – nur wenn der Nutzer ausdrücklich lokal arbeiten möchte oder für größere Umbauten (sonst site_edit_text/site_file_*): "
|
|
128
|
+
+ "Exportiert eine Website als ZIP in ein lokales Verzeichnis. which: 'published' (Live-Stand, Standard), "
|
|
129
|
+
+ "'draft' (Entwurf – zum Weiterbearbeiten) oder 'original' (flacher Stand ohne ProntoSite-Zutaten). LESEND.",
|
|
95
130
|
{ site: z.string().optional().describe(SITE_DESC),
|
|
96
131
|
which: z.enum(["published", "draft", "original"]).default("published"),
|
|
97
132
|
outDir: z.string().default(".").describe("Zielordner für die ZIP-Datei (lokal).") },
|
|
@@ -101,7 +136,9 @@ export function registerTools(server, { z, cfg, mode = "local", remote = null })
|
|
|
101
136
|
|
|
102
137
|
server.tool(
|
|
103
138
|
"site_deploy",
|
|
104
|
-
"SCHREIBEND:
|
|
139
|
+
"SCHREIBEND – AUSNAHME: nur für Erst-Import, ausdrücklichen Wunsch nach lokaler Arbeit oder größere Umbauten (kleine Änderungen: "
|
|
140
|
+
+ "site_edit_text/site_file_put). Übergibt eine lokale ZIP-Datei an ProntoSite (Import). Bei Updates PFLICHT: baseRev – exportiere "
|
|
141
|
+
+ "vorher mit site_export (which='draft'), die ZIP trägt ihren Stand dann automatisch; sonst baseRev = draftRev aus site_status. Mit publish=true wird die Website "
|
|
105
142
|
+ "DIREKT LIVE geschaltet und der bestehende Live-Stand überschrieben. ERST-IMPORT (neue Website): "
|
|
106
143
|
+ "Einzel-Config site='new'; Multi-Site/Betreuer site='<label>:new' (legt im Konto DES LABELS an). "
|
|
107
144
|
+ "WICHTIG beim ERST-IMPORT: Frag den Nutzer VOR dem Deploy nach der gewünschten Adresse "
|
|
@@ -130,13 +167,14 @@ export function registerTools(server, { z, cfg, mode = "local", remote = null })
|
|
|
130
167
|
name: z.string().optional().describe("Erst-Import: expliziter Website-Name (überschreibt die Ableitung aus der Startseite). Nützlich, wenn der Titel einen Ortszusatz trägt (z. B. 'Physio Phoenix – Bad Honnef Süd' → name='Physio Phoenix'). Bei Updates ignoriert."),
|
|
131
168
|
customer: z.string().optional().describe("Betreuer-Token + Erst-Import: Ziel-Konto-ID (k-…) für die neue Website. Alternativ direkt im site-Argument als '<Konto-ID>:new'."),
|
|
132
169
|
zipPath: z.string().describe("Pfad zur ZIP-Datei der Website (lokal)."),
|
|
170
|
+
baseRev: z.number().int().optional().describe("Update ohne Export-ZIP: draftRev aus site_status (sonst lehnt der Server mit 428 ab). Bei ZIPs aus site_export nicht nötig."),
|
|
133
171
|
publish: z.boolean().default(false).describe("true = nach dem Import direkt veröffentlichen (LIVE)."),
|
|
134
172
|
resetOverlay: z.boolean().default(false).describe("true = bisherige Dashboard-Änderungen verwerfen."),
|
|
135
173
|
legal: z.boolean().optional().describe("Rechts-Automatik (Impressum/Datenschutz): true erzwingt, false schaltet ab."),
|
|
136
174
|
company: companyObject.optional().describe("Optional: Firmendaten VOR dem Deploy setzen (ein Schritt) – Impressum/Datenschutz nutzen sie dann direkt. Teil-Update."),
|
|
137
175
|
siteOverride: z.boolean().default(false).describe("Nur mit company: true = Firmendaten NUR für diese Website (statt konto-weit).") },
|
|
138
|
-
async ({ site, slug, name, customer, zipPath, publish, resetOverlay, legal, company, siteOverride }) => {
|
|
139
|
-
try { return asText(await apiDeploy(cfg, { site, slug, name, customer, zipPath, publish, resetOverlay, legal, company, siteOverride })); } catch (e) { return asError(e); }
|
|
176
|
+
async ({ site, slug, name, customer, zipPath, baseRev, publish, resetOverlay, legal, company, siteOverride }) => {
|
|
177
|
+
try { return asText(await apiDeploy(cfg, { site, slug, name, customer, zipPath, baseRev, publish, resetOverlay, legal, company, siteOverride })); } catch (e) { return asError(e); }
|
|
140
178
|
}
|
|
141
179
|
);
|
|
142
180
|
|
|
@@ -145,9 +183,9 @@ export function registerTools(server, { z, cfg, mode = "local", remote = null })
|
|
|
145
183
|
}
|
|
146
184
|
server.tool(
|
|
147
185
|
"site_publish",
|
|
148
|
-
"SCHREIBEND: Veröffentlicht den aktuellen
|
|
149
|
-
+ "
|
|
150
|
-
+ "willst trotzdem neu ausliefern, setze force:true.
|
|
186
|
+
"SCHREIBEND – LIVE FÜR BESUCHER: Veröffentlicht den aktuellen Entwurf einer Website. NUR nach ausdrücklicher Bestätigung "
|
|
187
|
+
+ "des Nutzers im Chat aufrufen (vorher kurz zusammenfassen, was live geht, und die richtige Website prüfen). Meldet die Antwort "
|
|
188
|
+
+ "nothing:true (keine erkannte Änderung), aber du willst trotzdem neu ausliefern, setze force:true.",
|
|
151
189
|
{ site: z.string().optional().describe(SITE_DESC),
|
|
152
190
|
force: z.boolean().default(false).describe("true = trotzdem veröffentlichen, auch ohne erkannte Änderung (wie der Menüpunkt Erneut veröffentlichen).") },
|
|
153
191
|
async ({ site, force }) => { try { return asText(await apiPublish(cfg, { site, force })); } catch (e) { return asError(e); } }
|
|
@@ -290,7 +328,9 @@ export function registerTools(server, { z, cfg, mode = "local", remote = null })
|
|
|
290
328
|
);
|
|
291
329
|
|
|
292
330
|
// ---- Datei-API: kleine Änderungen am Entwurf direkt auf dem Server (Konzept §7) ----
|
|
293
|
-
const FILE_SMALL = "
|
|
331
|
+
const FILE_SMALL = "STANDARDWEG: Änderungen direkt am Server-Entwurf (für reine Textänderungen am einfachsten site_edit_text); "
|
|
332
|
+
+ "Export + Deploy nur als Ausnahme für größere Umbauten oder auf ausdrücklichen Wunsch. "
|
|
333
|
+
+ "Skriptdateien (JavaScript) ändern nur Betreuer und nur bei JavaScript-Freigabe der Website – Kunden bekommen dort 403. ";
|
|
294
334
|
server.tool(
|
|
295
335
|
"site_files",
|
|
296
336
|
"LESEND: Listet die Dateien des Entwurfs einer Website (Pfad, Größe, Typ, ob bearbeitbar) und die aktuelle draftRev. "
|
|
@@ -308,7 +348,7 @@ export function registerTools(server, { z, cfg, mode = "local", remote = null })
|
|
|
308
348
|
);
|
|
309
349
|
server.tool(
|
|
310
350
|
"site_file_put",
|
|
311
|
-
"SCHREIBEND (Entwurf, nicht live
|
|
351
|
+
"SCHREIBEND (Entwurf, nicht live): Ersetzt den Inhalt einer Text-Datei (HTML einer bestehenden Seite, "
|
|
312
352
|
+ "CSS, SVG, JSON, TXT …). PFLICHT: baseRev = draftRev aus deinem letzten site_file_get/site_status. Antwortet der Server mit "
|
|
313
353
|
+ "conflict:true (409), hat jemand anderes dieselbe Datei inzwischen geändert: NICHT erzwingen – Datei neu holen, Änderung neu "
|
|
314
354
|
+ "anwenden, mit der neuen draftRev erneut senden; die Antwort listet wer/wann/was. Prüfe danach overlay (umgehängte/deaktivierte "
|
|
@@ -324,7 +364,7 @@ export function registerTools(server, { z, cfg, mode = "local", remote = null })
|
|
|
324
364
|
);
|
|
325
365
|
server.tool(
|
|
326
366
|
"site_file_delete",
|
|
327
|
-
"SCHREIBEND (Entwurf
|
|
367
|
+
"SCHREIBEND (Entwurf): Markiert eine Text-Datei im Entwurf als gelöscht (rückgängig machbar; Seiten "
|
|
328
368
|
+ "löschen geht über das Dashboard). baseRev Pflicht, confirmDestructive:true erst NACH Rückfrage beim Nutzer. Prüfe vorher, "
|
|
329
369
|
+ "ob die Datei noch verlinkt ist. " + FILE_SMALL,
|
|
330
370
|
{ site: z.string().optional().describe(SITE_DESC), path: z.string(), baseRev: z.number().int(),
|
|
@@ -333,7 +373,7 @@ export function registerTools(server, { z, cfg, mode = "local", remote = null })
|
|
|
333
373
|
);
|
|
334
374
|
server.tool(
|
|
335
375
|
"site_file_rename",
|
|
336
|
-
"SCHREIBEND (Entwurf
|
|
376
|
+
"SCHREIBEND (Entwurf): Benennt eine Text-Datei (kein HTML) um. Verweise in anderen Dateien werden NICHT "
|
|
337
377
|
+ "angepasst – die musst du selbst per site_file_put nachziehen. baseRev Pflicht. " + FILE_SMALL,
|
|
338
378
|
{ site: z.string().optional().describe(SITE_DESC), from: z.string(), to: z.string(), baseRev: z.number().int() },
|
|
339
379
|
async ({ site, from, to, baseRev }) => { try { return asText(await apiFileRename(cfg, { site, from, to, baseRev })); } catch (e) { return asError(e); } }
|
|
@@ -362,7 +402,8 @@ export function registerTools(server, { z, cfg, mode = "local", remote = null })
|
|
|
362
402
|
function registerRemoteTransferTools(server, { z, cfg, remote, SITE_DESC, asText, asError }) {
|
|
363
403
|
server.tool(
|
|
364
404
|
"site_export",
|
|
365
|
-
"
|
|
405
|
+
"AUSNAHME – nur wenn der Nutzer ausdrücklich lokal arbeiten möchte oder für größere Umbauten (sonst site_edit_text/site_file_*). "
|
|
406
|
+
+ "LESEND: Exportiert eine Website als ZIP und liefert einen DOWNLOAD-LINK (15 Minuten gültig, nur EINMAL abrufbar). "
|
|
366
407
|
+ "which: 'published' (Live-Stand, Standard), 'draft' (Entwurf – für lokales Bearbeiten und späteres deploy_upload_begin) oder "
|
|
367
408
|
+ "'original' (flacher Stand ohne ProntoSite-Zutaten). Den Link mit curl/Browser laden, z. B. curl -o site.zip '<downloadUrl>'.",
|
|
368
409
|
{ site: z.string().optional().describe(SITE_DESC),
|
|
@@ -376,7 +417,8 @@ function registerRemoteTransferTools(server, { z, cfg, remote, SITE_DESC, asText
|
|
|
376
417
|
);
|
|
377
418
|
server.tool(
|
|
378
419
|
"deploy_upload_begin",
|
|
379
|
-
"SCHREIBEND
|
|
420
|
+
"SCHREIBEND – AUSNAHME: nur für Erst-Import, ausdrücklichen Wunsch nach lokaler Arbeit oder größere Umbauten (kleine Änderungen: "
|
|
421
|
+
+ "site_edit_text/site_file_put). Schritt 1 von 2: ZIP-Deploy vorbereiten. Liefert einen UPLOAD-LINK (HTTP PUT, 15 Minuten, einmalig, max. "
|
|
380
422
|
+ "100 MB) für die ZIP-Datei der Website, z. B. curl -T site.zip '<uploadUrl>'. Danach deploy_commit mit der uploadId. "
|
|
381
423
|
+ "Ein Deploy ersetzt das Original der Website (rückholbar über die Re-Import-Zeitleiste); mit publish=true geht der Stand "
|
|
382
424
|
+ "DIREKT LIVE. Ab dem Starter-Tarif. Bitte VOR dem Aufruf bestätigen lassen – und die richtige Website prüfen. "
|
|
@@ -387,9 +429,10 @@ function registerRemoteTransferTools(server, { z, cfg, remote, SITE_DESC, asText
|
|
|
387
429
|
name: z.string().optional().describe("Erst-Import: expliziter Website-Name. Bei Updates ignoriert."),
|
|
388
430
|
publish: z.boolean().default(false).describe("true = nach dem Import direkt veröffentlichen (LIVE)."),
|
|
389
431
|
resetOverlay: z.boolean().default(false).describe("true = bisherige Dashboard-Änderungen verwerfen."),
|
|
390
|
-
legal: z.boolean().optional().describe("Rechts-Automatik (Impressum/Datenschutz): true erzwingt, false schaltet ab.")
|
|
391
|
-
|
|
392
|
-
|
|
432
|
+
legal: z.boolean().optional().describe("Rechts-Automatik (Impressum/Datenschutz): true erzwingt, false schaltet ab."),
|
|
433
|
+
baseRev: z.number().int().optional().describe("Update ohne Export-ZIP: draftRev aus site_status (Pflicht, sonst 428). ZIPs aus site_export tragen ihn automatisch.") },
|
|
434
|
+
async ({ site, slug, name, publish, resetOverlay, legal, baseRev }) => {
|
|
435
|
+
try { return asText(remote.beginUpload({ site, slug, name, publish, resetOverlay, legal, baseRev })); } catch (e) { return asError(e); }
|
|
393
436
|
}
|
|
394
437
|
);
|
|
395
438
|
server.tool(
|
|
@@ -404,7 +447,7 @@ function registerRemoteTransferTools(server, { z, cfg, remote, SITE_DESC, asText
|
|
|
404
447
|
up = remote.takeUpload(uploadId);
|
|
405
448
|
const o = up.options;
|
|
406
449
|
return asText(await apiDeploy(cfg, { site: o.site, slug: o.slug, name: o.name, zipPath: up.path, publish: o.publish,
|
|
407
|
-
resetOverlay: o.resetOverlay, legal: o.legal }));
|
|
450
|
+
resetOverlay: o.resetOverlay, legal: o.legal, baseRev: o.baseRev }));
|
|
408
451
|
} catch (e) { return asError(e); } finally { if (up) up.done(); }
|
|
409
452
|
}
|
|
410
453
|
);
|