panini-connector-mcp 0.1.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/LICENSE +21 -0
- package/README.md +185 -0
- package/dist/index.cjs +728 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +704 -0
- package/dist/index.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/tools/install.ts","../src/tools/verify.ts","../src/tools/secret.ts","../src/tools/schema.ts"],"sourcesContent":["/**\n * panini-connector-mcp — MCP server exposing 4 tools that let an AI\n * agent (Claude Desktop / Cursor / Windsurf) install and verify the\n * panini-connector SDK on a customer's self-hosted site without human\n * intervention.\n *\n * Communication: stdio (JSON-RPC over stdin/stdout). Spawned by the\n * MCP client as a subprocess via `npx panini-connector-mcp`.\n *\n * IMPORTANT: never write to stdout (that's the protocol channel).\n * All diagnostic logging goes to stderr via the log() helper.\n */\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\n\nimport { registerInstallTool } from './tools/install.js'\nimport { registerVerifyTool } from './tools/verify.js'\nimport { registerSecretTool } from './tools/secret.js'\nimport { registerSchemaTool } from './tools/schema.js'\n\nexport function log(message: string): void {\n // stderr — safe to write freely; stdout is JSON-RPC only\n process.stderr.write(`[panini-connector-mcp] ${message}\\n`)\n}\n\nasync function main(): Promise<void> {\n const server = new McpServer(\n {\n name: 'panini-connector-mcp',\n version: '0.1.0',\n },\n {\n capabilities: {\n tools: {},\n },\n },\n )\n\n registerInstallTool(server)\n registerVerifyTool(server)\n registerSecretTool(server)\n registerSchemaTool(server)\n\n const transport = new StdioServerTransport()\n await server.connect(transport)\n log('server ready (stdio) — tools: install_panini_connector, verify_panini_connector, generate_shared_secret, get_envelope_schema')\n}\n\nmain().catch((err) => {\n process.stderr.write(`[panini-connector-mcp] fatal: ${err?.stack ?? err}\\n`)\n process.exit(1)\n})\n","/**\n * install_panini_connector — given a (language, framework, storage)\n * triple + optional customer schema config, returns everything the\n * agent needs to wire the connector into the customer's project:\n *\n * - install command (npm install or pip install)\n * - files to write (path + content)\n * - env vars to set\n * - post-deploy verification hint\n *\n * The generated code is IDENTICAL to what the wizard at\n * frontend/src/pages/growth/SiteIntegrationsPage.tsx generates.\n * Kept in sync manually for now — if this drifts we should extract\n * the snippet builder into a shared package.\n */\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { z } from 'zod'\n\nexport interface InstallInput {\n language: 'node' | 'python'\n framework:\n | 'next_app'\n | 'next_pages'\n | 'express'\n | 'fastapi'\n | 'django'\n | 'flask'\n storage: 'supabase' | 'postgres' | 'custom_db' | 'memory'\n existing_table?: string\n url_template?: string\n}\n\nexport interface InstallOutput {\n install_command: string\n files: Array<{ path: string; content: string; language: string }>\n env_vars: string\n post_install_note?: string\n verify_command: string\n}\n\nexport function buildInstall(input: InstallInput): InstallOutput {\n const {\n language,\n framework,\n storage,\n existing_table = 'blog_posts',\n url_template = 'https://yoursite.com/blog/{slug}',\n } = input\n\n const envVars = envVarsFor(storage)\n const verifyCommand =\n language === 'python'\n ? `panini-connector test --url https://your-site.com/api/seo-connector --secret $SEO_CONNECTOR_SECRET`\n : `npx panini-connector test --url https://your-site.com/api/seo-connector --secret $SEO_CONNECTOR_SECRET`\n\n if (language === 'node') {\n return buildNode(framework, storage, existing_table, url_template, envVars, verifyCommand)\n }\n return buildPython(framework, storage, existing_table, url_template, envVars, verifyCommand)\n}\n\n// ── Node builders ──────────────────────────────────────────────────\n\nfunction buildNode(\n framework: InstallInput['framework'],\n storage: InstallInput['storage'],\n table: string,\n urlTemplate: string,\n envVars: string,\n verifyCommand: string,\n): InstallOutput {\n const isCustomDb = storage === 'custom_db'\n const peerDeps = [\n storage === 'postgres' ? 'pg' : null,\n isCustomDb ? 'mysql2 # or mongodb / mssql / your DB driver' : null,\n framework === 'express' ? 'express' : null,\n ].filter(Boolean)\n const install =\n `npm install panini-connector` +\n (peerDeps.length ? `\\nnpm install ${peerDeps.join(' ')}` : '')\n\n const storageImport =\n storage === 'supabase'\n ? \"import { SupabaseStorage } from 'panini-connector/storage/supabase'\"\n : storage === 'postgres'\n ? \"import { PostgresStorage } from 'panini-connector/storage/postgres'\"\n : isCustomDb\n ? `import type { StorageAdapter, Post } from 'panini-connector'\nimport mysql from 'mysql2/promise' // ← swap for your DB driver`\n : \"import { MemoryStorage } from 'panini-connector'\"\n\n const storageInstance = isCustomDb\n ? 'new MyStorage()'\n : storage === 'supabase' || storage === 'postgres'\n ? renderExistingTableInstance(storage, table, urlTemplate)\n : 'new MemoryStorage()'\n\n const customDbClass = isCustomDb ? renderCustomDbClass(table, urlTemplate) : ''\n\n const nextAppCode = `import { createHandler } from 'panini-connector/next-app'\n${storageImport}\n${customDbClass}\nconst handler = createHandler({\n storage: ${storageInstance},\n})\nexport const POST = handler`\n\n const nextPagesCode = `import { createHandler } from 'panini-connector/next-pages'\n${storageImport}\n${customDbClass}\nexport const config = { api: { bodyParser: false } }\n\nexport default createHandler({\n storage: ${storageInstance},\n})`\n\n const expressCode = `import express from 'express'\nimport { mount } from 'panini-connector/express'\n${storageImport}\n${customDbClass}\nconst app = express()\n\nmount(app, {\n path: '/api/seo-connector',\n storage: ${storageInstance},\n})\n\napp.use(express.json()) // Safe here — comes AFTER mount()\napp.listen(3000)`\n\n const filePath =\n framework === 'next_app'\n ? 'app/api/seo-connector/route.ts'\n : framework === 'next_pages'\n ? 'pages/api/seo-connector.ts'\n : 'server.ts'\n const code =\n framework === 'next_app' ? nextAppCode\n : framework === 'next_pages' ? nextPagesCode\n : expressCode\n\n const postNote = buildPostNote(framework, storage)\n\n return {\n install_command: install,\n files: [{ path: filePath, content: code, language: 'typescript' }],\n env_vars: envVars,\n post_install_note: postNote,\n verify_command: verifyCommand,\n }\n}\n\nfunction renderExistingTableInstance(\n storage: 'supabase' | 'postgres',\n table: string,\n urlTemplate: string,\n): string {\n const cls = storage === 'supabase' ? 'SupabaseStorage' : 'PostgresStorage'\n return `new ${cls}({\n table: '${table}',\n externalIdColumn: 'id',\n externalUrlTemplate: '${urlTemplate}',\n toRow: (post) => ({\n title: post.title,\n slug: post.slug,\n content: post.body_html,\n description: post.excerpt || post.seo_meta?.description || '',\n meta_title: post.seo_meta?.title || post.title,\n meta_description: post.seo_meta?.description || post.excerpt,\n featured_image_url: post.featured_image_url,\n author_name: post.author_name,\n status: post.status || 'published',\n category: 'seo',\n external_source: 'panini',\n published_at: new Date().toISOString(),\n }),\n })`\n}\n\nfunction renderCustomDbClass(table: string, urlTemplate: string): string {\n const slugTemplate = urlTemplate.replace('{slug}', '${post.slug}')\n return `\nclass MyStorage implements StorageAdapter {\n async ping() {\n return { site_name: '${table}', version: '1.0' }\n }\n\n async create_post(post: Post) {\n const conn = await mysql.createConnection(process.env.DATABASE_URL!)\n const [result] = await conn.execute(\n \\`INSERT INTO ${table}\n (title, slug, content, description, status, external_source)\n VALUES (?, ?, ?, ?, ?, ?)\\`,\n [post.title, post.slug, post.body_html,\n post.excerpt || post.seo_meta?.description || '',\n 'published', 'panini'],\n )\n await conn.end()\n const externalId = String((result as { insertId: number }).insertId)\n return {\n external_id: externalId,\n external_url: \\`${slugTemplate}\\`,\n }\n }\n\n async update_post(externalId: string, post: Post) {\n const conn = await mysql.createConnection(process.env.DATABASE_URL!)\n await conn.execute(\n \\`UPDATE ${table} SET title=?, content=?, description=? WHERE id=?\\`,\n [post.title, post.body_html, post.excerpt || '', externalId],\n )\n await conn.end()\n return { external_id: externalId, external_url: \\`${slugTemplate}\\` }\n }\n\n async delete_post(externalId: string) {\n const conn = await mysql.createConnection(process.env.DATABASE_URL!)\n await conn.execute(\\`DELETE FROM ${table} WHERE id=?\\`, [externalId])\n await conn.end()\n }\n}\n`\n}\n\nfunction buildPostNote(\n framework: InstallInput['framework'],\n storage: InstallInput['storage'],\n): string | undefined {\n const parts: string[] = []\n if (storage === 'custom_db') {\n parts.push(\n 'Fill in the 4 methods of MyStorage with your DB queries — swap mysql2 for your driver (mongodb, mssql, cosmos, etc.).',\n )\n }\n if (framework === 'next_pages') {\n parts.push(\n 'You MUST keep the `export const config = { api: { bodyParser: false } }` line — Next\\'s default JSON parser would consume the raw bytes we need for HMAC verification.',\n )\n }\n if (framework === 'express') {\n parts.push(\n 'If you already have `express.json()` mounted globally, move it BELOW the `mount()` call so the SDK can read raw request bytes.',\n )\n }\n return parts.length ? parts.join(' ') : undefined\n}\n\n// ── Python builders ────────────────────────────────────────────────\n\nfunction buildPython(\n framework: InstallInput['framework'],\n storage: InstallInput['storage'],\n table: string,\n urlTemplate: string,\n envVars: string,\n verifyCommand: string,\n): InstallOutput {\n const isCustomDb = storage === 'custom_db'\n const storageExtra = storage === 'memory' || isCustomDb ? '' : `,${storage}`\n // Python isn't published to PyPI yet — install via git URL from the monorepo\n const install =\n `pip install \"panini-connector[${framework}${storageExtra}] @ ` +\n `git+https://github.com/prak16/PaniniOS.git@main#subdirectory=sdk/panini-connector-py\"` +\n (isCustomDb ? '\\npip install pymysql # or motor / pymongo / pymssql' : '')\n\n const storageArg = isCustomDb\n ? 'storage=MyStorage()'\n : storage === 'supabase' || storage === 'postgres'\n ? renderPyExistingTable(storage, table, urlTemplate)\n : `storage=\"${storage}\"`\n\n const customDbClass = isCustomDb ? renderPyCustomDbClass(table, urlTemplate) : ''\n const extraImport =\n isCustomDb\n ? ''\n : storage === 'supabase' || storage === 'postgres'\n ? `\\nfrom panini_connector.storage.${storage} import ${storage === 'supabase' ? 'SupabaseStorage' : 'PostgresStorage'}\\n`\n : '\\n'\n\n let filePath = 'main.py'\n let code = ''\n if (framework === 'fastapi') {\n filePath = 'main.py'\n code = `from fastapi import FastAPI\nfrom panini_connector import mount${extraImport}${customDbClass}\napp = FastAPI()\nmount(\n app,\n path=\"/api/seo-connector\",\n ${storageArg},\n)`\n } else if (framework === 'django') {\n filePath = 'urls.py'\n code = `from django.urls import path\nfrom panini_connector.framework.django import view${extraImport}${customDbClass}\nurlpatterns = [\n # ... your existing routes ...\n path(\n \"api/seo-connector\",\n view(${storageArg}),\n name=\"panini_connector\",\n ),\n]`\n } else {\n // Flask\n filePath = 'app.py'\n code = `from flask import Flask\nfrom panini_connector import mount_flask${extraImport}${customDbClass}\napp = Flask(__name__)\nmount_flask(\n app,\n path=\"/api/seo-connector\",\n ${storageArg},\n)`\n }\n\n const parts: string[] = []\n if (isCustomDb) {\n parts.push(\n 'Fill in the 4 methods of MyStorage with your DB queries — swap pymysql for your driver (motor / pymongo / pymssql / etc.).',\n )\n }\n if (framework === 'django') {\n parts.push('Requires Django 4.1+ for async views. CSRF exemption is handled by the SDK.')\n }\n\n return {\n install_command: install,\n files: [{ path: filePath, content: code, language: 'python' }],\n env_vars: envVars,\n post_install_note: parts.length ? parts.join(' ') : undefined,\n verify_command: verifyCommand,\n }\n}\n\nfunction renderPyExistingTable(\n storage: 'supabase' | 'postgres',\n table: string,\n urlTemplate: string,\n): string {\n const cls = storage === 'supabase' ? 'SupabaseStorage' : 'PostgresStorage'\n return `storage=${cls}(\n table=\"${table}\",\n external_id_column=\"id\",\n external_url_template=\"${urlTemplate}\",\n to_row=lambda post: {\n \"title\": post[\"title\"],\n \"slug\": post[\"slug\"],\n \"content\": post[\"body_html\"],\n \"description\": post.get(\"excerpt\") or post.get(\"seo_meta\", {}).get(\"description\", \"\"),\n \"meta_title\": post.get(\"seo_meta\", {}).get(\"title\") or post[\"title\"],\n \"meta_description\": post.get(\"seo_meta\", {}).get(\"description\") or post.get(\"excerpt\", \"\"),\n \"featured_image_url\": post.get(\"featured_image_url\"),\n \"author_name\": post.get(\"author_name\"),\n \"status\": post.get(\"status\", \"published\"),\n \"category\": \"seo\",\n \"external_source\": \"panini\",\n },\n )`\n}\n\nfunction renderPyCustomDbClass(table: string, urlTemplate: string): string {\n const slugTemplate = urlTemplate.replace('{slug}', '{post[\"slug\"]}')\n return `\nimport os\nimport pymysql\n\n\nclass MyStorage:\n async def ping(self):\n return {\"site_name\": \"${table}\", \"version\": \"1.0\"}\n\n async def create_post(self, post):\n conn = pymysql.connect(host=\"localhost\", user=\"user\", password=\"pass\", db=\"dbname\")\n with conn.cursor() as cur:\n cur.execute(\n \"\"\"INSERT INTO ${table}\n (title, slug, content, description, status, external_source)\n VALUES (%s, %s, %s, %s, %s, %s)\"\"\",\n (post[\"title\"], post[\"slug\"], post[\"body_html\"],\n post.get(\"excerpt\", \"\"), \"published\", \"panini\"),\n )\n external_id = cur.lastrowid\n conn.commit(); conn.close()\n return {\"external_id\": str(external_id), \"external_url\": f\"${slugTemplate}\"}\n\n async def update_post(self, external_id, post):\n conn = pymysql.connect(host=\"localhost\", user=\"user\", password=\"pass\", db=\"dbname\")\n with conn.cursor() as cur:\n cur.execute(\n \"\"\"UPDATE ${table} SET title=%s, content=%s, description=%s WHERE id=%s\"\"\",\n (post[\"title\"], post[\"body_html\"], post.get(\"excerpt\", \"\"), external_id),\n )\n conn.commit(); conn.close()\n return {\"external_id\": external_id, \"external_url\": f\"${slugTemplate}\"}\n\n async def delete_post(self, external_id):\n conn = pymysql.connect(host=\"localhost\", user=\"user\", password=\"pass\", db=\"dbname\")\n with conn.cursor() as cur:\n cur.execute(\"DELETE FROM ${table} WHERE id=%s\", (external_id,))\n conn.commit(); conn.close()\n\n\n`\n}\n\n// ── Env var block (same as wizard) ─────────────────────────────────\n\nfunction envVarsFor(storage: InstallInput['storage']): string {\n const base = \"SEO_CONNECTOR_SECRET=<call generate_shared_secret to get one>\"\n if (storage === 'supabase') {\n return `${base}\nSUPABASE_URL=https://xxxxx.supabase.co\nSUPABASE_SERVICE_ROLE_KEY=<service_role key, NOT anon>`\n }\n if (storage === 'postgres') {\n return `${base}\nDATABASE_URL=postgresql://user:pass@host:5432/dbname`\n }\n if (storage === 'custom_db') {\n return `${base}\n# Whatever env vars YOUR DB driver needs — e.g. for MySQL:\nDATABASE_URL=mysql://user:pass@host:3306/dbname`\n }\n return base + '\\n# (in-memory needs no extra env vars — posts vanish on restart)'\n}\n\n// ── MCP tool registration ──────────────────────────────────────────\n\nexport function registerInstallTool(server: McpServer): void {\n server.registerTool(\n 'install_panini_connector',\n {\n title: 'Install panini-connector on a site',\n description:\n 'Given the customer\\'s stack (language + framework + storage), returns the ' +\n 'exact commands, code, and env vars to wire panini-connector into their ' +\n 'self-hosted site. The AI agent should: (1) call this tool, (2) run the ' +\n 'install_command in the project root, (3) write each file to the path shown, ' +\n '(4) prompt the user to set the env_vars on their hosting platform, (5) tell ' +\n 'them to redeploy, then (6) call verify_panini_connector to confirm the ' +\n 'setup works end-to-end.',\n inputSchema: {\n language: z\n .enum(['node', 'python'])\n .describe('The customer site\\'s runtime language.'),\n framework: z\n .enum(['next_app', 'next_pages', 'express', 'fastapi', 'django', 'flask'])\n .describe(\n 'The customer\\'s web framework. next_app = Next.js App Router, next_pages = Next.js Pages Router. ' +\n 'Must match `language`: node → next_app|next_pages|express; python → fastapi|django|flask.',\n ),\n storage: z\n .enum(['supabase', 'postgres', 'custom_db', 'memory'])\n .describe(\n 'Where posts land. supabase = Supabase via PostgREST. postgres = direct Postgres DSN. ' +\n 'custom_db = MySQL/Mongo/MSSQL/CosmosDB/CMS — SDK generates a StorageAdapter class scaffold. ' +\n 'memory = ephemeral in-memory (testing only, posts vanish on restart).',\n ),\n existing_table: z\n .string()\n .optional()\n .describe(\n 'Name of the customer\\'s existing blog/posts table (e.g. \"blog_posts\"). ' +\n 'For supabase/postgres, seeds the toRow mapping. For custom_db, seeds the SQL. ' +\n 'Defaults to \"blog_posts\".',\n ),\n url_template: z\n .string()\n .optional()\n .describe(\n 'URL where published posts appear on the customer site. Use {slug} and/or {id} placeholders. ' +\n 'Example: \"https://mysite.com/blog/{slug}\". Defaults to \"https://yoursite.com/blog/{slug}\".',\n ),\n },\n outputSchema: {\n install_command: z.string(),\n files: z.array(\n z.object({\n path: z.string(),\n content: z.string(),\n language: z.string(),\n }),\n ),\n env_vars: z.string(),\n post_install_note: z.string().optional(),\n verify_command: z.string(),\n },\n },\n async (args) => {\n const result = buildInstall(args as InstallInput)\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n structuredContent: result as unknown as Record<string, unknown>,\n }\n },\n )\n}\n","/**\n * verify_panini_connector — end-to-end smoke test of a deployed\n * connector endpoint. Fires all 4 signed HTTP ops (ping, create_post,\n * update_post, delete_post) against the URL, returns per-op status\n * with diagnostic detail on failure.\n *\n * This is what the panini-connector CLI's `test` subcommand does, but\n * exposed as an MCP tool so an AI agent can call it programmatically\n * after installing the SDK.\n */\nimport { createHmac } from 'node:crypto'\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { z } from 'zod'\n\nexport interface VerifyInput {\n url: string\n secret: string\n timeout_ms?: number\n}\n\nexport interface OpResult {\n op: 'ping' | 'create_post' | 'update_post' | 'delete_post'\n status: 'pass' | 'fail'\n http_status?: number\n response_body?: unknown\n error?: string\n}\n\nexport interface VerifyOutput {\n ok: boolean\n url: string\n ops: OpResult[]\n summary: string\n}\n\nconst VERSION = '1'\n\nfunction computeSignature(secret: string, timestamp: string, rawBody: string): string {\n return 'sha256=' + createHmac('sha256', secret)\n .update(timestamp + '.' + rawBody, 'utf-8')\n .digest('hex')\n}\n\nasync function fireOp(\n url: string,\n secret: string,\n body: Record<string, unknown>,\n timeoutMs: number,\n): Promise<{ httpStatus: number; body: unknown; error?: string }> {\n const raw = JSON.stringify(body)\n const ts = String(Math.floor(Date.now() / 1000))\n const sig = computeSignature(secret, ts, raw)\n\n const controller = new AbortController()\n const timeoutHandle = setTimeout(() => controller.abort(), timeoutMs)\n try {\n const r = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Compass-Timestamp': ts,\n 'X-Compass-Signature': sig,\n 'X-Compass-Version': VERSION,\n },\n body: raw,\n signal: controller.signal,\n })\n clearTimeout(timeoutHandle)\n let parsed: unknown\n const text = await r.text()\n try {\n parsed = JSON.parse(text)\n } catch {\n parsed = text.slice(0, 500)\n }\n return { httpStatus: r.status, body: parsed }\n } catch (e) {\n clearTimeout(timeoutHandle)\n const msg = e instanceof Error ? e.message : String(e)\n return { httpStatus: 0, body: null, error: msg }\n }\n}\n\nfunction newRequestId(): string {\n return 'mcp-verify-' + Date.now().toString(16) + '-' + Math.floor(Math.random() * 1e6).toString(16)\n}\n\nexport async function runVerify(input: VerifyInput): Promise<VerifyOutput> {\n const url = input.url\n const secret = input.secret\n const timeoutMs = input.timeout_ms ?? 10_000\n\n const ops: OpResult[] = []\n let externalId: string | undefined\n const slug = `panini-mcp-smoke-${Date.now().toString(36)}`\n\n // 1. ping\n {\n const req = { op: 'ping', version: VERSION, request_id: newRequestId() }\n const r = await fireOp(url, secret, req, timeoutMs)\n const passed = r.httpStatus === 200\n ops.push({\n op: 'ping',\n status: passed ? 'pass' : 'fail',\n http_status: r.httpStatus,\n response_body: r.body,\n error: r.error,\n })\n if (!passed) {\n return summarize(url, ops) // don't try further ops if ping already failed\n }\n }\n\n // 2. create_post\n {\n const req = {\n op: 'create_post', version: VERSION, request_id: newRequestId(),\n timestamp: new Date().toISOString(),\n post: {\n title: 'MCP verify smoke test',\n slug,\n body_html: '<p>Auto-generated by verify_panini_connector. Safe to delete.</p>',\n excerpt: 'MCP smoke test',\n categories: [], tags: [],\n featured_image_url: '', author_name: 'panini-connector-mcp',\n seo_meta: { title: 'MCP smoke', description: 'MCP smoke' },\n json_ld: [], status: 'draft',\n },\n }\n const r = await fireOp(url, secret, req, timeoutMs)\n const passed = r.httpStatus === 200\n const body = r.body as { external_id?: string } | null\n if (passed && body?.external_id) externalId = body.external_id\n ops.push({\n op: 'create_post',\n status: passed ? 'pass' : 'fail',\n http_status: r.httpStatus,\n response_body: r.body,\n error: r.error,\n })\n if (!passed || !externalId) return summarize(url, ops)\n }\n\n // 3. update_post\n {\n const req = {\n op: 'update_post', version: VERSION, request_id: newRequestId(),\n timestamp: new Date().toISOString(),\n external_id: externalId,\n post: {\n title: 'MCP verify smoke test (updated)',\n slug, body_html: '<p>Updated. Safe to delete.</p>',\n excerpt: 'updated', categories: [], tags: [],\n featured_image_url: '', author_name: 'panini-connector-mcp',\n seo_meta: {}, json_ld: [], status: 'draft',\n },\n }\n const r = await fireOp(url, secret, req, timeoutMs)\n ops.push({\n op: 'update_post',\n status: r.httpStatus === 200 ? 'pass' : 'fail',\n http_status: r.httpStatus,\n response_body: r.body,\n error: r.error,\n })\n }\n\n // 4. delete_post (cleanup — always attempt even if update failed)\n {\n const req = {\n op: 'delete_post', version: VERSION, request_id: newRequestId(),\n timestamp: new Date().toISOString(),\n external_id: externalId,\n post: {},\n }\n const r = await fireOp(url, secret, req, timeoutMs)\n ops.push({\n op: 'delete_post',\n status: r.httpStatus === 200 ? 'pass' : 'fail',\n http_status: r.httpStatus,\n response_body: r.body,\n error: r.error,\n })\n }\n\n return summarize(url, ops)\n}\n\nfunction summarize(url: string, ops: OpResult[]): VerifyOutput {\n const passes = ops.filter((o) => o.status === 'pass').length\n const fails = ops.length - passes\n const ok = fails === 0 && ops.length === 4\n const summary = ok\n ? `✓ All 4 ops passed against ${url}. Connector is wired correctly.`\n : `✗ ${fails}/${ops.length} ops failed against ${url}. See per-op detail.`\n return { ok, url, ops, summary }\n}\n\nexport function registerVerifyTool(server: McpServer): void {\n server.registerTool(\n 'verify_panini_connector',\n {\n title: 'Verify a deployed panini-connector endpoint',\n description:\n 'Fire all 4 HMAC-signed operations (ping, create_post, update_post, delete_post) at ' +\n 'the customer\\'s deployed connector URL. Returns per-op status + diagnostics. Call this ' +\n 'AFTER install_panini_connector\\'s code has been written + env vars set + site redeployed, ' +\n 'to confirm the round-trip works. If any op fails, the response_body will contain the ' +\n 'customer\\'s handler error text — enough to debug (wrong secret? missing table column? RLS ' +\n 'policy? etc.). Cleans up after itself by deleting the smoke-test post.',\n inputSchema: {\n url: z\n .string()\n .url()\n .describe(\n 'The full HTTPS URL of the deployed connector endpoint. Must end with the mount path ' +\n '(usually \"/api/seo-connector\"). Example: \"https://mysite.com/api/seo-connector\"',\n ),\n secret: z\n .string()\n .min(8)\n .describe(\n 'The SEO_CONNECTOR_SECRET value set as an env var on the customer site. Must match ' +\n 'byte-for-byte or all signatures will 401.',\n ),\n timeout_ms: z\n .number()\n .int()\n .min(1000)\n .max(60_000)\n .optional()\n .describe('Per-op HTTP timeout in ms. Default 10000.'),\n },\n outputSchema: {\n ok: z.boolean(),\n url: z.string(),\n ops: z.array(\n z.object({\n op: z.enum(['ping', 'create_post', 'update_post', 'delete_post']),\n status: z.enum(['pass', 'fail']),\n http_status: z.number().optional(),\n response_body: z.any().optional(),\n error: z.string().optional(),\n }),\n ),\n summary: z.string(),\n },\n },\n async (args) => {\n const result = await runVerify(args as VerifyInput)\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n structuredContent: result as unknown as Record<string, unknown>,\n }\n },\n )\n}\n","/**\n * generate_shared_secret — returns a 32-byte crypto-random hex string\n * suitable for use as SEO_CONNECTOR_SECRET on the customer's site\n * (must match the value Panini stores server-side for HMAC signing).\n */\nimport { randomBytes } from 'node:crypto'\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { z } from 'zod'\n\nexport function generateSharedSecret(): { secret: string; length_bytes: number } {\n const secret = randomBytes(32).toString('hex')\n return { secret, length_bytes: 32 }\n}\n\nexport function registerSecretTool(server: McpServer): void {\n server.registerTool(\n 'generate_shared_secret',\n {\n title: 'Generate shared secret',\n description:\n 'Generate a cryptographically-random 32-byte hex secret. Use this ' +\n 'as the SEO_CONNECTOR_SECRET env var on the customer site AND paste ' +\n 'the same value into the Panini dashboard \"Shared secret\" field — ' +\n 'both sides must match for HMAC signatures to verify.',\n inputSchema: {},\n outputSchema: {\n secret: z.string().describe('64-character hex string (32 raw bytes)'),\n length_bytes: z.number().describe('Number of raw bytes before hex-encoding'),\n },\n },\n async () => {\n const result = generateSharedSecret()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n structuredContent: result as unknown as Record<string, unknown>,\n }\n },\n )\n}\n","/**\n * get_envelope_schema — returns the raw HTTP envelope contract Panini\n * uses to POST to the customer's connector endpoint. AI agents use this\n * when the customer's stack isn't Node or Python (Laravel, Rails, Go,\n * .NET, etc.) and they need to hand-implement the handler in the\n * customer's native language.\n */\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { z } from 'zod'\n\nexport const ENVELOPE_CONTRACT = {\n http_method: 'POST',\n request_headers: {\n 'Content-Type': 'application/json',\n 'X-Compass-Timestamp': '<unix seconds, string>',\n 'X-Compass-Signature':\n 'sha256=<64-hex-chars, HMAC-SHA256 over `${timestamp}.${raw_body}` with SEO_CONNECTOR_SECRET>',\n 'X-Compass-Version': '1',\n },\n request_body_shape: {\n op: 'create_post | update_post | delete_post | ping',\n version: '1',\n request_id: '<uuid>',\n timestamp: '<ISO-8601 datetime>',\n post: {\n title: 'string',\n slug: 'string (URL-safe)',\n body_html: 'string (rendered HTML)',\n excerpt: 'string (short summary)',\n categories: '[string]',\n tags: '[string]',\n featured_image_url: 'string (absolute URL) | null',\n author_name: 'string | null',\n seo_meta: { title: 'string', description: 'string' },\n json_ld: '[object] (Schema.org structured data)',\n status: 'publish | draft',\n },\n external_id: '<string, only for update_post + delete_post>',\n },\n response_success: {\n http_status: 200,\n body: {\n ok: true,\n external_id: '<your DB primary key for this post>',\n external_url: '<publicly-viewable URL, e.g. https://mysite.com/blog/slug>',\n },\n },\n response_ping: {\n http_status: 200,\n body: {\n ok: true,\n site_name: '<display name>',\n version: '1',\n supported_ops: ['ping', 'create_post', 'update_post', 'delete_post'],\n },\n },\n response_errors: {\n signature_fail: { http_status: 401, body: { ok: false, error: 'signature verification failed' } },\n bad_envelope: { http_status: 400, body: { ok: false, error: '<parse error detail>' } },\n unknown_post: { http_status: 404, body: { ok: false, error: 'no post with external_id=<id>' } },\n internal_error: { http_status: 500, body: { ok: false, error: '<internal>' } },\n },\n replay_window_seconds: 300,\n hmac_pseudocode:\n 'expected = \"sha256=\" + HMAC_SHA256(SEO_CONNECTOR_SECRET, timestamp + \".\" + raw_body).hex_lower()\\n' +\n 'if not constant_time_equal(expected, X-Compass-Signature): reject 401\\n' +\n 'if abs(now_unix_seconds - int(timestamp)) > 300: reject 401',\n laravel_example: `// PHP / Laravel — verify signature in a controller method\n$ts = $request->header('X-Compass-Timestamp');\n$sig = $request->header('X-Compass-Signature');\n$body = $request->getContent(); // raw bytes, before any JSON parsing\n$expect = 'sha256=' . hash_hmac('sha256', \"$ts.$body\", env('SEO_CONNECTOR_SECRET'));\nif (!hash_equals($expect, $sig)) return response()->json(['ok' => false], 401);\nif (abs(time() - (int)$ts) > 300) return response()->json(['ok' => false], 401);\n$payload = json_decode($body, true);\n// ... route on $payload['op'], write to your DB, respond {ok, external_id, external_url}`,\n}\n\nexport function getEnvelopeSchema(): typeof ENVELOPE_CONTRACT {\n return ENVELOPE_CONTRACT\n}\n\nexport function registerSchemaTool(server: McpServer): void {\n server.registerTool(\n 'get_envelope_schema',\n {\n title: 'Get envelope schema',\n description:\n 'Return the raw HTTP envelope contract Panini uses to POST to customer sites. ' +\n 'Use this when the customer stack is NOT Node or Python (e.g. Laravel, Rails, ' +\n 'Go, .NET, Rust) and you need to hand-implement the connector in their language. ' +\n 'Includes request/response shapes, HMAC verification pseudocode, and a Laravel example.',\n inputSchema: {},\n outputSchema: {\n http_method: z.string(),\n request_headers: z.record(z.string()),\n request_body_shape: z.record(z.any()),\n response_success: z.record(z.any()),\n response_ping: z.record(z.any()),\n response_errors: z.record(z.any()),\n replay_window_seconds: z.number(),\n hmac_pseudocode: z.string(),\n laravel_example: z.string(),\n },\n },\n async () => {\n const result = getEnvelopeSchema()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n structuredContent: result as unknown as Record<string, unknown>,\n }\n },\n )\n}\n"],"mappings":"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","names":["import_zod","import_node_crypto","import_zod","import_zod"]}
|
package/dist/index.d.cts
ADDED
package/dist/index.d.ts
ADDED