@prontosite/mcp 0.13.5 → 0.13.6
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 +10 -0
- package/api.mjs +8 -3
- package/index.mjs +1 -1
- package/package.json +1 -1
- package/tools.mjs +24 -5
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,16 @@
|
|
|
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.13.6 – 2026-10-20
|
|
7
|
+
|
|
8
|
+
- Websites beim Namen nennen: `site` darf jetzt auch Firmenname, Domain oder Site-Titel sein (unscharf: Groß/Klein, Umlaute,
|
|
9
|
+
Bindestriche, Teilstring). Eindeutig → ausgeführt, die Antwort nennt den aufgelösten slug (`aufgeloesteWebsite`); mehrdeutig →
|
|
10
|
+
Kandidatenliste, nichts geändert; kein Treffer → die drei ähnlichsten. Nur innerhalb der erreichbaren Websites.
|
|
11
|
+
- `sites_list`: je Website Firmenname (`company`), Site-Titel (`title`), Domains, Status (live/entwurf), Kunde-ID.
|
|
12
|
+
- `site_status`: konkrete Rechte des Aufrufers – `canRead`, `canEditDraft`, `canPublish`, `canDeploy` (+ `canReasons`);
|
|
13
|
+
`apiEnabled` → `restApiEnabled` (betrifft nur die REST-API).
|
|
14
|
+
- Tool-Annotationen (`readOnlyHint`, `destructiveHint`, `openWorldHint`) auf allen Tools; neue Instructions zu Namen und Rechten.
|
|
15
|
+
|
|
6
16
|
## 0.13.5 – 2026-10-20
|
|
7
17
|
|
|
8
18
|
- `site_edit_text`: Telefonnummern werden unabhängig von der Schreibweise gefunden (Leerzeichen, „/", Bindestrich-Varianten,
|
package/api.mjs
CHANGED
|
@@ -124,7 +124,11 @@ export function friendlyApiError(status, serverError = "") {
|
|
|
124
124
|
async function asJson(res) {
|
|
125
125
|
let body = {};
|
|
126
126
|
try { body = await res.json(); } catch { /* kein JSON */ }
|
|
127
|
-
if (!res.ok)
|
|
127
|
+
if (!res.ok) {
|
|
128
|
+
// Website-Auflösung (Firmenname/Domain): Kandidaten bzw. Vorschläge in die Meldung, damit der Assistent nachfragen kann.
|
|
129
|
+
const list = (body.candidates || body.suggestions || []).map(e => `${e.slug || e.site}${e.name ? ` (${e.name})` : ""}`).join(", ");
|
|
130
|
+
throw new Error(friendlyApiError(res.status, body.error) + (list ? ` ${body.candidates ? "Kandidaten" : "Ähnlich"}: ${list}.` : ""));
|
|
131
|
+
}
|
|
128
132
|
return body;
|
|
129
133
|
}
|
|
130
134
|
|
|
@@ -316,10 +320,11 @@ export async function apiLegalOptions(cfg, { site, impressum, datenschutz } = {}
|
|
|
316
320
|
async function apiSitesWithToken(cfg, token, fetchImpl) {
|
|
317
321
|
const body = await asJson(await fetchImpl(`${baseUrl(cfg)}/sites`, { headers: authHeaders(token, cfg) }));
|
|
318
322
|
const sites = (body.sites || []).map(s => ({ label: s.slug || s.site || s.customer, slug: s.slug || null, id: s.site || null,
|
|
319
|
-
name: s.name || null,
|
|
323
|
+
name: s.name || null, company: s.company || null, title: s.title || null, domains: s.domains || [], status: s.status || null,
|
|
324
|
+
plan: s.plan, everPublished: s.everPublished, publishNeeded: s.publishNeeded,
|
|
320
325
|
lastChangeAt: s.lastChangeAt || null, liveUrl: s.liveUrl || null, customer: s.customer,
|
|
321
326
|
...(s.needsFirstImport ? { needsFirstImport: true, hint: s.hint || null } : {}) }));
|
|
322
|
-
return { count: sites.length, manager: !!body.manager, hint: body.hint || null, sites };
|
|
327
|
+
return { count: sites.length, manager: !!body.manager, hint: body.hint || null, ...(body.hinweis ? { hinweis: body.hinweis } : {}), sites };
|
|
323
328
|
}
|
|
324
329
|
// sites-Map abfragen: jeden Eintrag einzeln (jeder Token → seine Site).
|
|
325
330
|
async function sitesFromMap(cfg, fetchImpl) {
|
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.13.
|
|
24
|
+
const server = new McpServer({ name: "prontosite", version: "0.13.6" }, { instructions: instructionsFor("local") });
|
|
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.13.
|
|
3
|
+
"version": "0.13.6",
|
|
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
|
@@ -20,6 +20,10 @@ const instructionLines = mode => [
|
|
|
20
20
|
"1. STANDARD: direkt am Server-Entwurf arbeiten. Ablauf: site_status (Stand, draftRev, wer zuletzt geändert hat) → Änderung mit "
|
|
21
21
|
+ "site_edit_text (Text suchen und ersetzen über alle Seiten) bzw. site_file_get/site_file_put (HTML/CSS) → site_status (Kontrolle). "
|
|
22
22
|
+ "Danach dem Nutzer sagen, was geändert wurde, und den Vorschau-Link (previewUrl aus site_status) nennen.",
|
|
23
|
+
"1a. Website wählen: Wenn der Nutzer eine Website beim Firmennamen nennt, erst sites_list, dann den passenden Slug verwenden; bei "
|
|
24
|
+
+ "Unklarheit nachfragen. (Die Tools lösen Firmenname/Domain/Titel auch selbst auf; mehrdeutig → Kandidatenliste, nichts geändert.)",
|
|
25
|
+
"1b. Rechte aus site_status.can* lesen (canRead, canEditDraft, canPublish, canDeploy, Gründe in canReasons); nicht aus anderen Feldern "
|
|
26
|
+
+ "schließen – restApiEnabled betrifft nur die REST-API, nicht diese Tools.",
|
|
23
27
|
"2. AUSNAHME: site_export + Deploy (site_deploy bzw. deploy_upload_begin/deploy_commit) NUR, wenn der Nutzer ausdrücklich lokal arbeiten "
|
|
24
28
|
+ "möchte oder die Website komplett umgebaut wird.",
|
|
25
29
|
"2a. Für Text-, Link- und Kontaktdaten-Änderungen (Telefonnummer, E-Mail, Adresse, Öffnungszeiten) IMMER site_edit_text mit "
|
|
@@ -46,16 +50,31 @@ const instructionLines = mode => [
|
|
|
46
50
|
export const instructionsFor = mode => instructionLines(mode).join("\n");
|
|
47
51
|
export const SERVER_INSTRUCTIONS = instructionsFor("remote");
|
|
48
52
|
|
|
53
|
+
// Tool-Annotationen (MCP ToolAnnotations): ChatGPT & Co. fragen sonst bei JEDEM Aufruf nach. readOnly = ändert nichts;
|
|
54
|
+
// destructive = live/kundenwirksam, ersetzt Bestand oder nimmt zurück (Bestätigung sinnvoll). Entwurfs-Änderungen sind
|
|
55
|
+
// rückgängig machbar und daher nicht destruktiv. openWorld = wirkt nach außen (öffentliche Website/DNS).
|
|
56
|
+
const READ_ONLY = new Set(["sites_list", "site_status", "site_page_texts", "site_media_list", "site_preview_screenshot", "site_company_get",
|
|
57
|
+
"site_domain_status", "site_embeds", "site_widgets", "site_files", "site_file_get"]);
|
|
58
|
+
const DESTRUCTIVE = new Set(["site_publish", "site_deploy", "deploy_commit", "site_file_delete", "site_media_delete", "site_undo", "site_redo", "site_domain_connect"]);
|
|
59
|
+
const OPEN_WORLD = new Set(["site_publish", "site_deploy", "deploy_commit", "site_domain_connect"]);
|
|
60
|
+
export function toolAnnotations(name, mode = "local") {
|
|
61
|
+
// site_export: remote nur ein Download-Link (lesend), lokal schreibt es eine ZIP auf die Platte.
|
|
62
|
+
const ro = READ_ONLY.has(name) || (name === "site_export" && mode === "remote");
|
|
63
|
+
return { readOnlyHint: ro, destructiveHint: !ro && DESTRUCTIVE.has(name), idempotentHint: ro || name === "site_lock" || name === "site_unlock", openWorldHint: OPEN_WORLD.has(name) };
|
|
64
|
+
}
|
|
65
|
+
|
|
49
66
|
const asText = obj => ({ content: [{ type: "text", text: JSON.stringify(obj, null, 2) }] });
|
|
50
67
|
const asError = e => ({ isError: true, content: [{ type: "text", text: "Fehler: " + (e?.message || String(e)) }] });
|
|
51
68
|
|
|
52
69
|
/** Alle Tools am MCP-Server registrieren. */
|
|
53
|
-
export function registerTools(
|
|
70
|
+
export function registerTools(server0, { z, cfg, mode = "local", remote = null }) {
|
|
71
|
+
// Jedes Tool mit Annotationen registrieren (Signatur des SDK: name, description, schema, annotations, handler).
|
|
72
|
+
const server = { tool: (name, desc, schema, cb) => server0.tool(name, desc, schema, toolAnnotations(name, mode), cb) };
|
|
54
73
|
const SITE_DESC = mode === "remote"
|
|
55
|
-
? "Website
|
|
56
|
-
+ "die gewünschte Website angeben (sites_list zeigt alle)."
|
|
57
|
-
: "Website
|
|
58
|
-
+ "Config (mehrere Sites) hier die gewünschte Website angeben.";
|
|
74
|
+
? "Website: Slug (z. B. 'mas-monti'), Site-ID (s-…), Firmenname, Domain oder Site-Titel (unscharf, z. B. 'Firat-Deveci'). Ohne Angabe "
|
|
75
|
+
+ "die Website des Tokens bzw. der Freigabe. Betreuer: die gewünschte Website angeben (sites_list zeigt alle mit Firmennamen)."
|
|
76
|
+
: "Website: Slug (z. B. 'mas-monti'), Firmenname, Domain oder Site-Titel (unscharf). Ohne Angabe die Standard-Site der Config. "
|
|
77
|
+
+ "Bei Betreuer-Config (mehrere Sites) hier die gewünschte Website angeben.";
|
|
59
78
|
|
|
60
79
|
// Firmendaten-Felder (identisch zum Dashboard-Formular / Sanitizer). Nur gesetzte Felder werden geändert.
|
|
61
80
|
const companyObject = z.object({
|