@prontosite/mcp 0.11.2 → 0.12.1
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 +28 -1
- package/api.mjs +14 -3
- package/index.mjs +3 -2
- package/package.json +1 -1
- package/tools.mjs +72 -22
package/CHANGELOG.md
CHANGED
|
@@ -3,12 +3,39 @@
|
|
|
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.1 – 2026-10-19
|
|
7
|
+
|
|
8
|
+
- **`site_edit_text` in einem Aufruf für die ganze Website:** `scope:"all"` ersetzt alle Fundstellen auf allen Seiten sofort
|
|
9
|
+
(ein Rückgängig-Schritt), `region` (`footer`/`header`/`main`/`all`) begrenzt auf einen Bereich, `attributes` (Standard an)
|
|
10
|
+
ersetzt auch in Links (`tel:`/`mailto:` normalisiert, Ländervorwahl bleibt) und Attributen (`alt`, `title`, `meta content`).
|
|
11
|
+
Antwort je Seite gefunden/geändert; steht der Wert in den Firmendaten, verweist `company` auf `site_company_set`.
|
|
12
|
+
- Instructions: Kontaktdaten-, Text- und Link-Änderungen immer per `site_edit_text` mit `scope:"all"`, nie Seite für Seite per
|
|
13
|
+
`site_file_put`.
|
|
14
|
+
|
|
15
|
+
## 0.12.0 – 2026-10-19
|
|
16
|
+
|
|
17
|
+
- **Arbeitsweise im Server verankert:** Der MCP-Server liefert beim Verbinden `instructions` – Standard ist das Arbeiten direkt
|
|
18
|
+
am Server-Entwurf (`site_status` → `site_edit_text`/`site_file_*` → `site_status`, Vorschau-Link nennen). Export + Deploy nur
|
|
19
|
+
auf ausdrücklichen Wunsch bzw. für größere Umbauten; `site_publish` nur nach Bestätigung im Chat; bei 409 erst `site_status`.
|
|
20
|
+
- **Neu `site_edit_text`** (`find`, `replace`, optional `pages`, `baseRev`): Text über alle Seiten suchen und ersetzen –
|
|
21
|
+
Formatierung bleibt, ein Rückgängig-Schritt, Rechtstexte ausgenommen; bei mehreren Fundstellen ohne `pages` nur eine Liste.
|
|
22
|
+
- Beschreibungen: Datei-Tools „STANDARDWEG", Export/Deploy „AUSNAHME"; `site_publish` ausdrücklich „live für Besucher, nur nach
|
|
23
|
+
Bestätigung".
|
|
24
|
+
- **`baseRev` beim Deploy-Update Pflicht** (F4a): ZIPs aus `site_export` tragen ihn automatisch, sonst `baseRev` aus `site_status`
|
|
25
|
+
mitgeben (neuer Parameter an `site_deploy`); ohne → `428`.
|
|
26
|
+
|
|
27
|
+
## 0.11.3 – 2026-10-19
|
|
28
|
+
|
|
29
|
+
- Beschreibungen von `site_file_put`/`site_file_delete`/`site_file_rename`: Schreiben geht jetzt auch mit Kunden-Token
|
|
30
|
+
(serverseitig seit 19.10.); Skriptdateien (JavaScript) bleiben Betreuern vorbehalten – Kunden bekommen dort 403.
|
|
31
|
+
|
|
32
|
+
## 0.11.2 – 2026-10-19 (veröffentlicht 19.10.2026)
|
|
7
33
|
|
|
8
34
|
- **Intern, kein Verhaltenswechsel:** die Tool-Definitionen liegen jetzt in `tools.mjs` – dieselbe Quelle nutzt der neue
|
|
9
35
|
Remote-Endpunkt `https://app.prontosite.io/mcp` (Streamable HTTP, Bearer-Token; für Cursor und Claude Code ohne lokale
|
|
10
36
|
Installation). Lokal bleiben `site_export`/`site_deploy` mit Dateipfaden; remote heißen sie Download-Link bzw.
|
|
11
37
|
`deploy_upload_begin` + `deploy_commit`.
|
|
38
|
+
- Die Config-Prüfung akzeptiert neben `psk_` auch `pso_` (OAuth-Zugriff – nur der Remote-Endpunkt reicht ihn intern weiter; lokal ändert sich nichts).
|
|
12
39
|
- MCP-SDK mindestens 1.31.0 (Sicherheitshinweis GHSA-6qxp-vccf-f47h betrifft nur die OAuth-Client-Seite, hier ungenutzt).
|
|
13
40
|
|
|
14
41
|
## 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,15 @@ 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, scope = null, region = null, attributes = null, pages = null, baseRev = null } = {}, fetchImpl = fetch) {
|
|
393
|
+
const { token, slug } = resolveTarget(cfg, site);
|
|
394
|
+
const body = { find, replace, ...(scope ? { scope } : {}), ...(region ? { region } : {}), ...(attributes === false ? { attributes: false } : {}),
|
|
395
|
+
...(Array.isArray(pages) && pages.length ? { pages } : {}), ...(Number.isInteger(baseRev) ? { baseRev } : {}) };
|
|
396
|
+
return asJsonOr409(await fetchImpl(`${baseUrl(cfg)}/edit-text/${encodeURIComponent(slug)}`, {
|
|
397
|
+
method: "POST", headers: { ...authHeaders(token, cfg), "content-type": "application/json" }, body: JSON.stringify(body) }));
|
|
398
|
+
}
|
|
399
|
+
|
|
389
400
|
/** GET/POST /api/lock/<site> – sanfte Sperre („in Bearbeitung durch …"); blockiert niemanden. action: lock|unlock|takeover. */
|
|
390
401
|
export async function apiLock(cfg, { site, action = null, note = null } = {}, fetchImpl = fetch) {
|
|
391
402
|
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.1" }, { 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.1",
|
|
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,27 @@
|
|
|
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
|
+
"2a. Für Text-, Link- und Kontaktdaten-Änderungen (Telefonnummer, E-Mail, Adresse, Öffnungszeiten) IMMER site_edit_text mit "
|
|
26
|
+
+ "scope:\"all\" (bei Bedarf region:\"footer\"/\"header\") – EIN Aufruf für alle Seiten, inkl. tel:/mailto:-Links. NIE Seite für Seite per "
|
|
27
|
+
+ "site_file_put. Meldet die Antwort company, zusätzlich site_company_set für die Firmendaten (Impressum/Datenschutz/Kontakt).",
|
|
28
|
+
"3. Veröffentlichen (site_publish, publish=true) NUR nach ausdrücklicher Bestätigung des Nutzers im Chat – bis dahin bleibt alles im Entwurf.",
|
|
29
|
+
"4. Konflikt (409, conflict:true): nicht erzwingen. Erst site_status aufrufen (zeigt, wer was geändert hat), Inhalt neu holen, Änderung "
|
|
30
|
+
+ "neu anwenden und mit der neuen draftRev erneut senden.",
|
|
31
|
+
"5. Jede Änderung lässt sich rückgängig machen; Rechtstexte (Impressum/Datenschutz) nicht umformulieren – sie kommen aus den Firmendaten.",
|
|
32
|
+
].join("\n");
|
|
13
33
|
|
|
14
34
|
const asText = obj => ({ content: [{ type: "text", text: JSON.stringify(obj, null, 2) }] });
|
|
15
35
|
const asError = e => ({ isError: true, content: [{ type: "text", text: "Fehler: " + (e?.message || String(e)) }] });
|
|
@@ -80,18 +100,40 @@ export function registerTools(server, { z, cfg, mode = "local", remote = null })
|
|
|
80
100
|
}
|
|
81
101
|
server.tool(
|
|
82
102
|
"site_status",
|
|
83
|
-
"Status einer ProntoSite-Website: Plan, Slug, Live-URL,
|
|
84
|
-
+ "
|
|
85
|
-
+ "
|
|
103
|
+
"LESEND – SCHRITT 1 und letzter Schritt jeder Änderung: Status einer ProntoSite-Website: Plan, Slug, Live-URL, Vorschau-Link "
|
|
104
|
+
+ "(previewUrl), letzte Änderungen, offener Veröffentlichungsbedarf (publishNeeded), draftRev und recentEdits (wer hat zuletzt "
|
|
105
|
+
+ "wo geändert). draftRev ist die baseRev für site_edit_text/site_file_put. Ändert nichts.",
|
|
86
106
|
{ site: z.string().optional().describe(SITE_DESC) },
|
|
87
107
|
async ({ site }) => { try { return asText(await apiStatus(cfg, { site })); } catch (e) { return asError(e); } }
|
|
88
108
|
);
|
|
89
109
|
|
|
110
|
+
server.tool(
|
|
111
|
+
"site_edit_text",
|
|
112
|
+
"SCHREIBEND (Entwurf, nicht live) – STANDARDWEG für Textänderungen (Telefonnummer, E-Mail, Öffnungszeiten, Preise, Sätze): sucht "
|
|
113
|
+
+ "`find` im sichtbaren Text UND (attributes, Standard an) in Links/Attributen (href inkl. tel:/mailto: – normalisiert –, alt, title, "
|
|
114
|
+
+ "meta content) ALLER Seiten und ersetzt durch `replace` – EIN Aufruf, EIN Rückgängig-Schritt, Formatierung bleibt. "
|
|
115
|
+
+ "scope:\"all\" = alle Fundstellen auf allen Seiten sofort ersetzen (für Kontaktdaten im Kopf-/Fußbereich: plus region:\"footer\" bzw. "
|
|
116
|
+
+ "\"header\"); ohne scope ändert der Server bei mehr als einer Fundstelle NICHTS und liefert nur die Liste (oder pages:[…]). "
|
|
117
|
+
+ "Antwort: pages = je Seite gefunden/geändert. Rechtstexte und geschützte Bereiche bleiben unberührt (skipped); steht der Wert in "
|
|
118
|
+
+ "den Firmendaten, nennt `company` das Feld → zusätzlich site_company_set. Nie stattdessen Seite für Seite site_file_put. "
|
|
119
|
+
+ "Vorher site_status, danach site_status; live erst nach Bestätigung mit site_publish.",
|
|
120
|
+
{ site: z.string().optional().describe(SITE_DESC),
|
|
121
|
+
find: z.string().describe("Genau der Text, wie er auf der Website steht (z. B. '02242 123456')."),
|
|
122
|
+
replace: z.string().describe("Neuer Text (reiner Text)."),
|
|
123
|
+
scope: z.enum(["all"]).optional().describe("\"all\" = alle Fundstellen auf allen (bzw. den per pages gewählten) Seiten in diesem Aufruf ersetzen. Standard für Kontaktdaten."),
|
|
124
|
+
region: z.enum(["all", "header", "main", "footer"]).default("all").describe("Nur in diesem Bereich suchen: footer (Fußbereich), header (Kopf/Navigation), main (Inhalt) oder all."),
|
|
125
|
+
attributes: z.boolean().default(true).describe("Auch Linkziele (href, tel:/mailto: normalisiert) und Attribute (alt, title, meta content) ersetzen."),
|
|
126
|
+
pages: z.array(z.string()).optional().describe("Optional: Seiten-IDs (aus der Fundstellen-Liste), auf denen ersetzt werden soll. Ohne pages und ohne scope:\"all\" nur bei genau EINER Fundstelle."),
|
|
127
|
+
baseRev: z.number().int().optional().describe("Optional: draftRev aus site_status (Konfliktschutz, 409 bei zwischenzeitlicher Änderung derselben Seite).") },
|
|
128
|
+
async ({ site, find, replace, scope, region, attributes, pages, baseRev }) => { try { return asText(await apiEditText(cfg, { site, find, replace, scope, region, attributes, pages, baseRev })); } catch (e) { return asError(e); } }
|
|
129
|
+
);
|
|
130
|
+
|
|
90
131
|
if (mode === "local") {
|
|
91
132
|
server.tool(
|
|
92
133
|
"site_export",
|
|
93
|
-
"
|
|
94
|
-
+ "
|
|
134
|
+
"AUSNAHME – nur wenn der Nutzer ausdrücklich lokal arbeiten möchte oder für größere Umbauten (sonst site_edit_text/site_file_*): "
|
|
135
|
+
+ "Exportiert eine Website als ZIP in ein lokales Verzeichnis. which: 'published' (Live-Stand, Standard), "
|
|
136
|
+
+ "'draft' (Entwurf – zum Weiterbearbeiten) oder 'original' (flacher Stand ohne ProntoSite-Zutaten). LESEND.",
|
|
95
137
|
{ site: z.string().optional().describe(SITE_DESC),
|
|
96
138
|
which: z.enum(["published", "draft", "original"]).default("published"),
|
|
97
139
|
outDir: z.string().default(".").describe("Zielordner für die ZIP-Datei (lokal).") },
|
|
@@ -101,7 +143,9 @@ export function registerTools(server, { z, cfg, mode = "local", remote = null })
|
|
|
101
143
|
|
|
102
144
|
server.tool(
|
|
103
145
|
"site_deploy",
|
|
104
|
-
"SCHREIBEND:
|
|
146
|
+
"SCHREIBEND – AUSNAHME: nur für Erst-Import, ausdrücklichen Wunsch nach lokaler Arbeit oder größere Umbauten (kleine Änderungen: "
|
|
147
|
+
+ "site_edit_text/site_file_put). Übergibt eine lokale ZIP-Datei an ProntoSite (Import). Bei Updates PFLICHT: baseRev – exportiere "
|
|
148
|
+
+ "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
149
|
+ "DIREKT LIVE geschaltet und der bestehende Live-Stand überschrieben. ERST-IMPORT (neue Website): "
|
|
106
150
|
+ "Einzel-Config site='new'; Multi-Site/Betreuer site='<label>:new' (legt im Konto DES LABELS an). "
|
|
107
151
|
+ "WICHTIG beim ERST-IMPORT: Frag den Nutzer VOR dem Deploy nach der gewünschten Adresse "
|
|
@@ -130,13 +174,14 @@ export function registerTools(server, { z, cfg, mode = "local", remote = null })
|
|
|
130
174
|
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
175
|
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
176
|
zipPath: z.string().describe("Pfad zur ZIP-Datei der Website (lokal)."),
|
|
177
|
+
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
178
|
publish: z.boolean().default(false).describe("true = nach dem Import direkt veröffentlichen (LIVE)."),
|
|
134
179
|
resetOverlay: z.boolean().default(false).describe("true = bisherige Dashboard-Änderungen verwerfen."),
|
|
135
180
|
legal: z.boolean().optional().describe("Rechts-Automatik (Impressum/Datenschutz): true erzwingt, false schaltet ab."),
|
|
136
181
|
company: companyObject.optional().describe("Optional: Firmendaten VOR dem Deploy setzen (ein Schritt) – Impressum/Datenschutz nutzen sie dann direkt. Teil-Update."),
|
|
137
182
|
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); }
|
|
183
|
+
async ({ site, slug, name, customer, zipPath, baseRev, publish, resetOverlay, legal, company, siteOverride }) => {
|
|
184
|
+
try { return asText(await apiDeploy(cfg, { site, slug, name, customer, zipPath, baseRev, publish, resetOverlay, legal, company, siteOverride })); } catch (e) { return asError(e); }
|
|
140
185
|
}
|
|
141
186
|
);
|
|
142
187
|
|
|
@@ -145,9 +190,9 @@ export function registerTools(server, { z, cfg, mode = "local", remote = null })
|
|
|
145
190
|
}
|
|
146
191
|
server.tool(
|
|
147
192
|
"site_publish",
|
|
148
|
-
"SCHREIBEND: Veröffentlicht den aktuellen
|
|
149
|
-
+ "
|
|
150
|
-
+ "willst trotzdem neu ausliefern, setze force:true.
|
|
193
|
+
"SCHREIBEND – LIVE FÜR BESUCHER: Veröffentlicht den aktuellen Entwurf einer Website. NUR nach ausdrücklicher Bestätigung "
|
|
194
|
+
+ "des Nutzers im Chat aufrufen (vorher kurz zusammenfassen, was live geht, und die richtige Website prüfen). Meldet die Antwort "
|
|
195
|
+
+ "nothing:true (keine erkannte Änderung), aber du willst trotzdem neu ausliefern, setze force:true.",
|
|
151
196
|
{ site: z.string().optional().describe(SITE_DESC),
|
|
152
197
|
force: z.boolean().default(false).describe("true = trotzdem veröffentlichen, auch ohne erkannte Änderung (wie der Menüpunkt Erneut veröffentlichen).") },
|
|
153
198
|
async ({ site, force }) => { try { return asText(await apiPublish(cfg, { site, force })); } catch (e) { return asError(e); } }
|
|
@@ -290,7 +335,9 @@ export function registerTools(server, { z, cfg, mode = "local", remote = null })
|
|
|
290
335
|
);
|
|
291
336
|
|
|
292
337
|
// ---- Datei-API: kleine Änderungen am Entwurf direkt auf dem Server (Konzept §7) ----
|
|
293
|
-
const FILE_SMALL = "
|
|
338
|
+
const FILE_SMALL = "STANDARDWEG: Änderungen direkt am Server-Entwurf (für reine Textänderungen am einfachsten site_edit_text); "
|
|
339
|
+
+ "Export + Deploy nur als Ausnahme für größere Umbauten oder auf ausdrücklichen Wunsch. "
|
|
340
|
+
+ "Skriptdateien (JavaScript) ändern nur Betreuer und nur bei JavaScript-Freigabe der Website – Kunden bekommen dort 403. ";
|
|
294
341
|
server.tool(
|
|
295
342
|
"site_files",
|
|
296
343
|
"LESEND: Listet die Dateien des Entwurfs einer Website (Pfad, Größe, Typ, ob bearbeitbar) und die aktuelle draftRev. "
|
|
@@ -308,7 +355,7 @@ export function registerTools(server, { z, cfg, mode = "local", remote = null })
|
|
|
308
355
|
);
|
|
309
356
|
server.tool(
|
|
310
357
|
"site_file_put",
|
|
311
|
-
"SCHREIBEND (Entwurf, nicht live
|
|
358
|
+
"SCHREIBEND (Entwurf, nicht live): Ersetzt den Inhalt einer Text-Datei (HTML einer bestehenden Seite, "
|
|
312
359
|
+ "CSS, SVG, JSON, TXT …). PFLICHT: baseRev = draftRev aus deinem letzten site_file_get/site_status. Antwortet der Server mit "
|
|
313
360
|
+ "conflict:true (409), hat jemand anderes dieselbe Datei inzwischen geändert: NICHT erzwingen – Datei neu holen, Änderung neu "
|
|
314
361
|
+ "anwenden, mit der neuen draftRev erneut senden; die Antwort listet wer/wann/was. Prüfe danach overlay (umgehängte/deaktivierte "
|
|
@@ -324,7 +371,7 @@ export function registerTools(server, { z, cfg, mode = "local", remote = null })
|
|
|
324
371
|
);
|
|
325
372
|
server.tool(
|
|
326
373
|
"site_file_delete",
|
|
327
|
-
"SCHREIBEND (Entwurf
|
|
374
|
+
"SCHREIBEND (Entwurf): Markiert eine Text-Datei im Entwurf als gelöscht (rückgängig machbar; Seiten "
|
|
328
375
|
+ "löschen geht über das Dashboard). baseRev Pflicht, confirmDestructive:true erst NACH Rückfrage beim Nutzer. Prüfe vorher, "
|
|
329
376
|
+ "ob die Datei noch verlinkt ist. " + FILE_SMALL,
|
|
330
377
|
{ site: z.string().optional().describe(SITE_DESC), path: z.string(), baseRev: z.number().int(),
|
|
@@ -333,7 +380,7 @@ export function registerTools(server, { z, cfg, mode = "local", remote = null })
|
|
|
333
380
|
);
|
|
334
381
|
server.tool(
|
|
335
382
|
"site_file_rename",
|
|
336
|
-
"SCHREIBEND (Entwurf
|
|
383
|
+
"SCHREIBEND (Entwurf): Benennt eine Text-Datei (kein HTML) um. Verweise in anderen Dateien werden NICHT "
|
|
337
384
|
+ "angepasst – die musst du selbst per site_file_put nachziehen. baseRev Pflicht. " + FILE_SMALL,
|
|
338
385
|
{ site: z.string().optional().describe(SITE_DESC), from: z.string(), to: z.string(), baseRev: z.number().int() },
|
|
339
386
|
async ({ site, from, to, baseRev }) => { try { return asText(await apiFileRename(cfg, { site, from, to, baseRev })); } catch (e) { return asError(e); } }
|
|
@@ -362,7 +409,8 @@ export function registerTools(server, { z, cfg, mode = "local", remote = null })
|
|
|
362
409
|
function registerRemoteTransferTools(server, { z, cfg, remote, SITE_DESC, asText, asError }) {
|
|
363
410
|
server.tool(
|
|
364
411
|
"site_export",
|
|
365
|
-
"
|
|
412
|
+
"AUSNAHME – nur wenn der Nutzer ausdrücklich lokal arbeiten möchte oder für größere Umbauten (sonst site_edit_text/site_file_*). "
|
|
413
|
+
+ "LESEND: Exportiert eine Website als ZIP und liefert einen DOWNLOAD-LINK (15 Minuten gültig, nur EINMAL abrufbar). "
|
|
366
414
|
+ "which: 'published' (Live-Stand, Standard), 'draft' (Entwurf – für lokales Bearbeiten und späteres deploy_upload_begin) oder "
|
|
367
415
|
+ "'original' (flacher Stand ohne ProntoSite-Zutaten). Den Link mit curl/Browser laden, z. B. curl -o site.zip '<downloadUrl>'.",
|
|
368
416
|
{ site: z.string().optional().describe(SITE_DESC),
|
|
@@ -376,7 +424,8 @@ function registerRemoteTransferTools(server, { z, cfg, remote, SITE_DESC, asText
|
|
|
376
424
|
);
|
|
377
425
|
server.tool(
|
|
378
426
|
"deploy_upload_begin",
|
|
379
|
-
"SCHREIBEND
|
|
427
|
+
"SCHREIBEND – AUSNAHME: nur für Erst-Import, ausdrücklichen Wunsch nach lokaler Arbeit oder größere Umbauten (kleine Änderungen: "
|
|
428
|
+
+ "site_edit_text/site_file_put). Schritt 1 von 2: ZIP-Deploy vorbereiten. Liefert einen UPLOAD-LINK (HTTP PUT, 15 Minuten, einmalig, max. "
|
|
380
429
|
+ "100 MB) für die ZIP-Datei der Website, z. B. curl -T site.zip '<uploadUrl>'. Danach deploy_commit mit der uploadId. "
|
|
381
430
|
+ "Ein Deploy ersetzt das Original der Website (rückholbar über die Re-Import-Zeitleiste); mit publish=true geht der Stand "
|
|
382
431
|
+ "DIREKT LIVE. Ab dem Starter-Tarif. Bitte VOR dem Aufruf bestätigen lassen – und die richtige Website prüfen. "
|
|
@@ -387,9 +436,10 @@ function registerRemoteTransferTools(server, { z, cfg, remote, SITE_DESC, asText
|
|
|
387
436
|
name: z.string().optional().describe("Erst-Import: expliziter Website-Name. Bei Updates ignoriert."),
|
|
388
437
|
publish: z.boolean().default(false).describe("true = nach dem Import direkt veröffentlichen (LIVE)."),
|
|
389
438
|
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
|
-
|
|
439
|
+
legal: z.boolean().optional().describe("Rechts-Automatik (Impressum/Datenschutz): true erzwingt, false schaltet ab."),
|
|
440
|
+
baseRev: z.number().int().optional().describe("Update ohne Export-ZIP: draftRev aus site_status (Pflicht, sonst 428). ZIPs aus site_export tragen ihn automatisch.") },
|
|
441
|
+
async ({ site, slug, name, publish, resetOverlay, legal, baseRev }) => {
|
|
442
|
+
try { return asText(remote.beginUpload({ site, slug, name, publish, resetOverlay, legal, baseRev })); } catch (e) { return asError(e); }
|
|
393
443
|
}
|
|
394
444
|
);
|
|
395
445
|
server.tool(
|
|
@@ -404,7 +454,7 @@ function registerRemoteTransferTools(server, { z, cfg, remote, SITE_DESC, asText
|
|
|
404
454
|
up = remote.takeUpload(uploadId);
|
|
405
455
|
const o = up.options;
|
|
406
456
|
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 }));
|
|
457
|
+
resetOverlay: o.resetOverlay, legal: o.legal, baseRev: o.baseRev }));
|
|
408
458
|
} catch (e) { return asError(e); } finally { if (up) up.done(); }
|
|
409
459
|
}
|
|
410
460
|
);
|