@prontosite/mcp 0.3.1 → 0.5.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 +32 -0
- package/README.md +4 -1
- package/api.mjs +36 -1
- package/index.mjs +73 -4
- package/package.json +3 -3
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Changelog – @prontosite/mcp
|
|
2
|
+
|
|
3
|
+
Alle nennenswerten Änderungen am MCP-Server für ProntoSite. Der Server ist ein dünner Client um die
|
|
4
|
+
ProntoSite Deploy-API; die eigentliche Logik lebt serverseitig.
|
|
5
|
+
|
|
6
|
+
## 0.5.0 – 2026-09-20
|
|
7
|
+
|
|
8
|
+
- **Neu: `site_legal_options`** – Anzeige-Optionen der Rechtstext-Widgets je Rolle (impressum/datenschutz):
|
|
9
|
+
`showTitle` (Überschrift), `showDate` (Stand-Datum), `draftNotice` (Entwurfs-Hinweis in der Live-Ausgabe).
|
|
10
|
+
Ohne `impressum`/`datenschutz` nur lesen; sonst Teil-Update. `draftNotice:false` blendet den Entwurfs-Hinweis
|
|
11
|
+
live aus (protokolliert) – Bestätigung empfohlen. Serverseitig: `GET/PUT /api/legal-options/<site>`.
|
|
12
|
+
|
|
13
|
+
## 0.4.0 – 2026-09-20
|
|
14
|
+
|
|
15
|
+
- **Neu: `site_company_get`** – Firmendaten (Impressum/Footer) einer Website lesen (effektiver Stand +
|
|
16
|
+
konto-weite Angaben + ob eine Website-Abweichung gesetzt ist). Lesend.
|
|
17
|
+
- **Neu: `site_company_set`** – Firmendaten des Kontos setzen/ergänzen (Name, Rechtsform, Inhaber, Anschrift,
|
|
18
|
+
Kontakt, USt-IdNr., Register, Kammer/Beruf, Aufsicht, § 18 MStV, Social). **Teil-Update** (nur übergebene
|
|
19
|
+
Felder ändern sich); der Assistent kann die Angaben aus Impressum-Text/Visitenkarte/Freitext extrahieren.
|
|
20
|
+
Default konto-weit, `siteOverride:true` für website-spezifische Angaben. Schreibend (Bestätigung empfohlen).
|
|
21
|
+
- **`site_deploy`** akzeptiert jetzt optional `company:{…}` (+ `siteOverride`) – Firmendaten setzen **und**
|
|
22
|
+
deployen in einem Aufruf; die Rechtstext-Automatik nutzt die Daten dann direkt beim Import.
|
|
23
|
+
- Diese Firmendaten füllen beim (Erst-)Deploy automatisch Impressum & Datenschutz; die Deploy-Antwort meldet
|
|
24
|
+
über `legal.missingCompanyFields`, welche Pflichtangaben noch fehlen.
|
|
25
|
+
- Doku/Homepage auf `https://prontosite.io/docs/mcp` umgestellt.
|
|
26
|
+
|
|
27
|
+
Benötigt die serverseitige Company-API (`/api/company/<site>`, seit 2026-09-20 live).
|
|
28
|
+
|
|
29
|
+
## 0.3.x
|
|
30
|
+
|
|
31
|
+
- Tools `sites_list`, `site_status`, `site_export`, `site_deploy` (inkl. `legal`-Schalter), `site_publish`.
|
|
32
|
+
- Erst-Import (`site:"new"` bzw. `<label>:new`), Betreuer-/Multi-Site-Config, Wunsch-Slug beim Erst-Import.
|
package/README.md
CHANGED
|
@@ -84,8 +84,11 @@ sichern: `cp ~/.prontosite.json ~/.prontosite.json.bak`).
|
|
|
84
84
|
| `sites_list` | Alle erreichbaren Websites (Slug, Plan, letzte Änderung, Publish-Bedarf). **Lesend.** |
|
|
85
85
|
| `site_status({ site? })` | Plan, Slug, Live-URL, Publish-Bedarf. **Lesend.** |
|
|
86
86
|
| `site_export({ site?, which, outDir })` | ZIP (`published`\|`draft`\|`original`) lokal speichern. **Lesend.** |
|
|
87
|
-
| `site_deploy({ site?, zipPath, publish?, resetOverlay?, legal? })` | ZIP importieren, optional live. **Schreibend.** |
|
|
87
|
+
| `site_deploy({ site?, zipPath, publish?, resetOverlay?, legal?, company?, siteOverride? })` | ZIP importieren, optional live; optional Firmendaten im selben Aufruf setzen. **Schreibend.** |
|
|
88
88
|
| `site_publish({ site? })` | Aktuellen Stand erneut veröffentlichen. **Schreibend.** |
|
|
89
|
+
| `site_company_get({ site? })` | Firmendaten (Impressum/Footer) lesen. **Lesend.** |
|
|
90
|
+
| `site_company_set({ site?, company, siteOverride? })` | Firmendaten setzen/ergänzen (Teil-Update); füllen Impressum/Datenschutz beim Deploy. **Schreibend.** |
|
|
91
|
+
| `site_legal_options({ site?, impressum?, datenschutz? })` | Anzeige-Optionen der Rechtstexte (showTitle, showDate, draftNotice); ohne Rolle nur lesen. **Schreibend.** |
|
|
89
92
|
|
|
90
93
|
`site_deploy` und `site_publish` sind **schreibende** Aktionen (verändern die Live-Website). Die
|
|
91
94
|
Tool-Beschreibungen weisen den Assistenten an, vorher rückzufragen. Jeder Aufruf wird serverseitig auditiert.
|
package/api.mjs
CHANGED
|
@@ -117,8 +117,10 @@ export async function apiExport(cfg, { site, which = "published", outDir = "." }
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
/** POST /api/deploy/<site> – ZIP übergeben (+ optional publish/resetOverlay/legal). site="new" = anlegen. */
|
|
120
|
-
export async function apiDeploy(cfg, { site, customer = null, slug: wishSlug = null, zipPath, publish = false, resetOverlay = false, legal = null } = {}, fetchImpl = fetch) {
|
|
120
|
+
export async function apiDeploy(cfg, { site, customer = null, slug: wishSlug = null, zipPath, publish = false, resetOverlay = false, legal = null, company = null, siteOverride = false } = {}, fetchImpl = fetch) {
|
|
121
121
|
if (!zipPath || !fs.existsSync(zipPath)) throw new Error(`ZIP nicht gefunden: ${zipPath}`);
|
|
122
|
+
// Optional: Firmendaten VOR dem Deploy setzen (ein Schritt) → Rechtstext-Automatik nutzt sie direkt.
|
|
123
|
+
if (company && typeof company === "object") await apiCompanySet(cfg, { site, company, siteOverride }, fetchImpl);
|
|
122
124
|
const { token, slug } = resolveTarget(cfg, site);
|
|
123
125
|
const q = new URLSearchParams();
|
|
124
126
|
if (publish) q.set("publish", "1");
|
|
@@ -142,6 +144,39 @@ export async function apiPublish(cfg, { site, force = false } = {}, fetchImpl =
|
|
|
142
144
|
return asJson(res);
|
|
143
145
|
}
|
|
144
146
|
|
|
147
|
+
/** GET /api/company/<site> – konto-weite Firmendaten (effektiv + account_company + siteOverride-Flag). */
|
|
148
|
+
export async function apiCompanyGet(cfg, { site } = {}, fetchImpl = fetch) {
|
|
149
|
+
const { token, slug } = resolveTarget(cfg, site);
|
|
150
|
+
const res = await fetchImpl(`${baseUrl(cfg)}/company/${encodeURIComponent(slug)}`, { headers: authHeaders(token) });
|
|
151
|
+
return asJson(res);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** PUT /api/company/<site> – Firmendaten TEIL-aktualisieren (nur gesetzte Felder). Default konto-weit;
|
|
155
|
+
* siteOverride=true schreibt die abweichenden Firmendaten DIESER Website. */
|
|
156
|
+
export async function apiCompanySet(cfg, { site, company, siteOverride = false } = {}, fetchImpl = fetch) {
|
|
157
|
+
if (!company || typeof company !== "object") throw new Error("company-Objekt erwartet.");
|
|
158
|
+
const { token, slug } = resolveTarget(cfg, site);
|
|
159
|
+
const res = await fetchImpl(`${baseUrl(cfg)}/company/${encodeURIComponent(slug)}`, {
|
|
160
|
+
method: "PUT", headers: { ...authHeaders(token), "content-type": "application/json" },
|
|
161
|
+
body: JSON.stringify({ company, siteOverride: !!siteOverride }) });
|
|
162
|
+
return asJson(res);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** GET/PUT /api/legal-options/<site> – Anzeige-Optionen der Rechtstext-Widgets. Ohne impressum/datenschutz
|
|
166
|
+
* nur LESEN; sonst Teil-Update (nur echte Booleans wirken). */
|
|
167
|
+
export async function apiLegalOptions(cfg, { site, impressum, datenschutz } = {}, fetchImpl = fetch) {
|
|
168
|
+
const { token, slug } = resolveTarget(cfg, site);
|
|
169
|
+
const endpoint = `${baseUrl(cfg)}/legal-options/${encodeURIComponent(slug)}`;
|
|
170
|
+
if (impressum === undefined && datenschutz === undefined) {
|
|
171
|
+
return asJson(await fetchImpl(endpoint, { headers: authHeaders(token) }));
|
|
172
|
+
}
|
|
173
|
+
const body = {};
|
|
174
|
+
if (impressum !== undefined) body.impressum = impressum;
|
|
175
|
+
if (datenschutz !== undefined) body.datenschutz = datenschutz;
|
|
176
|
+
const res = await fetchImpl(endpoint, { method: "PUT", headers: { ...authHeaders(token), "content-type": "application/json" }, body: JSON.stringify(body) });
|
|
177
|
+
return asJson(res);
|
|
178
|
+
}
|
|
179
|
+
|
|
145
180
|
/** Alle erreichbaren Sites auflisten (Slug, Plan, Publish-Bedarf, letzte Änderung). Bei einer sites-Map wird
|
|
146
181
|
* jeder Eintrag per Status abgefragt; sonst die eine konfigurierte Site. Fehler je Site werden mitgeführt. */
|
|
147
182
|
export async function sitesList(cfg, fetchImpl = fetch) {
|
package/index.mjs
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
12
12
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
13
13
|
import { z } from "zod";
|
|
14
|
-
import { loadConfig, apiStatus, apiExport, apiDeploy, apiPublish, sitesList } from "./api.mjs";
|
|
14
|
+
import { loadConfig, apiStatus, apiExport, apiDeploy, apiPublish, sitesList, apiCompanyGet, apiCompanySet, apiLegalOptions } from "./api.mjs";
|
|
15
15
|
|
|
16
16
|
const cfg = loadConfig();
|
|
17
17
|
const server = new McpServer({ name: "prontosite", version: "0.3.1" });
|
|
@@ -20,6 +20,29 @@ const asError = e => ({ isError: true, content: [{ type: "text", text: "Fehler:
|
|
|
20
20
|
const SITE_DESC = "Website-Slug (z. B. 'mas-monti'). Ohne Angabe die Standard-Site der Config. Bei Betreuer-"
|
|
21
21
|
+ "Config (mehrere Sites) hier die gewünschte Website angeben.";
|
|
22
22
|
|
|
23
|
+
// Firmendaten-Felder (identisch zum Dashboard-Formular / Sanitizer). Nur gesetzte Felder werden geändert.
|
|
24
|
+
const companyObject = z.object({
|
|
25
|
+
name: z.string().optional().describe("Firmenname"),
|
|
26
|
+
legalForm: z.string().optional().describe("Rechtsform (z. B. GmbH, UG, Einzelunternehmen, Freiberufler)"),
|
|
27
|
+
representative: z.string().optional().describe("Inhaber / Vertretungsberechtigte:r"),
|
|
28
|
+
street: z.string().optional().describe("Straße und Hausnummer"),
|
|
29
|
+
zip: z.string().optional().describe("PLZ"),
|
|
30
|
+
city: z.string().optional().describe("Ort"),
|
|
31
|
+
country: z.string().optional().describe("Land"),
|
|
32
|
+
email: z.string().optional().describe("E-Mail-Adresse (Impressum)"),
|
|
33
|
+
phone: z.string().optional().describe("Telefon"),
|
|
34
|
+
fax: z.string().optional().describe("Fax"),
|
|
35
|
+
vatId: z.string().optional().describe("USt-IdNr."),
|
|
36
|
+
registerCourt: z.string().optional().describe("Registergericht"),
|
|
37
|
+
registerNumber: z.string().optional().describe("Handelsregister-Nr."),
|
|
38
|
+
kammer: z.string().optional().describe("Zuständige Kammer (reglementierte Berufe)"),
|
|
39
|
+
berufsbezeichnung: z.string().optional().describe("Berufsbezeichnung (reglementierte Berufe)"),
|
|
40
|
+
aufsichtsbehoerde: z.string().optional().describe("Aufsichtsbehörde (erlaubnispflichtige Gewerbe)"),
|
|
41
|
+
verantwortlichMStV: z.string().optional().describe("Verantwortlich i.S.d. § 18 Abs. 2 MStV (falls abweichend)"),
|
|
42
|
+
berufshaftpflicht: z.string().optional().describe("Berufshaftpflichtversicherung (Name, Ort, Geltungsraum)"),
|
|
43
|
+
social: z.array(z.object({ label: z.string(), href: z.string() })).optional().describe("Social-Links [{label,href}] (href muss http/https sein)"),
|
|
44
|
+
}).describe("Firmendaten fürs Impressum/Footer – Teil-Update: nur gesetzte Felder ändern sich.");
|
|
45
|
+
|
|
23
46
|
server.tool(
|
|
24
47
|
"sites_list",
|
|
25
48
|
"Listet alle erreichbaren ProntoSite-Websites auf (Slug, Plan, letzte Änderung, offener Veröffentlichungs"
|
|
@@ -61,9 +84,11 @@ server.tool(
|
|
|
61
84
|
zipPath: z.string().describe("Pfad zur ZIP-Datei der Website (lokal)."),
|
|
62
85
|
publish: z.boolean().default(false).describe("true = nach dem Import direkt veröffentlichen (LIVE)."),
|
|
63
86
|
resetOverlay: z.boolean().default(false).describe("true = bisherige Dashboard-Änderungen verwerfen."),
|
|
64
|
-
legal: z.boolean().optional().describe("Rechts-Automatik (Impressum/Datenschutz): true erzwingt, false schaltet ab.")
|
|
65
|
-
|
|
66
|
-
|
|
87
|
+
legal: z.boolean().optional().describe("Rechts-Automatik (Impressum/Datenschutz): true erzwingt, false schaltet ab."),
|
|
88
|
+
company: companyObject.optional().describe("Optional: Firmendaten VOR dem Deploy setzen (ein Schritt) – Impressum/Datenschutz nutzen sie dann direkt. Teil-Update."),
|
|
89
|
+
siteOverride: z.boolean().default(false).describe("Nur mit company: true = Firmendaten NUR für diese Website (statt konto-weit).") },
|
|
90
|
+
async ({ site, slug, customer, zipPath, publish, resetOverlay, legal, company, siteOverride }) => {
|
|
91
|
+
try { return asText(await apiDeploy(cfg, { site, slug, customer, zipPath, publish, resetOverlay, legal, company, siteOverride })); } catch (e) { return asError(e); }
|
|
67
92
|
}
|
|
68
93
|
);
|
|
69
94
|
|
|
@@ -77,5 +102,49 @@ server.tool(
|
|
|
77
102
|
async ({ site, force }) => { try { return asText(await apiPublish(cfg, { site, force })); } catch (e) { return asError(e); } }
|
|
78
103
|
);
|
|
79
104
|
|
|
105
|
+
server.tool(
|
|
106
|
+
"site_company_get",
|
|
107
|
+
"Firmendaten (Impressum/Footer) einer Website lesen: effektiver Stand + konto-weite Angaben + ob eine "
|
|
108
|
+
+ "Website-Abweichung gesetzt ist. LESEND – ändert nichts.",
|
|
109
|
+
{ site: z.string().optional().describe(SITE_DESC) },
|
|
110
|
+
async ({ site }) => { try { return asText(await apiCompanyGet(cfg, { site })); } catch (e) { return asError(e); } }
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
server.tool(
|
|
114
|
+
"site_company_set",
|
|
115
|
+
"SCHREIBEND: Firmendaten des Kontos setzen/ergänzen (Name, Rechtsform, Inhaber, Anschrift, Kontakt, "
|
|
116
|
+
+ "USt-IdNr., Register, Kammer/Beruf, Aufsicht, § 18 MStV, Social). Diese Daten füllen beim nächsten Deploy "
|
|
117
|
+
+ "automatisch Impressum & Datenschutz. TEIL-UPDATE: nur die übergebenen Felder ändern sich. Du darfst die "
|
|
118
|
+
+ "Angaben aus jeder Vorlage extrahieren (Impressum-Text, Visitenkarte, Freitext) und strukturiert übergeben. "
|
|
119
|
+
+ "Default konto-weit; siteOverride=true nur, wenn DIESE eine Website abweichende Angaben braucht. Bitte VOR "
|
|
120
|
+
+ "dem Speichern die erkannten Felder kurz zusammenfassen und bestätigen lassen.",
|
|
121
|
+
{ site: z.string().optional().describe(SITE_DESC),
|
|
122
|
+
company: companyObject,
|
|
123
|
+
siteOverride: z.boolean().default(false).describe("true = Firmendaten NUR für diese Website (statt konto-weit).") },
|
|
124
|
+
async ({ site, company, siteOverride }) => {
|
|
125
|
+
try { return asText(await apiCompanySet(cfg, { site, company, siteOverride })); } catch (e) { return asError(e); }
|
|
126
|
+
}
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
const legalOptShape = z.object({
|
|
130
|
+
showTitle: z.boolean().optional().describe("Überschrift (H1) des Rechtstextes anzeigen (Default an)."),
|
|
131
|
+
showDate: z.boolean().optional().describe("Stand-Datum anzeigen (Default an; nur wo vorhanden, i. d. R. Datenschutz)."),
|
|
132
|
+
draftNotice: z.boolean().optional().describe("Entwurfs-Hinweis in der VERÖFFENTLICHTEN Seite anzeigen (Default an)."),
|
|
133
|
+
}).describe("Anzeige-Optionen einer Rechtstext-Rolle – Teil-Update, nur gesetzte Booleans wirken.");
|
|
134
|
+
|
|
135
|
+
server.tool(
|
|
136
|
+
"site_legal_options",
|
|
137
|
+
"LESEND/SCHREIBEND: Anzeige-Optionen der Rechtstext-Widgets je Rolle (impressum/datenschutz): showTitle "
|
|
138
|
+
+ "(Überschrift), showDate (Stand-Datum), draftNotice (Entwurfs-Hinweis in der Live-Ausgabe). OHNE impressum/"
|
|
139
|
+
+ "datenschutz nur LESEN. Teil-Update (nur gesetzte Felder). HINWEIS: draftNotice:false blendet den rechtlich "
|
|
140
|
+
+ "empfohlenen Entwurfs-Hinweis live aus (protokolliert) – bitte vorher bestätigen lassen.",
|
|
141
|
+
{ site: z.string().optional().describe(SITE_DESC),
|
|
142
|
+
impressum: legalOptShape.optional(),
|
|
143
|
+
datenschutz: legalOptShape.optional() },
|
|
144
|
+
async ({ site, impressum, datenschutz }) => {
|
|
145
|
+
try { return asText(await apiLegalOptions(cfg, { site, impressum, datenschutz })); } catch (e) { return asError(e); }
|
|
146
|
+
}
|
|
147
|
+
);
|
|
148
|
+
|
|
80
149
|
const transport = new StdioServerTransport();
|
|
81
150
|
await server.connect(transport);
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prontosite/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.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": { "prontosite-mcp": "index.mjs" },
|
|
7
7
|
"main": "index.mjs",
|
|
8
|
-
"files": ["index.mjs", "api.mjs", "README.md"],
|
|
8
|
+
"files": ["index.mjs", "api.mjs", "README.md", "CHANGELOG.md"],
|
|
9
9
|
"engines": { "node": ">=18" },
|
|
10
10
|
"keywords": ["prontosite", "mcp", "model-context-protocol", "claude", "deploy", "website"],
|
|
11
11
|
"license": "UNLICENSED",
|
|
12
|
-
"homepage": "https://prontosite.io/mcp
|
|
12
|
+
"homepage": "https://prontosite.io/docs/mcp",
|
|
13
13
|
"publishConfig": { "access": "public" },
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"@modelcontextprotocol/sdk": "^1.0.0",
|