@bettercms-ai/mcp 0.9.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/README.md +85 -0
- package/SKILL.md +65 -0
- package/dist/index.d.ts +132 -0
- package/dist/index.js +1266 -0
- package/dist/index.js.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/token-store.ts","../src/device-auth.ts","../src/server.ts","../src/tools.ts","../src/prompts.ts"],"sourcesContent":["import { realpathSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { loadConfig } from \"./config.js\";\nimport { FileTokenStore } from \"./token-store.js\";\nimport { DeviceAuthClient } from \"./device-auth.js\";\nimport { buildServer } from \"./server.js\";\n\nexport { buildServer } from \"./server.js\";\nexport { DeviceAuthClient } from \"./device-auth.js\";\nexport { loadConfig } from \"./config.js\";\n\n/** Entrypoint for the `bettercms-mcp` stdio server. */\nasync function main(): Promise<void> {\n const config = loadConfig();\n const store = new FileTokenStore(config.credentialsPath, config.apiUrl);\n const auth = new DeviceAuthClient(config, store);\n const server = buildServer({ auth, managementBaseUrl: config.managementBaseUrl });\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n process.stderr.write(`[bettercms-mcp] ready on stdio (api: ${config.apiUrl})\\n`);\n}\n\n// Only run when executed as the entry (not when imported, e.g. in tests).\n// Compare realpaths so symlinked launch paths still detect the entrypoint:\n// `import.meta.url` is realpath-resolved by Node, but `process.argv[1]` is not,\n// so the old `file://${argv[1]}` check silently failed under npx `.bin`\n// symlinks, global installs, and macOS /var→/private/var — the server would\n// exit 0 without starting.\nfunction isMainModule(): boolean {\n const argv1 = process.argv[1];\n if (!argv1) return false;\n try {\n return realpathSync(argv1) === fileURLToPath(import.meta.url);\n } catch {\n return false;\n }\n}\n\nif (isMainModule()) {\n main().catch((err) => {\n process.stderr.write(`[bettercms-mcp] fatal: ${err instanceof Error ? err.message : String(err)}\\n`);\n process.exit(1);\n });\n}\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\n/**\n * Resolved configuration for the BetterCMS MCP server.\n *\n * A single `BETTERCMS_API_URL` (origin, no path) drives both the device-auth\n * endpoints and the Management API base the SDK targets:\n * device: {apiUrl}/api/v1/auth/device/*\n * management: {apiUrl}/api/v1 (SDK appends /management/content/*)\n */\nexport interface McpConfig {\n apiUrl: string;\n deviceBaseUrl: string;\n managementBaseUrl: string;\n credentialsPath: string;\n clientName: string;\n}\n\nconst DEFAULT_API_URL = \"https://api.bettercms.ai\";\n\nexport function loadConfig(env: NodeJS.ProcessEnv = process.env): McpConfig {\n const apiUrl = (env.BETTERCMS_API_URL?.trim() || DEFAULT_API_URL).replace(/\\/+$/, \"\");\n return {\n apiUrl,\n deviceBaseUrl: `${apiUrl}/api/v1/auth/device`,\n managementBaseUrl: `${apiUrl}/api/v1`,\n credentialsPath:\n env.BETTERCMS_MCP_CREDENTIALS?.trim() ||\n join(homedir(), \".bettercms\", \"mcp-credentials.json\"),\n clientName: env.BETTERCMS_MCP_CLIENT_NAME?.trim() || \"BetterCMS MCP\",\n };\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\n/** Credentials cached between runs so the device flow runs only once per env. */\nexport interface StoredCredentials {\n accessToken: string;\n refreshToken: string;\n /** Epoch ms when the access token expires. */\n accessTokenExpiresAt: number;\n workspaceId: string | null;\n projectId: string | null;\n}\n\n/**\n * An authorization the user has been sent off to approve but hasn't yet.\n * Persisted so a later tool call can *resume* polling that same code instead of\n * minting a fresh one — this is what lets the flow survive across the\n * \"return the link → user approves → retry\" round-trip in clients (VS Code)\n * that never surface the server's stderr prompt.\n */\nexport interface PendingDevice {\n deviceCode: string;\n userCode: string;\n verificationUri: string;\n /** verification_uri with `?code=` prefilled — the link we hand the user. */\n verificationUriComplete: string;\n intervalSeconds: number;\n /** Epoch ms when the device code expires. */\n expiresAt: number;\n}\n\n/** Persistence boundary for credentials (file-backed in prod, in-memory in tests). */\nexport interface TokenStore {\n read(): Promise<StoredCredentials | null>;\n write(creds: StoredCredentials): Promise<void>;\n clear(): Promise<void>;\n /** In-progress device authorization awaiting approval, if any. */\n readPending(): Promise<PendingDevice | null>;\n writePending(pending: PendingDevice): Promise<void>;\n clearPending(): Promise<void>;\n}\n\n/**\n * File-backed token store. Credentials are namespaced by `key` (the API origin)\n * so pointing the server at a different environment doesn't reuse a stale token.\n * The file is written 0600 (owner-only) since it holds bearer credentials.\n */\nexport class FileTokenStore implements TokenStore {\n /** Pending authorizations live under a sibling key so they never shadow creds. */\n private readonly pendingKey: string;\n\n constructor(\n private readonly path: string,\n private readonly key: string,\n ) {\n this.pendingKey = `${key}::pending`;\n }\n\n private async readAll(): Promise<Record<string, unknown>> {\n try {\n const raw = await readFile(this.path, \"utf-8\");\n const parsed = JSON.parse(raw) as Record<string, unknown>;\n return parsed && typeof parsed === \"object\" ? parsed : {};\n } catch {\n return {};\n }\n }\n\n private async writeAll(all: Record<string, unknown>): Promise<void> {\n await mkdir(dirname(this.path), { recursive: true });\n await writeFile(this.path, JSON.stringify(all, null, 2), { mode: 0o600 });\n }\n\n async read(): Promise<StoredCredentials | null> {\n const all = await this.readAll();\n return (all[this.key] as StoredCredentials | undefined) ?? null;\n }\n\n async write(creds: StoredCredentials): Promise<void> {\n const all = await this.readAll();\n all[this.key] = creds;\n await this.writeAll(all);\n }\n\n async clear(): Promise<void> {\n const all = await this.readAll();\n delete all[this.key];\n await this.writeAll(all);\n }\n\n async readPending(): Promise<PendingDevice | null> {\n const all = await this.readAll();\n return (all[this.pendingKey] as PendingDevice | undefined) ?? null;\n }\n\n async writePending(pending: PendingDevice): Promise<void> {\n const all = await this.readAll();\n all[this.pendingKey] = pending;\n await this.writeAll(all);\n }\n\n async clearPending(): Promise<void> {\n const all = await this.readAll();\n delete all[this.pendingKey];\n await this.writeAll(all);\n }\n}\n","import type { McpConfig } from \"./config.js\";\nimport type { PendingDevice, StoredCredentials, TokenStore } from \"./token-store.js\";\n\n/** Skew applied when deciding if a cached access token is still usable. */\nconst EXPIRY_SKEW_MS = 60_000;\n\n/**\n * How long a single tool call waits inline for the user to approve before it\n * gives up and hands the activation link back to the caller. Fast approvals\n * complete in the *same* call; slow ones resume on the next tool call.\n */\nconst GRACE_POLL_MS = 25_000;\n\ninterface TokenSuccess {\n access_token: string;\n token_type: string;\n expires_in: number;\n refresh_token: string;\n scope: string;\n workspace_id: string | null;\n project_id: string | null;\n}\n\ninterface DeviceCodeResponse {\n device_code: string;\n user_code: string;\n verification_uri: string;\n verification_uri_complete?: string;\n expires_in: number;\n interval: number;\n}\n\n/** Injectable seams so tests can run without real timers / network / stderr. */\nexport interface DeviceAuthDeps {\n fetch?: typeof fetch;\n sleep?: (ms: number) => Promise<void>;\n log?: (message: string) => void;\n now?: () => number;\n}\n\n/** Thrown when the device flow cannot complete (denied / expired / unexpected). */\nexport class DeviceAuthError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"DeviceAuthError\";\n }\n}\n\n/**\n * Thrown when authorization is *legitimately still pending* after the inline\n * grace window. Carries the activation link so the caller (a tool handler) can\n * surface it in the visible tool result — the device flow is persisted, so the\n * next tool call resumes it and completes the user's original request.\n */\nexport class DeviceAuthPendingError extends Error {\n readonly verificationUri: string;\n readonly verificationUriComplete: string;\n readonly userCode: string;\n readonly expiresAt: number;\n\n constructor(pending: PendingDevice) {\n super(\"Authorization pending — approve in the browser, then retry.\");\n this.name = \"DeviceAuthPendingError\";\n this.verificationUri = pending.verificationUri;\n this.verificationUriComplete = pending.verificationUriComplete;\n this.userCode = pending.userCode;\n this.expiresAt = pending.expiresAt;\n }\n}\n\n/**\n * Drives the OAuth 2.0 Device Authorization Grant (RFC 8628) against the\n * BetterCMS backend and hands the SDK a valid `content:manage` access token.\n *\n * - `getAccessToken()` returns a usable token: cached if fresh, refreshed if\n * expired, or freshly minted via the full device flow if there's nothing valid.\n * - All human-facing output goes to stderr — stdout is the MCP JSON-RPC channel.\n */\nexport class DeviceAuthClient {\n private readonly fetchImpl: typeof fetch;\n private readonly sleep: (ms: number) => Promise<void>;\n private readonly log: (message: string) => void;\n private readonly now: () => number;\n private inFlight: Promise<string> | null = null;\n private refreshInFlight: Promise<string | null> | null = null;\n\n constructor(\n private readonly config: McpConfig,\n private readonly store: TokenStore,\n deps: DeviceAuthDeps = {},\n ) {\n this.fetchImpl = deps.fetch ?? globalThis.fetch;\n this.sleep = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));\n this.log = deps.log ?? ((m) => process.stderr.write(`${m}\\n`));\n this.now = deps.now ?? (() => Date.now());\n }\n\n /** Return a valid access token, doing the least work necessary. Single-flighted. */\n async getAccessToken(): Promise<string> {\n if (this.inFlight) return this.inFlight;\n this.inFlight = this.resolveToken().finally(() => {\n this.inFlight = null;\n });\n return this.inFlight;\n }\n\n private async resolveToken(): Promise<string> {\n const creds = await this.store.read();\n if (creds && creds.accessTokenExpiresAt - this.now() > EXPIRY_SKEW_MS) {\n return creds.accessToken;\n }\n if (creds?.refreshToken) {\n const refreshed = await this.refresh();\n if (refreshed) return refreshed;\n }\n return this.runDeviceFlow();\n }\n\n /**\n * Resume a still-live authorization if one is persisted, otherwise start a\n * fresh one; then grace-poll. Throws {@link DeviceAuthPendingError} (carrying\n * the activation link) if the user hasn't approved within the grace window.\n */\n private async runDeviceFlow(): Promise<string> {\n let pending = await this.store.readPending();\n if (pending && pending.expiresAt - this.now() <= EXPIRY_SKEW_MS) {\n await this.store.clearPending(); // stale — don't resume an expired code\n pending = null;\n }\n if (!pending) {\n pending = await this.startDeviceFlow();\n }\n\n const graceDeadline = Math.min(this.now() + GRACE_POLL_MS, pending.expiresAt);\n const token = await this.pollForApproval(pending, graceDeadline);\n if (token) return token;\n\n // Still pending after the grace window — hand the link to the caller so it\n // lands in the visible tool result. The next tool call resumes this code.\n throw new DeviceAuthPendingError(pending);\n }\n\n /** Request a fresh device code, persist it as pending, and log a breadcrumb. */\n private async startDeviceFlow(): Promise<PendingDevice> {\n const start = await this.fetchImpl(`${this.config.deviceBaseUrl}/code`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ client_name: this.config.clientName }),\n });\n if (!start.ok) {\n throw new DeviceAuthError(\n `Failed to start device authorization (HTTP ${start.status}).`,\n );\n }\n const code = (await start.json()) as DeviceCodeResponse;\n const pending: PendingDevice = {\n deviceCode: code.device_code,\n userCode: code.user_code,\n verificationUri: code.verification_uri,\n verificationUriComplete:\n code.verification_uri_complete ??\n `${code.verification_uri}?code=${encodeURIComponent(code.user_code)}`,\n intervalSeconds: code.interval,\n expiresAt: this.now() + code.expires_in * 1000,\n };\n await this.store.writePending(pending);\n\n // Breadcrumb for terminal/non-VS-Code clients that DO surface stderr.\n this.log(\"\");\n this.log(\"┌─ BetterCMS authorization required ─────────────────────────\");\n this.log(`│ Visit: ${pending.verificationUri}`);\n this.log(`│ Enter code: ${pending.userCode}`);\n this.log(`│ Or open: ${pending.verificationUriComplete}`);\n this.log(\"└────────────────────────────────────────────────────────────\");\n return pending;\n }\n\n /**\n * Poll the token endpoint until `deadline`. Returns the access token on\n * approval, or null if the deadline passes while still pending. Throws\n * {@link DeviceAuthError} on a terminal outcome (denied / expired).\n */\n private async pollForApproval(\n pending: PendingDevice,\n deadline: number,\n ): Promise<string | null> {\n let intervalMs = pending.intervalSeconds * 1000;\n\n while (this.now() < deadline) {\n await this.sleep(intervalMs);\n if (this.now() >= deadline) break; // don't poll once past the window\n\n const res = await this.fetchImpl(`${this.config.deviceBaseUrl}/token`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n device_code: pending.deviceCode,\n grant_type: \"urn:ietf:params:oauth:grant-type:device_code\",\n }),\n });\n\n if (res.ok) {\n const body = (await res.json()) as TokenSuccess;\n await this.store.clearPending();\n this.log(\"[bettercms-mcp] authorized ✓\");\n return this.persist(body);\n }\n\n const err = (await res.json().catch(() => ({}))) as { error?: string };\n switch (err.error) {\n case \"authorization_pending\":\n continue;\n case \"slow_down\":\n intervalMs += 5_000; // RFC 8628 §3.5\n continue;\n case \"access_denied\":\n await this.store.clearPending();\n throw new DeviceAuthError(\"Authorization was denied.\");\n case \"expired_token\":\n await this.store.clearPending();\n throw new DeviceAuthError(\"The device code expired before approval. Try again.\");\n default:\n throw new DeviceAuthError(\n `Device authorization failed: ${err.error ?? `HTTP ${res.status}`}.`,\n );\n }\n }\n return null; // still pending — caller surfaces the activation link\n }\n\n /**\n * Exchange the stored refresh token for a new access token. Single-flighted:\n * the device `/refresh` endpoint is single-use (it rotates the refresh token\n * and revokes the prior access key), so a burst of concurrent 401s must NOT\n * each fire their own refresh — the first would rotate, and the rest would\n * send the now-stale token, get `invalid_grant`, and wipe the freshly-minted\n * credentials. Collapsing them into one in-flight rotation keeps the session\n * alive without a needless re-auth.\n */\n async refresh(): Promise<string | null> {\n if (this.refreshInFlight) return this.refreshInFlight;\n this.refreshInFlight = this.doRefresh().finally(() => {\n this.refreshInFlight = null;\n });\n return this.refreshInFlight;\n }\n\n /**\n * Forget the cached credentials and start a fresh device flow. Called when the\n * bound project was deleted server-side (a key bound to a dead project can never\n * succeed again) — clearing lets the user re-authorize against a LIVE project.\n * Returns a new token if approval is fast, else throws {@link DeviceAuthPendingError}\n * carrying the activation link (the next tool call resumes into the new project).\n */\n async resetAndReauthorize(): Promise<string> {\n await this.store.clear();\n await this.store.clearPending();\n return this.getAccessToken();\n }\n\n private async doRefresh(): Promise<string | null> {\n const creds = await this.store.read();\n if (!creds?.refreshToken) return null;\n\n let res: Response;\n try {\n res = await this.fetchImpl(`${this.config.deviceBaseUrl}/refresh`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refresh_token: creds.refreshToken }),\n });\n } catch {\n // Network/transport error — transient. Keep the (still-valid, 30-day)\n // refresh token so a later call can retry instead of forcing a re-auth.\n return null;\n }\n\n if (res.ok) {\n const body = (await res.json()) as TokenSuccess;\n return this.persist(body);\n }\n\n // Clear only on a definitive auth rejection: the OAuth `invalid_grant` signal\n // (the backend's \"this refresh token is dead\", sent as 400 invalid_grant) or a\n // hard 401/403. A bare 400 WITHOUT that signal (request-validation error, or a\n // WAF/infra page with an unparseable body → err={}) is treated as transient —\n // keep the refresh token so a later call can retry instead of forcing re-auth.\n const err = (await res.json().catch(() => ({}))) as { error?: string };\n if (err.error === \"invalid_grant\" || res.status === 401 || res.status === 403) {\n await this.store.clear();\n }\n return null;\n }\n\n private async persist(body: TokenSuccess): Promise<string> {\n const creds: StoredCredentials = {\n accessToken: body.access_token,\n refreshToken: body.refresh_token,\n accessTokenExpiresAt: this.now() + body.expires_in * 1000,\n workspaceId: body.workspace_id,\n projectId: body.project_id,\n };\n await this.store.write(creds);\n return creds.accessToken;\n }\n}\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { BetterCMS } from \"@bettercms-ai/sdk\";\nimport { registerTools } from \"./tools.js\";\nimport { registerPrompts } from \"./prompts.js\";\nimport type { DeviceAuthClient } from \"./device-auth.js\";\n\nexport const SERVER_NAME = \"bettercms\";\nexport const SERVER_VERSION = \"0.1.0\";\n\nexport interface BuildServerDeps {\n auth: DeviceAuthClient;\n managementBaseUrl: string;\n}\n\n/**\n * Build the BetterCMS MCP server with its tools registered. Auth is lazy — the\n * device flow runs on the first tool call, not at connect time, so the MCP\n * handshake/tool-listing never blocks on user approval.\n */\nexport function buildServer(deps: BuildServerDeps): McpServer {\n const server = new McpServer(\n { name: SERVER_NAME, version: SERVER_VERSION },\n { capabilities: { tools: {}, prompts: {} } },\n );\n\n registerTools(server, {\n auth: deps.auth,\n createClient: (apiKey) =>\n BetterCMS.management({ apiKey, baseUrl: deps.managementBaseUrl }),\n });\n\n // Guided slash-command prompts ship with the server (auto-available as\n // /mcp__bettercms__studio and /mcp__bettercms__new_page when the MCP is added).\n registerPrompts(server);\n\n return server;\n}\n","import { z } from \"zod\";\nimport { BetterCMSError } from \"@bettercms-ai/sdk\";\nimport { DeviceAuthPendingError } from \"./device-auth.js\";\nimport type {\n ManagedContentModel,\n ManagedContentEntry,\n ManagedPage,\n ManagedForm,\n ManagedFormInput,\n ManagedComponent,\n ManagedComponentInput,\n UpdateModelInput,\n CreateEntryInput,\n UpdateEntryInput,\n CreateManagedPageInput,\n UploadAssetInput,\n UploadedAsset,\n} from \"@bettercms-ai/sdk\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\n\n/** Subset of the management SDK client the tools use (kept narrow for testability). */\nexport interface ManagementApi {\n listPages(): Promise<ManagedPage[]>;\n getPage(id: string): Promise<ManagedPage>;\n getModel(id: string): Promise<ManagedContentModel>;\n updateModel(id: string, input: UpdateModelInput): Promise<ManagedContentModel>;\n createPage(input: CreateManagedPageInput): Promise<ManagedPage>;\n addPageFields(\n id: string,\n input: { addFields: OutField[] },\n ): Promise<ManagedPage>;\n setPageContent(\n id: string,\n input: { data: Record<string, unknown>; status?: \"draft\" | \"published\" },\n ): Promise<ManagedContentEntry>;\n createEntry(input: CreateEntryInput): Promise<ManagedContentEntry>;\n updateEntry(\n id: string,\n input: UpdateEntryInput,\n opts?: { ifMatch?: number },\n ): Promise<ManagedContentEntry>;\n listEntries(filter?: {\n modelId?: string;\n pageId?: string;\n status?: \"draft\" | \"published\";\n }): Promise<ManagedContentEntry[]>;\n getEntry(id: string): Promise<ManagedContentEntry>;\n uploadAsset(input: UploadAssetInput): Promise<UploadedAsset>;\n deletePage(id: string): Promise<{ id: string }>;\n deleteEntry(id: string): Promise<{ id: string }>;\n deleteModel(id: string): Promise<{ id: string }>;\n listForms(): Promise<ManagedForm[]>;\n getForm(id: string): Promise<ManagedForm>;\n createForm(input: ManagedFormInput): Promise<ManagedForm>;\n updateForm(id: string, input: ManagedFormInput): Promise<ManagedForm>;\n listComponents(): Promise<ManagedComponent[]>;\n getComponent(id: string): Promise<ManagedComponent>;\n createComponent(input: ManagedComponentInput): Promise<ManagedComponent>;\n updateComponent(id: string, input: ManagedComponentInput): Promise<ManagedComponent>;\n}\n\nexport interface ToolDeps {\n auth: {\n getAccessToken(): Promise<string>;\n refresh(): Promise<string | null>;\n /** Forget cached creds + re-run device flow (used when the bound project was deleted). */\n resetAndReauthorize(): Promise<string>;\n };\n /** Build a management client bound to the given access token. */\n createClient: (apiKey: string) => ManagementApi;\n}\n\n/** MCP tool result shape (text content + optional error flag). */\nexport interface ToolResult {\n content: Array<{ type: \"text\"; text: string }>;\n isError?: boolean;\n}\n\nexport interface ToolDef {\n name: string;\n config: { title: string; description: string; inputSchema: z.ZodRawShape };\n handler: (args: Record<string, unknown>) => Promise<ToolResult>;\n}\n\n// ── Shared schema pieces ──────────────────────────────────────────────────────\n\nconst fieldType = z.enum([\n \"text\",\n \"richtext\",\n \"image\",\n \"boolean\",\n \"number\",\n \"select\",\n \"reference\",\n \"multi-reference\",\n \"array\",\n \"date\",\n \"datetime\",\n \"group\", // Non-Repeatable Zone: one nested object of fields\n \"repeater\", // Repeatable Zone: an array of nested field-objects\n]);\n\nconst slug = z\n .string()\n .regex(/^[a-z0-9-]+$/, \"lowercase letters, numbers, and hyphens only\");\n\nconst fieldKey = z\n .string()\n .regex(/^[a-zA-Z0-9_]+$/, \"letters, numbers, and underscores only\");\n\n// Recursive field schema. The MCP SDK serialises this Zod shape to the JSON\n// Schema the LLM sees — on SDK >= 1.29 (the floor pinned in package.json) the\n// `z.lazy()` below emits a proper `$ref`/`$defs` recursion so nested\n// group/repeater fields are VISIBLE. A stale older SDK collapsed it to an\n// opaque {} (the historic \"nested fields not created\" bug). The same recursive\n// shape is hand-written as JSON Schema in the remote proxy — keep the two in\n// sync: see bettercms-backend/src/routes/mcp/index.ts (FIELD_DEF).\n/** A field definition. Recursive: `group`/`repeater` nest more fields. */\nexport type FieldInput = {\n key: string;\n label: string;\n type: z.infer<typeof fieldType>;\n required?: boolean;\n options?: string[];\n config?: Record<string, unknown>;\n fields?: FieldInput[];\n};\n\nconst fieldShape = {\n key: fieldKey.describe(\"machine field key, e.g. 'title' or 'blog_hero'\"),\n label: z.string().min(1).describe(\"human label shown in the editor\"),\n type: fieldType,\n required: z.boolean().optional(),\n options: z.array(z.string()).optional().describe(\"choices when type is 'select'\"),\n config: z\n .record(z.string(), z.unknown())\n .optional()\n .describe(\n \"per-type config: reference {contentModelId}, multi-reference {contentModelId,min,max}, array {itemType: 'text'|'number'|'date'}, date {includeTime}\",\n ),\n fields: z\n .array(z.lazy(() => fieldObject))\n .optional()\n .describe(\n \"NESTED child fields — REQUIRED for type 'group' (one nested object, a Non-Repeatable Zone like blog_hero → heading, description, hero_image) and type 'repeater' (a repeatable array of such objects, a Repeatable Zone / section-list like testimonials → quote, author). A section with repeating items is a 'repeater'; a fixed grouped block is a 'group'. Recurse to any depth — do NOT flatten zones into separate top-level fields.\",\n ),\n};\nconst fieldObject: z.ZodType<FieldInput> = z.object(fieldShape);\n\n/**\n * Output field shape persisted by the API. Canonical: nesting lives on `array` via\n * `config.zones`. The LLM may still speak `group`/`repeater` (kept in the input schema\n * because it's intuitive) — toField() maps those into the canonical array.zones shape\n * here, so the backend, dashboard, codegen, and delivery all see one model.\n */\ntype OutField = {\n key: string;\n label: string;\n type: Exclude<FieldInput[\"type\"], \"group\" | \"repeater\">;\n required?: boolean;\n options?: string[];\n config?: Record<string, unknown>;\n};\n\nfunction toFields(fs: FieldInput[] | undefined): OutField[] {\n return (fs ?? []).map(toField);\n}\n\nfunction toField(f: FieldInput): OutField {\n const base = {\n key: f.key,\n label: f.label,\n ...(f.required !== undefined ? { required: f.required } : {}),\n };\n\n // group → array with a non-repeatable zone (a fixed block).\n if (f.type === \"group\") {\n return { ...base, type: \"array\", config: { zones: { nonRepeatable: toFields(f.fields) } } };\n }\n // repeater → array with a repeatable zone (a list of blocks).\n if (f.type === \"repeater\") {\n return { ...base, type: \"array\", config: { zones: { repeatable: { fields: toFields(f.fields) } } } };\n }\n // Already-canonical zoned array (the LLM emitted config.zones directly) → recurse.\n if (f.type === \"array\" && f.config && typeof f.config === \"object\" && \"zones\" in f.config) {\n const zones = (f.config as { zones?: { nonRepeatable?: FieldInput[]; repeatable?: { fields?: FieldInput[]; minItems?: number; maxItems?: number } } }).zones ?? {};\n return {\n ...base,\n type: \"array\",\n config: {\n zones: {\n ...(zones.nonRepeatable ? { nonRepeatable: toFields(zones.nonRepeatable) } : {}),\n ...(zones.repeatable\n ? {\n repeatable: {\n fields: toFields(zones.repeatable.fields),\n ...(zones.repeatable.minItems !== undefined ? { minItems: zones.repeatable.minItems } : {}),\n ...(zones.repeatable.maxItems !== undefined ? { maxItems: zones.repeatable.maxItems } : {}),\n },\n }\n : {}),\n },\n },\n };\n }\n\n // Leaf field or primitive array (config.itemType) — pass through.\n return {\n ...base,\n type: f.type,\n ...(f.options ? { options: f.options } : {}),\n ...(f.config ? { config: f.config } : {}),\n };\n}\n\n// ── Result helpers ────────────────────────────────────────────────────────────\n\nfunction ok(summary: string, data: unknown): ToolResult {\n return {\n content: [\n { type: \"text\", text: summary },\n { type: \"text\", text: JSON.stringify(data, null, 2) },\n ],\n };\n}\n\nfunction fail(message: string): ToolResult {\n return { content: [{ type: \"text\", text: message }], isError: true };\n}\n\n/**\n * Authorization isn't done yet. Surface the clickable activation link *in the\n * tool result* (the one channel every MCP client renders — unlike the server's\n * stderr, which VS Code hides). The device flow is persisted, so simply\n * re-running this tool after approval resumes it and completes the request.\n */\nfunction authPrompt(err: DeviceAuthPendingError): ToolResult {\n const text = [\n \"🔐 BetterCMS authorization required — you're not signed in yet.\",\n \"\",\n `1. Open this link and approve: ${err.verificationUriComplete}`,\n ` (or visit ${err.verificationUri} and enter code ${err.userCode})`,\n \"2. Once approved, run this tool again — it resumes automatically and completes your request.\",\n ].join(\"\\n\");\n return { content: [{ type: \"text\", text }], isError: true };\n}\n\n// ── Tool definitions ──────────────────────────────────────────────────────────\n\nexport function buildToolDefs(deps: ToolDeps): ToolDef[] {\n /** Run with a token, retrying once on 401 after a refresh / re-auth. */\n async function withClient<T>(fn: (client: ManagementApi) => Promise<T>): Promise<T> {\n const token = await deps.auth.getAccessToken();\n try {\n return await fn(deps.createClient(token));\n } catch (err) {\n if (err instanceof BetterCMSError && err.status === 401) {\n const next = (await deps.auth.refresh()) ?? (await deps.auth.getAccessToken());\n return await fn(deps.createClient(next));\n }\n // The key is bound to a project that was deleted server-side (L1: 409\n // PROJECT_DELETED). It can never succeed again — clear creds and re-authorize\n // so the user picks a LIVE project, then retry the original call into it.\n // (resetAndReauthorize throws DeviceAuthPendingError if approval isn't instant,\n // which guard() turns into the clickable activation prompt.)\n if (err instanceof BetterCMSError && err.status === 409 && err.bodyCode === \"PROJECT_DELETED\") {\n const next = await deps.auth.resetAndReauthorize();\n return await fn(deps.createClient(next));\n }\n throw err;\n }\n }\n\n /** Wrap a handler with uniform error → ToolResult conversion. */\n function guard<A>(fn: (args: A) => Promise<ToolResult>) {\n return async (args: A): Promise<ToolResult> => {\n try {\n return await fn(args);\n } catch (err) {\n if (err instanceof DeviceAuthPendingError) {\n return authPrompt(err);\n }\n if (err instanceof BetterCMSError) {\n return fail(`BetterCMS error (${err.status} ${err.code}): ${err.message}`);\n }\n return fail(`Unexpected error: ${err instanceof Error ? err.message : String(err)}`);\n }\n };\n }\n\n const createPageInput = z.object({\n title: z.string().min(1).describe(\"display title of the page, e.g. 'Home'\"),\n slug: slug.describe(\"URL-safe path segment, unique per project, e.g. 'home'\"),\n pageType: z\n .enum([\"singleton\", \"dynamic\"])\n .default(\"singleton\")\n .describe(\n \"'singleton' = exactly one entry (Home, About, Contact); 'dynamic' = many entries sharing this schema (Blog posts, Products). Defaults to singleton.\",\n ),\n fields: z.array(fieldObject).optional().describe(\"the page's typed schema fields\"),\n metaTitle: z.string().optional().describe(\"SEO meta title\"),\n metaDescription: z.string().optional().describe(\"SEO meta description\"),\n });\n\n const addFieldInput = z.object({\n modelId: z.string().min(1).describe(\"id of the content model to extend\"),\n ...fieldShape,\n });\n\n const addPageFieldInput = z.object({\n pageId: z.string().min(1).describe(\"id of the page to extend (from list_pages / create_page)\"),\n ...fieldShape,\n });\n\n // Exactly one of localPath/url (enforced by the SDK + backend route, documented here).\n const uploadAssetInput = z.object({\n localPath: z\n .string()\n .min(1)\n .optional()\n .describe(\"absolute path to a local file (e.g. a repo image); provide this OR url\"),\n url: z\n .string()\n .url()\n .optional()\n .describe(\"remote image URL to ingest; provide this OR localPath\"),\n filename: z.string().optional().describe(\"override the stored filename\"),\n altText: z.string().optional().describe(\"accessibility alt text\"),\n caption: z.string().optional(),\n folderId: z.string().optional().describe(\"target Media Library folder (defaults to project root)\"),\n });\n\n const createEntryInput = z.object({\n contentModelId: z.string().min(1).describe(\"id of the model this entry belongs to\"),\n slug: slug.optional(),\n status: z.enum([\"draft\", \"published\"]).optional().describe(\"defaults to draft\"),\n data: z\n .record(z.string(), z.unknown())\n .optional()\n .describe(\"field values keyed by field key\"),\n });\n\n const getPageInput = z.object({\n pageId: z.string().min(1).describe(\"page id (from list_pages / create_page)\"),\n });\n\n const setPageContentInput = z.object({\n pageId: z.string().min(1).describe(\"id of the page to write values to\"),\n data: z\n .record(z.string(), z.unknown())\n .describe(\n \"field values keyed by field key. A nested 'array' (zone) value is an OBJECT { nonRepeatable: { childKey: value, … }, repeatable: [ { childKey: value }, … ] } — nonRepeatable holds the fixed-block values, repeatable is the list of item objects (omit a zone you didn't define). A primitive 'array' (itemType) is a plain list. An 'image' value is an asset URL or asset id (from upload_asset) — the server resolves it to { id, url, name, altText }. Read get_page first to see each field's zones.\",\n ),\n status: z.enum([\"draft\", \"published\"]).optional().describe(\"omit to leave status unchanged\"),\n });\n\n const getEntryInput = z.object({\n entryId: z.string().min(1).describe(\"content entry id\"),\n });\n\n const listEntriesInput = z.object({\n modelId: z.string().optional().describe(\"filter by content model id\"),\n pageId: z.string().optional().describe(\"filter by page id (a singleton page has one entry)\"),\n status: z.enum([\"draft\", \"published\"]).optional(),\n });\n\n const updateEntryInput = z.object({\n entryId: z.string().min(1).describe(\"content entry id\"),\n data: z.record(z.string(), z.unknown()).optional().describe(\"field values keyed by field key\"),\n status: z.enum([\"draft\", \"published\"]).optional(),\n slug: slug.optional(),\n });\n\n const deletePageInput = z.object({\n pageId: z.string().min(1).describe(\"id of the page to delete (from list_pages)\"),\n });\n const deleteEntryInput = z.object({\n entryId: z.string().min(1).describe(\"id of the content entry to delete (from list_entries)\"),\n });\n const deleteModelInput = z.object({\n modelId: z.string().min(1).describe(\"id of the content model to delete (from list_content_models)\"),\n });\n\n const getFormInput = z.object({\n formId: z.string().min(1).describe(\"form id (from list_forms)\"),\n });\n\n // ── Form authoring schemas ──\n const formFieldObject = z.object({\n key: z.string().min(1).describe(\"machine key for the submitted value, e.g. 'email'\"),\n label: z.string().min(1).describe(\"field label shown to the visitor\"),\n type: z.enum([\n \"text\", \"email\", \"textarea\", \"select\", \"checkbox\", \"number\",\n \"phone\", \"date\", \"url\", \"consent\", \"hidden\",\n ]),\n placeholder: z.string().optional(),\n required: z.boolean().optional(),\n options: z.array(z.string()).optional().describe(\"choices when type is 'select'\"),\n defaultValue: z.string().optional(),\n showIf: z\n .object({ field: z.string(), equals: z.string() })\n .optional()\n .describe(\"show this field only when another field equals a value\"),\n });\n const formSettingsShape = {\n description: z.string().optional(),\n submitLabel: z.string().optional().describe(\"submit button label (default 'Submit')\"),\n successMessage: z.string().optional(),\n redirectUrl: z.string().url().optional().describe(\"URL to redirect to on success\"),\n };\n const createFormInput = z.object({\n name: z.string().min(1).describe(\"human form name (used by getForm('Name'))\"),\n fields: z.array(formFieldObject).default([]).describe(\"the form's fields\"),\n ...formSettingsShape,\n });\n const updateFormInput = z.object({\n formId: z.string().min(1).describe(\"form id (from list_forms)\"),\n name: z.string().optional(),\n fields: z.array(formFieldObject).optional().describe(\"REPLACES the field array — include all fields to keep\"),\n ...formSettingsShape,\n });\n\n // ── Component authoring schemas ──\n // blockJson is a recursive ContentBlock tree; props is the override allowlist. The\n // backend validates the exact block union, so blocks are typed loosely here (type/id/props).\n interface BlockInput { type: string; id: string; props: Record<string, unknown> }\n const blockObject: z.ZodType<BlockInput> = z.object({\n type: z\n .enum([\"heading\", \"text\", \"image\", \"button\", \"spacer\", \"video\", \"columns\"])\n .describe(\"block type; 'columns' nests child blocks in props.columns\"),\n id: z.string().min(1).describe(\"stable unique block id\"),\n props: z\n .record(z.string(), z.unknown())\n .describe(\n \"per-type props: heading {text, level}; text {text}; image {src, alt}; button {text, href}; spacer {height}; video {url}; columns {columns: block[][], gap}\",\n ),\n });\n const componentPropObject = z.object({\n key: z.string().min(1),\n label: z.string().min(1),\n target: z.object({ blockId: z.string().min(1), path: z.string().min(1) }),\n type: z.enum([\"text\", \"richtext\", \"image\", \"url\", \"boolean\"]),\n defaultValue: z.unknown().optional(),\n });\n const componentCategory = z.enum([\n \"navbar\", \"footer\", \"button\", \"section\", \"slider\", \"tabs\", \"form\", \"custom\",\n ]);\n const createComponentInput = z.object({\n name: z.string().min(1),\n slug: slug.describe(\"url-safe unique slug (lowercase letters/numbers/hyphens)\"),\n category: componentCategory.optional().describe(\"defaults to 'custom'\"),\n description: z.string().optional(),\n blockJson: z.array(blockObject).default([]).describe(\"the component's block tree\"),\n props: z.array(componentPropObject).default([]).describe(\"overridable fields\"),\n });\n const updateComponentInput = z.object({\n componentId: z.string().min(1).describe(\"component id (from list_components)\"),\n name: z.string().optional(),\n category: componentCategory.optional(),\n description: z.string().optional(),\n blockJson: z.array(blockObject).optional().describe(\"REPLACES the block tree\"),\n props: z.array(componentPropObject).optional(),\n });\n const getComponentInput = z.object({\n componentId: z.string().min(1).describe(\"component id (from list_components)\"),\n });\n\n const defs: ToolDef[] = [\n {\n name: \"list_pages\",\n config: {\n title: \"List pages in the current project\",\n description:\n \"List the pages in the project this MCP key is bound to, each with its full field SCHEMA, pageType (singleton|dynamic), and status. Use it to verify WHERE content lands and to read field keys/types before set_page_content / add_page_field.\",\n inputSchema: {},\n },\n handler: guard(async () =>\n withClient(async (client) => {\n const pages = await client.listPages();\n return ok(\n `${pages.length} page(s) in the bound project.`,\n pages.map((p) => ({\n id: p.id,\n title: p.title,\n slug: p.slug,\n pageType: p.pageType,\n status: p.status,\n fields: p.fields,\n })),\n );\n }),\n ) as ToolDef[\"handler\"],\n },\n {\n name: \"get_page\",\n config: {\n title: \"Get a page (with its field schema)\",\n description:\n \"Get one page by id INCLUDING its full field schema (keys, types, nested group/repeater children) and pageType. Read this before set_page_content so you write correctly-keyed values, or before add_page_field so you know the existing keys.\",\n inputSchema: getPageInput.shape,\n },\n handler: guard(async (args: z.infer<typeof getPageInput>) =>\n withClient(async (client) => {\n const page = await client.getPage(args.pageId);\n return ok(\n `Page '${page.title}' (${page.pageType ?? \"page\"}, ${page.fields.length} field(s)).`,\n page,\n );\n }),\n ) as ToolDef[\"handler\"],\n },\n {\n name: \"create_page\",\n config: {\n title: \"Create a page\",\n description:\n \"Create a page with its own typed schema. Supports pageType 'singleton' (exactly one entry — Home, About, Contact) and 'dynamic' (many entries sharing the schema — Blog posts, Products). Project-scoped from the key. Additive — does not delete or overwrite existing pages. FIRST read the page's real markup and DECOMPOSE it into a destructured tree: each visual section becomes a nested field — a fixed grouped block → type 'group', a repeating list of items (cards, testimonials, features, FAQs) → type 'repeater' — each carrying its own child `fields`. Do NOT flatten sections into many flat top-level fields. Build the full nested tree, then call this once.\",\n inputSchema: createPageInput.shape,\n },\n handler: guard(async (args: z.infer<typeof createPageInput>) =>\n withClient(async (client) => {\n const page = await client.createPage({\n title: args.title,\n slug: args.slug,\n pageType: args.pageType ?? \"singleton\",\n ...(args.fields ? { fields: args.fields.map(toField) } : {}),\n ...(args.metaTitle !== undefined ? { metaTitle: args.metaTitle } : {}),\n ...(args.metaDescription !== undefined ? { metaDescription: args.metaDescription } : {}),\n });\n return ok(\n `Created ${page.pageType ?? \"page\"} page '${page.title}' (id ${page.id}, slug ${page.slug}) with ${page.fields.length} field(s).`,\n page,\n );\n }),\n ) as ToolDef[\"handler\"],\n },\n {\n name: \"add_field\",\n config: {\n title: \"Add a field to a content model\",\n description:\n \"Append a field to an existing content model. Reads the model's current fields and adds yours (read-modify-write) — never removes existing fields. For a section/zone, add ONE 'group' (fixed block) or 'repeater' (repeating list) field carrying its child `fields` — don't add the zone's inner fields as separate top-level fields.\",\n inputSchema: addFieldInput.shape,\n },\n handler: guard(async (args: z.infer<typeof addFieldInput>) =>\n withClient(async (client) => {\n const model = await client.getModel(args.modelId);\n if (model.fields.some((f) => f.key === args.key)) {\n return fail(`Field '${args.key}' already exists on model '${model.name}'.`);\n }\n const updated = await client.updateModel(args.modelId, {\n fields: [...model.fields, toField(args)],\n });\n return ok(\n `Added field '${args.key}' to '${updated.name}'. Model now has ${updated.fields.length} field(s).`,\n updated,\n );\n }),\n ) as ToolDef[\"handler\"],\n },\n {\n name: \"add_page_field\",\n config: {\n title: \"Add a field to a page\",\n description:\n \"Append a field to an existing page's schema (Home, About, a blog template, etc.). Additive — the API rejects a key that already exists and never overwrites or retypes existing fields. Use this when the target is a page (singleton or dynamic); use add_field when the target is a content model. For a section/zone, add ONE 'group' (fixed block) or 'repeater' (repeating list) field carrying its child `fields` — don't add the zone's inner fields as separate top-level fields.\",\n inputSchema: addPageFieldInput.shape,\n },\n handler: guard(async (args: z.infer<typeof addPageFieldInput>) =>\n withClient(async (client) => {\n const page = await client.addPageFields(args.pageId, { addFields: [toField(args)] });\n return ok(\n `Added field '${args.key}' to page '${page.title}'. Page now has ${page.fields.length} field(s).`,\n page,\n );\n }),\n ) as ToolDef[\"handler\"],\n },\n {\n name: \"create_entry\",\n config: {\n title: \"Create a content entry\",\n description:\n \"Create a content entry under a model. If data/status are provided they are applied in a follow-up update (entries are created as empty drafts).\",\n inputSchema: createEntryInput.shape,\n },\n handler: guard(async (args: z.infer<typeof createEntryInput>) =>\n withClient(async (client) => {\n const created = await client.createEntry({\n contentModelId: args.contentModelId,\n ...(args.slug !== undefined ? { slug: args.slug } : {}),\n });\n const needsUpdate = args.data !== undefined || args.status !== undefined;\n const entry = needsUpdate\n ? await client.updateEntry(created.id, {\n ...(args.data !== undefined ? { data: args.data } : {}),\n ...(args.status !== undefined ? { status: args.status } : {}),\n })\n : created;\n return ok(\n `Created entry '${entry.slug}' (id ${entry.id}, status ${entry.status}).`,\n entry,\n );\n }),\n ) as ToolDef[\"handler\"],\n },\n {\n name: \"set_page_content\",\n config: {\n title: \"Set a page's field values (content)\",\n description:\n \"Set a page's field VALUES — the actual content. For a SINGLETON page (Home, About, Site Settings) this creates or updates its one entry, so call it again to edit. `data` is keyed by field key: a nested 'array' (zone) value is an OBJECT { nonRepeatable: { childKey: value }, repeatable: [ { childKey: value } ] }; a primitive 'array' is a plain list; an 'image' value is an asset URL. Read the schema first with get_page. This is how you populate Home/About/Settings — create_entry is for dynamic collections only.\",\n inputSchema: setPageContentInput.shape,\n },\n handler: guard(async (args: z.infer<typeof setPageContentInput>) =>\n withClient(async (client) => {\n const entry = await client.setPageContent(args.pageId, {\n data: args.data,\n ...(args.status !== undefined ? { status: args.status } : {}),\n });\n return ok(\n `Set content on page ${args.pageId} (entry ${entry.id}, status ${entry.status}).`,\n entry,\n );\n }),\n ) as ToolDef[\"handler\"],\n },\n {\n name: \"list_entries\",\n config: {\n title: \"List content entries (incl. drafts)\",\n description:\n \"List content entries — including drafts — filtered by model and/or page. Use it to SEE existing content before editing. For a singleton page, pass its pageId to get its single entry.\",\n inputSchema: listEntriesInput.shape,\n },\n handler: guard(async (args: z.infer<typeof listEntriesInput>) =>\n withClient(async (client) => {\n const entries = await client.listEntries({\n ...(args.modelId ? { modelId: args.modelId } : {}),\n ...(args.pageId ? { pageId: args.pageId } : {}),\n ...(args.status ? { status: args.status } : {}),\n });\n return ok(`${entries.length} entr(y/ies).`, entries);\n }),\n ) as ToolDef[\"handler\"],\n },\n {\n name: \"get_entry\",\n config: {\n title: \"Get a content entry (with its values)\",\n description: \"Get one content entry by id INCLUDING its `data` (field values), even when draft.\",\n inputSchema: getEntryInput.shape,\n },\n handler: guard(async (args: z.infer<typeof getEntryInput>) =>\n withClient(async (client) => {\n const entry = await client.getEntry(args.entryId);\n return ok(`Entry '${entry.slug}' (id ${entry.id}, status ${entry.status}).`, entry);\n }),\n ) as ToolDef[\"handler\"],\n },\n {\n name: \"update_entry\",\n config: {\n title: \"Update a content entry's values\",\n description:\n \"Update a content entry's `data` (field values) and/or status by id. `data` is keyed by field key; a nested 'array' (zone) value is an object { nonRepeatable: {…}, repeatable: [{…}] }, a primitive 'array' is a plain list. Use this to edit an existing entry; for a singleton page prefer set_page_content.\",\n inputSchema: updateEntryInput.shape,\n },\n handler: guard(async (args: z.infer<typeof updateEntryInput>) =>\n withClient(async (client) => {\n const entry = await client.updateEntry(args.entryId, {\n ...(args.data !== undefined ? { data: args.data } : {}),\n ...(args.status !== undefined ? { status: args.status } : {}),\n ...(args.slug !== undefined ? { slug: args.slug } : {}),\n });\n return ok(`Updated entry '${entry.slug}' (id ${entry.id}, status ${entry.status}).`, entry);\n }),\n ) as ToolDef[\"handler\"],\n },\n {\n name: \"upload_asset\",\n config: {\n title: \"Upload an asset to the Media Library\",\n description:\n \"Upload an image/asset from a local file path or a remote URL into the project's Media Library. Returns the asset's stable CDN URL — put that URL into a content entry's image field. Use this BEFORE creating entries that reference images.\",\n inputSchema: uploadAssetInput.shape,\n },\n handler: guard(async (args: z.infer<typeof uploadAssetInput>) =>\n withClient(async (client) => {\n const asset = await client.uploadAsset(args);\n return ok(\n `Uploaded '${asset.filename}' (id ${asset.id}). Put this URL (or the id ${asset.id}) into an image field to attach it: ${asset.url}`,\n asset,\n );\n }),\n ) as ToolDef[\"handler\"],\n },\n {\n name: \"delete_page\",\n config: {\n title: \"Delete a page\",\n description:\n \"DELETE a page and its content. Destructive but REVERSIBLE — it soft-deletes (can be restored from the dashboard) and is audit-logged. Use it to remove a page you created by mistake. Confirm with the user before deleting content they may want.\",\n inputSchema: deletePageInput.shape,\n },\n handler: guard(async (args: z.infer<typeof deletePageInput>) =>\n withClient(async (client) => {\n const res = await client.deletePage(args.pageId);\n return ok(`Deleted page ${res.id} (soft-delete — restorable from the dashboard).`, res);\n }),\n ) as ToolDef[\"handler\"],\n },\n {\n name: \"delete_entry\",\n config: {\n title: \"Delete a content entry\",\n description:\n \"DELETE a single content entry. Destructive but REVERSIBLE (soft-delete, restorable from the dashboard) and audit-logged. Use it to remove content created by mistake.\",\n inputSchema: deleteEntryInput.shape,\n },\n handler: guard(async (args: z.infer<typeof deleteEntryInput>) =>\n withClient(async (client) => {\n const res = await client.deleteEntry(args.entryId);\n return ok(`Deleted entry ${res.id} (soft-delete — restorable from the dashboard).`, res);\n }),\n ) as ToolDef[\"handler\"],\n },\n {\n name: \"delete_content_model\",\n config: {\n title: \"Delete a content model\",\n description:\n \"DELETE a content model and its entries. Destructive but REVERSIBLE (soft-delete, restorable from the dashboard) and audit-logged. Confirm with the user first — this removes all content under the model.\",\n inputSchema: deleteModelInput.shape,\n },\n handler: guard(async (args: z.infer<typeof deleteModelInput>) =>\n withClient(async (client) => {\n const res = await client.deleteModel(args.modelId);\n return ok(`Deleted content model ${res.id} and its entries (soft-delete — restorable from the dashboard).`, res);\n }),\n ) as ToolDef[\"handler\"],\n },\n // ── Forms (read-only — discover dashboard forms to embed into the site) ──\n {\n name: \"list_forms\",\n config: {\n title: \"List forms in the current project\",\n description:\n \"List the forms built in the dashboard for the bound project — each with its id, name, and field schema. Use it to find a form to add to the user's site: read it, then write `<BcmsForm form={getForm('Name')} />` (from @bettercms-ai/next) into the page/component where the user wants it.\",\n inputSchema: {},\n },\n handler: guard(async () =>\n withClient(async (client) => {\n const forms = await client.listForms();\n return ok(\n `${forms.length} form(s) in the bound project.`,\n forms.map((f) => ({ id: f.id, name: f.name, fields: f.fields })),\n );\n }),\n ) as ToolDef[\"handler\"],\n },\n {\n name: \"get_form\",\n config: {\n title: \"Get a form (with its field schema)\",\n description:\n \"Get one form by id INCLUDING its fields (keys, types, required, options, showIf) and settings (submitLabel, successMessage, redirectUrl, turnstileEnabled, honeypotField). Read this before wiring `<BcmsForm>` so you render the right fields.\",\n inputSchema: getFormInput.shape,\n },\n handler: guard(async (args: z.infer<typeof getFormInput>) =>\n withClient(async (client) => {\n const form = await client.getForm(args.formId);\n return ok(`Form '${form.name}' (${form.fields.length} field(s)).`, form);\n }),\n ) as ToolDef[\"handler\"],\n },\n {\n name: \"create_form\",\n config: {\n title: \"Create a form\",\n description:\n \"Create a form (fields + settings) in the bound project. CONFIRM the fields with the user first. Returns the new form's id — then embed it with `<BcmsForm form={getForm('Name')} />` from @bettercms-ai/next. Field types: text,email,textarea,select(needs options),checkbox,number,phone,date,url,consent,hidden.\",\n inputSchema: createFormInput.shape,\n },\n handler: guard(async (args: z.infer<typeof createFormInput>) =>\n withClient(async (client) => {\n const form = await client.createForm(args as ManagedFormInput);\n return ok(`Created form '${form.name}' (id ${form.id}). Embed with <BcmsForm form={getForm('${form.name}')} />.`, form);\n }),\n ) as ToolDef[\"handler\"],\n },\n {\n name: \"update_form\",\n config: {\n title: \"Update a form\",\n description:\n \"Update a form by id — name, fields, or settings. Read get_form first. Passing `fields` REPLACES the array (include all fields to keep).\",\n inputSchema: updateFormInput.shape,\n },\n handler: guard(async (args: z.infer<typeof updateFormInput>) =>\n withClient(async (client) => {\n const { formId, ...input } = args;\n const form = await client.updateForm(formId, input as ManagedFormInput);\n return ok(`Updated form '${form.name}' (id ${form.id}).`, form);\n }),\n ) as ToolDef[\"handler\"],\n },\n // ── Components (discover + author reusable symbols) ──\n {\n name: \"list_components\",\n config: {\n title: \"List reusable components\",\n description:\n \"List the reusable components in the bound project — each with id, name, slug, category, blockJson, and props. Use it to find a component to render with `<BcmsBlocks>` from @bettercms-ai/next.\",\n inputSchema: {},\n },\n handler: guard(async () =>\n withClient(async (client) => {\n const list = await client.listComponents();\n return ok(\n `${list.length} component(s) in the bound project.`,\n list.map((cmp) => ({ id: cmp.id, name: cmp.name, slug: cmp.slug, category: cmp.category })),\n );\n }),\n ) as ToolDef[\"handler\"],\n },\n {\n name: \"get_component\",\n config: {\n title: \"Get a component (with its blockJson)\",\n description:\n \"Get one component by id INCLUDING its blockJson tree and props. Read this before update_component so you keep the existing blocks.\",\n inputSchema: getComponentInput.shape,\n },\n handler: guard(async (args: z.infer<typeof getComponentInput>) =>\n withClient(async (client) => {\n const cmp = await client.getComponent(args.componentId);\n return ok(`Component '${cmp.name}' (${cmp.blockJson.length} block(s)).`, cmp);\n }),\n ) as ToolDef[\"handler\"],\n },\n {\n name: \"create_component\",\n config: {\n title: \"Create a reusable component\",\n description:\n \"Create a reusable component from a blockJson tree. CONFIRM the structure with the user first. blockJson is an array of blocks (heading/text/image/button/spacer/video/columns); a 'columns' block nests child blocks in props.columns. `props` declares overridable fields. Returns the new id — render with `<BcmsBlocks>`.\",\n inputSchema: createComponentInput.shape,\n },\n handler: guard(async (args: z.infer<typeof createComponentInput>) =>\n withClient(async (client) => {\n const cmp = await client.createComponent(args as ManagedComponentInput);\n return ok(`Created component '${cmp.name}' (id ${cmp.id}, slug ${cmp.slug}).`, cmp);\n }),\n ) as ToolDef[\"handler\"],\n },\n {\n name: \"update_component\",\n config: {\n title: \"Update a reusable component\",\n description:\n \"Update a component by id — blockJson, props, name, or category. Read get_component first. Passing `blockJson`/`props` REPLACES them. Updating re-bakes every page that embeds this component.\",\n inputSchema: updateComponentInput.shape,\n },\n handler: guard(async (args: z.infer<typeof updateComponentInput>) =>\n withClient(async (client) => {\n const { componentId, ...input } = args;\n const cmp = await client.updateComponent(componentId, input as ManagedComponentInput);\n return ok(`Updated component '${cmp.name}' (id ${cmp.id}).`, cmp);\n }),\n ) as ToolDef[\"handler\"],\n },\n ];\n\n return defs;\n}\n\n/** Register all BetterCMS tools on an MCP server. */\nexport function registerTools(server: McpServer, deps: ToolDeps): void {\n for (const def of buildToolDefs(deps)) {\n server.registerTool(def.name, def.config, def.handler as never);\n }\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\n\n/**\n * Guided slash-command prompts that ship WITH the MCP. Claude Code (and other\n * hosts) surface server prompts automatically as `/mcp__bettercms__<name>`, so\n * adding the MCP is all it takes — no per-machine skill files.\n *\n * Design: one **generous parent** (`studio`) that reads the user's intent and\n * routes to the right sub-flow, plus thin direct entry points (`new_page`, ...)\n * for the individual flows. New BetterCMS tools get a new sub-flow section here.\n */\n\n// ── Reusable sub-flows ──────────────────────────────────────────────────────\n\nconst SCHEMA_PROPOSAL_FLOW = `### Whole-project schema design (declarative, confirm-first) → \\`create_page\\` / \\`add_field\\`\nDesign the WHOLE project's content schema from its code, and **confirm the\nshape with the user BEFORE creating anything**. Never silently guess.\n1. **Read the project's code** — the connected git repo OR, for an uploaded project,\n its source in your current working directory (same thing: pages/routes and their\n components). For each page, identify the editable regions and how they're structured.\n2. **Destructure every page into a tree**, do NOT flatten. Map each region to:\n - a plain field (text/richtext/image/number/boolean/select/date/datetime/reference),\n - a **group** (Non-Repeatable Zone — a fixed grouped block, e.g. a hero with\n heading + subheading + image), or\n - a **repeater** (Repeatable Zone / section-list — a repeating list of items\n such as feature cards, testimonials, FAQs, pricing tiers), each carrying its\n own child \\`fields\\`. Use \\`array\\` only for primitive lists (tags, bullet strings).\n Decide pageType per page: singleton (Home/About/Contact) vs dynamic (Blog/Products).\n3. **Present the proposal for confirmation (REQUIRED gate).** Show the full tree —\n page → zones → nested fields, with each zone labelled group vs repeater — and ask\n the user (AskUserQuestion) to confirm or adjust: which zones repeat, what fields\n each holds, singleton vs dynamic, any missing/extra pages. Iterate until they\n approve. Do NOT call create_page / add_field before this approval.\n4. **Build it** — once approved, call \\`create_page\\` once per page with the full\n nested \\`fields\\` tree; use \\`add_field\\`/\\`add_page_field\\` only to extend later.\n Keep field keys stable and human-readable. Schema only — no content yet.\nField object: { key, label, type, required?, options?, config?, fields? }.`;\n\nconst PAGE_FLOW = `### Page authoring → \\`create_page\\` tool\nAuthor a page (the page-first schema). Ask, in order, via AskUserQuestion:\n1. **Page type** — singleton (exactly one entry: Home, About, Contact) vs dynamic\n (many entries sharing the schema: Blog posts, Products).\n2. **Identity** — title; derive a slug (lowercase, a–z 0–9 -) and confirm; optional\n metaTitle/metaDescription.\n3. **Fields (loop until done)** — for each: key (^[a-zA-Z0-9_]+$), label, type,\n required?. Types (13): text, richtext, image, boolean, number, select (needs\n \\`options: string[]\\`), reference / multi-reference (\\`config.contentModelId\\`,\n multi adds min/max), array (primitives — \\`config.itemType\\`: text|number|date),\n date (\\`config.includeTime\\`?), datetime, **group** (Non-Repeatable Zone: ONE\n nested object — recurse to collect its \\`fields\\`, e.g. blog_hero → heading,\n description, hero_image), **repeater** (Repeatable Zone: an ARRAY of such objects\n — recurse to collect item \\`fields\\`, e.g. testimonials → quote, author). Nesting\n may go several levels deep.\n4. **Review** the assembled tree, then call \\`create_page\\` with\n { title, slug, pageType, fields, metaTitle?, metaDescription? }. Field object:\n { key, label, type, required?, options?, config?, fields? }.`;\n\nconst FIELD_FLOW = `### Add a field → \\`add_field\\` (models) / \\`add_page_field\\` (pages)\nAppend a field to an existing schema. First decide the target: a **content model**\nor a **page** (Home, About, a blog template). Ask: which target (id — use\n\\`list_pages\\` to find a page id), then the field (key, label, type — any of the 13\nabove, including nested group/repeater), required?. Confirm, then call:\n- a model → \\`add_field\\` { modelId, key, label, type, ... }\n- a page → \\`add_page_field\\` { pageId, key, label, type, ... }\nBoth are additive: they reject a key that already exists and never retype/overwrite\nan existing field (edit those in the dashboard).`;\n\nconst ENTRY_FLOW = `### Create an entry → \\`create_entry\\` tool\nCreate a content entry under a model. Ask: which model (id), the field values (data),\nstatus (draft/published). Then call \\`create_entry\\` { contentModelId, data?, status?, slug? }.`;\n\nconst FORM_FLOW = `### Form authoring → \\`create_form\\` / \\`update_form\\` tools\nAuthor a form (then the user embeds it with \\`<BcmsForm form={getForm('Name')} />\\` from\n@bettercms-ai/next). Confirm the fields with the user BEFORE creating. Never guess fields.\n1. **Discover** — \\`list_forms\\` to see existing forms; \\`get_form\\` to read one before editing.\n2. **Collect fields (loop)** — for each: key (machine key for the value), label, type. Types:\n text, email, textarea, select (needs \\`options: string[]\\`), checkbox, number, phone, date,\n url, consent, hidden. Optional per field: required?, placeholder?, defaultValue?, and\n \\`showIf: { field, equals }\\` for conditional display.\n3. **Settings** — name (used by getForm('Name')), submitLabel?, successMessage?, redirectUrl?.\n4. **Confirm**, then \\`create_form\\` { name, fields, ... } (returns the new id), or\n \\`update_form\\` { formId, ... } to edit (passing \\`fields\\` REPLACES the array — include all).\n5. Offer to wire \\`<BcmsForm>\\` into the page/component where the user wants it.`;\n\nconst COMPONENT_FLOW = `### Component authoring → \\`create_component\\` / \\`update_component\\` tools\nAuthor a reusable component (Webflow-style symbol) the user renders with \\`<BcmsBlocks>\\`.\nblockJson is the visual definition; authoring it blind is error-prone, so go slow and\nconfirm. Never guess the layout.\n1. **Discover** — \\`list_components\\` / \\`get_component\\` (read before update; keep existing blocks).\n2. **Design the blockJson tree** — an array of blocks, each { type, id, props }. Types:\n heading {text, level 1-6}, text {text}, image {src, alt}, button {text, href}, spacer\n {height}, video {url}, and **columns** {columns: array of arrays of blocks, gap} which\n NESTS child blocks. Give every block a stable unique id.\n3. **Props (optional)** — declare overridable fields: { key, label, target: { blockId, path },\n type: text|richtext|image|url|boolean, defaultValue? } so instances can be customized.\n4. **Confirm the structure** (AskUserQuestion: show the block tree), then \\`create_component\\`\n { name, slug, category?, blockJson, props? } (returns the id), or \\`update_component\\`\n { componentId, ... } — note updating re-bakes every page that embeds it.`;\n\n// ── Registration ─────────────────────────────────────────────────────────────\n\nexport function registerPrompts(server: McpServer): void {\n // Parent router — the generous entry point.\n server.registerPrompt(\n \"studio\",\n {\n title: \"BetterCMS Studio (guided)\",\n description:\n \"One command to author in BetterCMS. Detects what you want — create a page, add a field, create an entry — and runs the matching guided flow, then calls the right tool.\",\n argsSchema: {\n request: z\n .string()\n .optional()\n .describe(\"what you want to do, e.g. 'a blog page with a hero zone'\"),\n },\n },\n ({ request }) => ({\n messages: [\n {\n role: \"user\",\n content: {\n type: \"text\",\n text: `You are the BetterCMS authoring assistant (via the bettercms MCP).\n${request ? `The user's request: \"${request}\".\\n` : \"\"}\nFirst, **preflight**: confirm the bettercms tools are loaded (\\`create_page\\`, \\`add_field\\`,\n\\`create_entry\\`). If \\`create_page\\` is missing and only \\`create_model\\` shows, the host has a\nstale cached MCP — tell the user to run \\`rm -rf ~/.npm/_npx\\` and restart, then stop.\n\nThen **route** to the matching sub-flow below based on the request and conversation\ncontext. DEFAULT: when setting up a project or designing its schema from the repo (or\nwhen intent is unclear), run the **whole-repo schema design** flow — it proposes the\nstructure and confirms with the user before creating anything. Use the single-page or\nsingle-field flows only for targeted follow-ups. If still unsure, ask the user\n(AskUserQuestion: \"Design the schema from my project\" / \"Create one page\" / \"Add a field\" /\n\"Create an entry\" / \"Build a form\" / \"Build a component\"). Run flows by asking ONE step at\na time, pre-filling sensible defaults from the request but never inventing fields the user\ndidn't imply. Whatever the page/zone/form/component is scoped to follows the MCP key's project.\n\n${SCHEMA_PROPOSAL_FLOW}\n\n${PAGE_FLOW}\n\n${FIELD_FLOW}\n\n${ENTRY_FLOW}\n\n${FORM_FLOW}\n\n${COMPONENT_FLOW}\n\nThis assistant is extensible: when new BetterCMS tools are added, a new sub-flow appears\nhere — route to it the same way.`,\n },\n },\n ],\n }),\n );\n\n // Direct entry point for whole-repo schema design (the confirm-first default).\n server.registerPrompt(\n \"propose_schema\",\n {\n title: \"Design schema from repo (confirm-first)\",\n description:\n \"Read the repository, propose a destructured content schema (pages → group/repeater zones → nested fields), confirm it with you, then create it via create_page. Use this to set up a project's schema.\",\n argsSchema: {\n request: z\n .string()\n .optional()\n .describe(\"optional focus, e.g. 'just the marketing pages' or 'the whole site'\"),\n },\n },\n ({ request }) => ({\n messages: [\n {\n role: \"user\",\n content: {\n type: \"text\",\n text: `Design the BetterCMS content schema for this repository.${\n request ? ` Focus: \"${request}\".` : \"\"\n }\n\nPreflight: if \\`create_page\\` isn't available (only create_model/add_field/create_entry),\nthe host has a stale cached MCP — tell the user to \\`rm -rf ~/.npm/_npx\\` and restart, then stop.\n\n${SCHEMA_PROPOSAL_FLOW}\n\nAfter creating, report each page's id, slug, type, and field count, and the project it\nlanded in. On 409 slug_taken, offer an alternative slug; on 401/403, the MCP key needs\n(re)authorizing.`,\n },\n },\n ],\n }),\n );\n\n // Direct entry point for the page flow (parent can also dispatch here).\n server.registerPrompt(\n \"new_page\",\n {\n title: \"New page (guided)\",\n description:\n \"Guided creation of a BetterCMS page (singleton or dynamic) with fields, including nested group (Non-Repeatable Zone) and repeater (Repeatable Zone) fields. Calls create_page.\",\n argsSchema: {\n request: z\n .string()\n .optional()\n .describe(\"what the page is, e.g. 'a blog page with a hero'\"),\n },\n },\n ({ request }) => ({\n messages: [\n {\n role: \"user\",\n content: {\n type: \"text\",\n text: `Create a BetterCMS page via the \\`create_page\\` tool.${\n request ? ` The user wants: \"${request}\".` : \"\"\n }\n\nPreflight: if \\`create_page\\` isn't available (only create_model/add_field/create_entry),\nthe host has a stale cached MCP — tell the user to \\`rm -rf ~/.npm/_npx\\` and restart, then stop.\n\n${PAGE_FLOW}\n\nAfter creating, report the page id, slug, type, field count, and the project it landed in.\nOn 409 slug_taken, offer an alternative slug; on 401/403, the MCP key needs (re)authorizing.`,\n },\n },\n ],\n }),\n );\n\n // Direct entry point for form authoring.\n server.registerPrompt(\n \"new_form\",\n {\n title: \"New form (guided)\",\n description:\n \"Guided creation of a BetterCMS form (fields + settings) you can embed with <BcmsForm>. Calls create_form.\",\n argsSchema: {\n request: z.string().optional().describe(\"what the form is, e.g. 'a contact form with name, email, message'\"),\n },\n },\n ({ request }) => ({\n messages: [\n {\n role: \"user\",\n content: {\n type: \"text\",\n text: `Author a BetterCMS form via the \\`create_form\\` tool.${\n request ? ` The user wants: \"${request}\".` : \"\"\n }\n\n${FORM_FLOW}\n\nAfter creating, report the form id and name, and how to embed it (\\`<BcmsForm form={getForm('Name')} />\\`).\nOn 401/403, the MCP key needs (re)authorizing.`,\n },\n },\n ],\n }),\n );\n\n // Direct entry point for component authoring.\n server.registerPrompt(\n \"new_component\",\n {\n title: \"New component (guided)\",\n description:\n \"Guided creation of a reusable BetterCMS component (a blockJson tree + overridable props) you render with <BcmsBlocks>. Calls create_component.\",\n argsSchema: {\n request: z.string().optional().describe(\"what the component is, e.g. 'a hero with heading, text and a button'\"),\n },\n },\n ({ request }) => ({\n messages: [\n {\n role: \"user\",\n content: {\n type: \"text\",\n text: `Author a reusable BetterCMS component via the \\`create_component\\` tool.${\n request ? ` The user wants: \"${request}\".` : \"\"\n }\n\n${COMPONENT_FLOW}\n\nAfter creating, report the component id, name, and slug, and how to render it (\\`<BcmsBlocks>\\`).\nOn 409 slug_taken, offer an alternative slug; on 401/403, the MCP key needs (re)authorizing.`,\n },\n },\n ],\n }),\n );\n}\n"],"mappings":"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","names":["z"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bettercms-ai/mcp",
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "BetterCMS MCP server — schema, content, forms + components authoring tools over the Management API, authed via OAuth device-code.",
|
|
6
|
+
"bin": {
|
|
7
|
+
"bettercms-mcp": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"module": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"README.md",
|
|
22
|
+
"SKILL.md"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"build": "tsup",
|
|
27
|
+
"start": "node dist/index.js",
|
|
28
|
+
"dev": "bun --watch run src/index.ts",
|
|
29
|
+
"test": "vitest run",
|
|
30
|
+
"test:watch": "vitest"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@bettercms-ai/sdk": "^1.6.0",
|
|
34
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
35
|
+
"zod": "^4.3.6"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/node": "^20",
|
|
39
|
+
"tsup": "^8.5.1",
|
|
40
|
+
"vitest": "^4.1.4"
|
|
41
|
+
},
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public",
|
|
44
|
+
"registry": "https://registry.npmjs.org/"
|
|
45
|
+
}
|
|
46
|
+
}
|