@prontosite/mcp 0.12.0 → 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 CHANGED
@@ -3,6 +3,15 @@
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.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
+
6
15
  ## 0.12.0 – 2026-10-19
7
16
 
8
17
  - **Arbeitsweise im Server verankert:** Der MCP-Server liefert beim Verbinden `instructions` – Standard ist das Arbeiten direkt
package/api.mjs CHANGED
@@ -389,9 +389,10 @@ export async function apiFileRename(cfg, { site, from, to, baseRev } = {}, fetch
389
389
  }
390
390
 
391
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) {
392
+ export async function apiEditText(cfg, { site, find, replace, scope = null, region = null, attributes = null, pages = null, baseRev = null } = {}, fetchImpl = fetch) {
393
393
  const { token, slug } = resolveTarget(cfg, site);
394
- const body = { find, replace, ...(Array.isArray(pages) && pages.length ? { pages } : {}), ...(Number.isInteger(baseRev) ? { baseRev } : {}) };
394
+ const body = { find, replace, ...(scope ? { scope } : {}), ...(region ? { region } : {}), ...(attributes === false ? { attributes: false } : {}),
395
+ ...(Array.isArray(pages) && pages.length ? { pages } : {}), ...(Number.isInteger(baseRev) ? { baseRev } : {}) };
395
396
  return asJsonOr409(await fetchImpl(`${baseUrl(cfg)}/edit-text/${encodeURIComponent(slug)}`, {
396
397
  method: "POST", headers: { ...authHeaders(token, cfg), "content-type": "application/json" }, body: JSON.stringify(body) }));
397
398
  }
package/index.mjs CHANGED
@@ -21,7 +21,7 @@ const cfg = loadConfig();
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
+ const server = new McpServer({ name: "prontosite", version: "0.12.1" }, { instructions: SERVER_INSTRUCTIONS });
25
25
  // Tool-Definitionen: gemeinsame Quelle mit dem Remote-Endpunkt (tools.mjs, Konzept KI-Anbindung §4.2).
26
26
  registerTools(server, { z, cfg, mode: "local" });
27
27
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prontosite/mcp",
3
- "version": "0.12.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
@@ -22,6 +22,9 @@ export const SERVER_INSTRUCTIONS = [
22
22
  + "Danach dem Nutzer sagen, was geändert wurde, und den Vorschau-Link (previewUrl aus site_status) nennen.",
23
23
  "2. AUSNAHME: site_export + Deploy (site_deploy bzw. deploy_upload_begin/deploy_commit) NUR, wenn der Nutzer ausdrücklich lokal arbeiten "
24
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).",
25
28
  "3. Veröffentlichen (site_publish, publish=true) NUR nach ausdrücklicher Bestätigung des Nutzers im Chat – bis dahin bleibt alles im Entwurf.",
26
29
  "4. Konflikt (409, conflict:true): nicht erzwingen. Erst site_status aufrufen (zeigt, wer was geändert hat), Inhalt neu holen, Änderung "
27
30
  + "neu anwenden und mit der neuen draftRev erneut senden.",
@@ -106,19 +109,23 @@ export function registerTools(server, { z, cfg, mode = "local", remote = null })
106
109
 
107
110
  server.tool(
108
111
  "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). "
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. "
115
119
  + "Vorher site_status, danach site_status; live erst nach Bestätigung mit site_publish.",
116
120
  { site: z.string().optional().describe(SITE_DESC),
117
121
  find: z.string().describe("Genau der Text, wie er auf der Website steht (z. B. '02242 123456')."),
118
122
  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."),
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."),
120
127
  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); } }
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); } }
122
129
  );
123
130
 
124
131
  if (mode === "local") {