webcake-landing-mcp 1.0.25 → 1.0.26

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.
@@ -1,4 +1,11 @@
1
1
  [
2
+ {
3
+ "v": "1.0.26",
4
+ "d": "07/06/2026",
5
+ "type": "Added",
6
+ "en": "The HTTP server now serves a pre-rendered 1200×630 PNG social card at GET /og.png; the guide page's og:image and twitter:image meta tags now point…",
7
+ "vi": "HTTP server nay phục vụ ảnh social card PNG được render sẵn 1200×630 tại GET /og.png; các meta tag og:image và twitter:image của trang hướng dẫn nay…"
8
+ },
2
9
  {
3
10
  "v": "1.0.25",
4
11
  "d": "07/06/2026",
@@ -33,12 +40,5 @@
33
40
  "type": "Added",
34
41
  "en": "The HTTP server's GET / guide page is now bilingual (vi/en): append ?lang=en to the URL to switch to English (default is Vietnamese), a language…",
35
42
  "vi": "Trang hướng dẫn GET / của HTTP server nay hỗ trợ song ngữ (vi/en): thêm ?lang=en vào URL để chuyển sang tiếng Anh (mặc định là tiếng Việt), kèm nút…"
36
- },
37
- {
38
- "v": "1.0.20",
39
- "d": "07/06/2026",
40
- "type": "Changed",
41
- "en": "get_generation_guide now includes a Layout Archetypes block that maps seven page types (sales/COD, lead-gen/service, event/invitation, app/SaaS…",
42
- "vi": "get_generation_guide now includes a Layout Archetypes block that maps seven page types (sales/COD, lead-gen/service, event/invitation, app/SaaS…"
43
43
  }
44
44
  ]
package/dist/http.js CHANGED
@@ -12,6 +12,7 @@
12
12
  * All logging stays on stderr (console.error), same as stdio mode.
13
13
  */
14
14
  import { randomUUID } from "node:crypto";
15
+ import { readFileSync } from "node:fs";
15
16
  import { createServer as createHttpServer } from "node:http";
16
17
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
17
18
  import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
@@ -19,6 +20,21 @@ import { createServer } from "./server.js";
19
20
  import { ICON_SVG, ICON_MIME } from "./branding.js";
20
21
  import { guideHtml, ogImageSvg, normalizeLang } from "./web-guide.js";
21
22
  const MCP_PATH = "/mcp";
23
+ // The raster social card (1200x630), pre-rendered and committed at src/og.png,
24
+ // mirrored to dist/og.png by copy-assets. Served at GET /og.png as the og:image —
25
+ // SVG OG images don't unfurl on Facebook/X/LinkedIn/Zalo. Read once, lazily.
26
+ let OG_PNG = null;
27
+ function ogImagePng() {
28
+ if (OG_PNG)
29
+ return OG_PNG;
30
+ try {
31
+ OG_PNG = readFileSync(new URL("./og.png", import.meta.url));
32
+ return OG_PNG;
33
+ }
34
+ catch {
35
+ return null;
36
+ }
37
+ }
22
38
  function sendJson(res, status, body) {
23
39
  res.writeHead(status, { "content-type": "application/json" });
24
40
  res.end(JSON.stringify(body));
@@ -75,6 +91,18 @@ export async function startHttpServer(port) {
75
91
  return res.end(ICON_SVG);
76
92
  }
77
93
  // Social-card image referenced by the landing page's og:image / twitter:image.
94
+ // PNG is the canonical og:image (unfurls everywhere); the SVG stays for clients
95
+ // that prefer it (Slack/Telegram/Discord) and as a fallback if og.png is absent.
96
+ if (req.method === "GET" && path === "/og.png") {
97
+ const png = ogImagePng();
98
+ if (png) {
99
+ res.writeHead(200, { "content-type": "image/png", "cache-control": "public, max-age=86400" });
100
+ return res.end(png);
101
+ }
102
+ // Fall back to the SVG card if the raster asset didn't ship.
103
+ res.writeHead(200, { "content-type": ICON_MIME, "cache-control": "public, max-age=86400" });
104
+ return res.end(ogImageSvg());
105
+ }
78
106
  if (req.method === "GET" && path === "/og.svg") {
79
107
  res.writeHead(200, { "content-type": ICON_MIME, "cache-control": "public, max-age=86400" });
80
108
  return res.end(ogImageSvg());
package/dist/og.png ADDED
Binary file
package/dist/web-guide.js CHANGED
@@ -16,7 +16,9 @@
16
16
  * It's also built to be *shareable*: a full SEO `<head>` (description, canonical,
17
17
  * Open Graph, Twitter Card, JSON-LD for SoftwareApplication + WebSite + FAQPage)
18
18
  * so links unfurl nicely on social/chat and the page can be indexed. The social
19
- * card image is served separately at `/og.svg` (see `ogImageSvg`, wired in http.ts).
19
+ * card image is a pre-rendered 1200x630 PNG at `/og.png` (committed src/og.png,
20
+ * regenerated by scripts/render-og.mjs from `ogImageSvg`) — PNG because SVG OG
21
+ * images don't unfurl on Facebook/X/LinkedIn/Zalo. `/og.svg` is still served too.
20
22
  *
21
23
  * Self-contained (inline CSS + the Webcake icon, no external assets/fonts/trackers)
22
24
  * so it loads instantly and leaks nothing.
@@ -333,7 +335,8 @@ export function guideHtml(origin, lang = "vi") {
333
335
  const m = META[L];
334
336
  const faq = FAQ[L];
335
337
  const endpoint = `${origin}/mcp`;
336
- const ogImage = `${origin}/og.svg`;
338
+ // PNG (not SVG) so the card unfurls on Facebook/X/LinkedIn/Zalo; served by http.ts.
339
+ const ogImage = `${origin}/og.png`;
337
340
  const selfPath = L === "en" ? "?lang=en" : "/";
338
341
  const otherLang = L === "vi" ? "en" : "vi";
339
342
  const otherHref = otherLang === "en" ? "?lang=en" : "?lang=vi";
@@ -396,14 +399,17 @@ export function guideHtml(origin, lang = "vi") {
396
399
  <meta property="og:description" content="${m.desc}">
397
400
  <meta property="og:url" content="${canonical}">
398
401
  <meta property="og:image" content="${ogImage}">
402
+ <meta property="og:image:type" content="image/png">
399
403
  <meta property="og:image:width" content="1200">
400
404
  <meta property="og:image:height" content="630">
405
+ <meta property="og:image:alt" content="${m.title}">
401
406
  <meta property="og:locale" content="${m.locale}">
402
407
  <meta property="og:locale:alternate" content="${META[otherLang].locale}">
403
408
  <meta name="twitter:card" content="summary_large_image">
404
409
  <meta name="twitter:title" content="${m.title}">
405
410
  <meta name="twitter:description" content="${m.desc}">
406
411
  <meta name="twitter:image" content="${ogImage}">
412
+ <meta name="twitter:image:alt" content="${m.title}">
407
413
  <script type="application/ld+json">${jsonLdScript}</script>
408
414
  <style>
409
415
  /* Light defaults. Dark applies via OS preference OR a forced [data-theme="dark"]
@@ -751,11 +757,13 @@ export function guideHtml(origin, lang = "vi") {
751
757
  </body></html>`;
752
758
  }
753
759
  /**
754
- * The social-card image served at `/og.svg` and referenced by the page's
755
- * `og:image` / `twitter:image`. A self-contained 1200×630 branded SVG (the size
756
- * social scrapers expect) no external fonts/assets. Slack, Telegram, LinkedIn,
757
- * Discord render SVG OG images; a few (older Twitter/Facebook) may skip it, which
758
- * is acceptable for a dependency-free server.
760
+ * The social-card source: a self-contained 1200×630 branded SVG (the size social
761
+ * scrapers expect) no external fonts/assets. This is the SOURCE that
762
+ * scripts/render-og.mjs rasterizes into the committed src/og.png used as the
763
+ * canonical `og:image` / `twitter:image` (PNG unfurls on Facebook/X/LinkedIn/Zalo
764
+ * where SVG doesn't). Still served verbatim at `/og.svg` for SVG-friendly clients
765
+ * (Slack/Telegram/Discord) and as the http.ts fallback. EDIT THIS, then re-run
766
+ * scripts/render-og.mjs and commit the regenerated PNG.
759
767
  */
760
768
  export function ogImageSvg() {
761
769
  return `<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630" viewBox="0 0 1200 630" fill="none" font-family="system-ui,-apple-system,Segoe UI,Roboto,sans-serif">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webcake-landing-mcp",
3
- "version": "1.0.25",
3
+ "version": "1.0.26",
4
4
  "description": "MCP server exposing Webcake landing-page element schemas + AI usage hints, and persisting LLM-generated page sources to a Webcake backend.",
5
5
  "type": "module",
6
6
  "bin": {