@formfeed/mcp 0.3.12 → 0.3.13

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.
@@ -70689,7 +70689,10 @@ var barcodeTypes = {
70689
70689
  var ownTextLayout = /* @__PURE__ */ new Set(["ean13", "ean8", "upca"]);
70690
70690
  function codeText(helper, value) {
70691
70691
  const text4 = value === void 0 || value === null ? "" : String(value);
70692
- if (text4 === "") throw new Error(`${helper}: nothing to encode, the value is empty or missing from the data`);
70692
+ if (text4 === "")
70693
+ throw new Error(
70694
+ `${helper}: nothing to encode, the value is empty or missing from the data`
70695
+ );
70693
70696
  return text4;
70694
70697
  }
70695
70698
  var codeHelpers = [
@@ -70698,18 +70701,25 @@ var codeHelpers = [
70698
70701
  aliases: ["qr"],
70699
70702
  doc: {
70700
70703
  signature: "qrcode(value, { size = 160, ecc = 'M', margin = 1, color, background })",
70701
- description: "QR code as an SVG data URI for an <img> src.",
70704
+ description: "QR code as an SVG data URI for an <img> src. Give the image an `alt` that says where the code leads: a screen reader cannot scan it.",
70702
70705
  examples: {
70703
- jinja2: '<img src="{{ qrcode(order.url, { size: 120 }) }}">',
70704
- liquid: '<img src="{{ order.url | qrcode: size: 120 }}">',
70705
- handlebars: '<img src="{{qrcode order.url size=120}}">'
70706
+ jinja2: '<img src="{{ qrcode(order.url, { size: 120 }) }}" alt="QR code: {{ order.url }}">',
70707
+ liquid: '<img src="{{ order.url | qrcode: size: 120 }}" alt="QR code: {{ order.url }}">',
70708
+ handlebars: '<img src="{{qrcode order.url size=120}}" alt="QR code: {{order.url}}">'
70706
70709
  },
70707
70710
  category: "code"
70708
70711
  },
70709
70712
  fn: (ctx, value, options) => {
70710
70713
  const o = options ?? {};
70711
70714
  const svg = qrSvg(codeText("qrcode", value), o);
70712
- if (ctx.drawing) return ctx.drawing({ kind: "svg", svg, width: o.size, height: o.size, alt: "QR code" });
70715
+ if (ctx.drawing)
70716
+ return ctx.drawing({
70717
+ kind: "svg",
70718
+ svg,
70719
+ width: o.size,
70720
+ height: o.size,
70721
+ alt: "QR code"
70722
+ });
70713
70723
  return svgDataUri(svg);
70714
70724
  }
70715
70725
  },
@@ -70717,11 +70727,11 @@ var codeHelpers = [
70717
70727
  name: "barcode",
70718
70728
  doc: {
70719
70729
  signature: "barcode(value, { type = 'code128', height = 12, width, text = true, scale = 2 })",
70720
- description: "Barcode (code128, ean13, ean8, upc, itf14, code39, datamatrix, pdf417) as an SVG data URI.",
70730
+ description: "Barcode (code128, ean13, ean8, upc, itf14, code39, datamatrix, pdf417) as an SVG data URI. Give the image an `alt` with the value it encodes.",
70721
70731
  examples: {
70722
- jinja2: `<img src="{{ barcode(item.sku, { type: 'code128' }) }}">`,
70723
- liquid: `<img src="{{ item.sku | barcode: type: 'code128' }}">`,
70724
- handlebars: `<img src="{{barcode item.sku type='code128'}}">`
70732
+ jinja2: `<img src="{{ barcode(item.sku, { type: 'code128' }) }}" alt="Barcode {{ item.sku }}">`,
70733
+ liquid: `<img src="{{ item.sku | barcode: type: 'code128' }}" alt="Barcode {{ item.sku }}">`,
70734
+ handlebars: `<img src="{{barcode item.sku type='code128'}}" alt="Barcode {{item.sku}}">`
70725
70735
  },
70726
70736
  category: "code"
70727
70737
  },
@@ -70738,7 +70748,8 @@ var codeHelpers = [
70738
70748
  includetext: o.text !== false,
70739
70749
  ...ownTextLayout.has(bcid) ? {} : { textxalign: "center" }
70740
70750
  });
70741
- if (ctx.drawing) return ctx.drawing({ kind: "svg", svg, alt: `Barcode ${text4}` });
70751
+ if (ctx.drawing)
70752
+ return ctx.drawing({ kind: "svg", svg, alt: `Barcode ${text4}` });
70742
70753
  return svgDataUri(svg);
70743
70754
  }
70744
70755
  },
@@ -70747,11 +70758,11 @@ var codeHelpers = [
70747
70758
  aliases: ["epc_qr", "girocode"],
70748
70759
  doc: {
70749
70760
  signature: "epcQr({ name, iban, bic?, amount?, reference?, text? }, { size })",
70750
- description: "SEPA credit transfer QR code (EPC069-12 / GiroCode) that banking apps scan to prefill a payment.",
70761
+ description: "SEPA credit transfer QR code (EPC069-12 / GiroCode) that banking apps scan to prefill a payment. Give the image an `alt`, and print the bank details as text as well: the code is a picture.",
70751
70762
  examples: {
70752
- jinja2: '<img src="{{ epcQr({ name: company.name, iban: company.iban, amount: invoice.total, reference: invoice.number }) }}">',
70753
- liquid: '<img src="{{ company.iban | epcQr: name: company.name, amount: invoice.total, reference: invoice.number }}">',
70754
- handlebars: '<img src="{{epcQr name=company.name iban=company.iban amount=invoice.total reference=invoice.number}}">'
70763
+ jinja2: '<img src="{{ epcQr({ name: company.name, iban: company.iban, amount: invoice.total, reference: invoice.number }) }}" alt="QR code to pay invoice {{ invoice.number }}">',
70764
+ liquid: '<img src="{{ company.iban | epcQr: name: company.name, amount: invoice.total, reference: invoice.number }}" alt="QR code to pay invoice {{ invoice.number }}">',
70765
+ handlebars: '<img src="{{epcQr name=company.name iban=company.iban amount=invoice.total reference=invoice.number}}" alt="QR code to pay invoice {{invoice.number}}">'
70755
70766
  },
70756
70767
  category: "code"
70757
70768
  },
@@ -70770,12 +70781,27 @@ var codeHelpers = [
70770
70781
  (EPC_FIELDS.has(key) ? payment2 : qr)[key] = value;
70771
70782
  const o = { ecc: "M", ...qr };
70772
70783
  const svg = qrSvg(epcPayload(payment2), o);
70773
- if (ctx.drawing) return ctx.drawing({ kind: "svg", svg, width: o.size, height: o.size, alt: "GiroCode" });
70784
+ if (ctx.drawing)
70785
+ return ctx.drawing({
70786
+ kind: "svg",
70787
+ svg,
70788
+ width: o.size,
70789
+ height: o.size,
70790
+ alt: "GiroCode"
70791
+ });
70774
70792
  return svgDataUri(svg);
70775
70793
  }
70776
70794
  }
70777
70795
  ];
70778
- var EPC_FIELDS = /* @__PURE__ */ new Set(["name", "iban", "bic", "amount", "reference", "text", "purpose"]);
70796
+ var EPC_FIELDS = /* @__PURE__ */ new Set([
70797
+ "name",
70798
+ "iban",
70799
+ "bic",
70800
+ "amount",
70801
+ "reference",
70802
+ "text",
70803
+ "purpose"
70804
+ ]);
70779
70805
 
70780
70806
  // node_modules/.pnpm/@date-fns+tz@1.5.0/node_modules/@date-fns/tz/constants/index.js
70781
70807
  var constructFromSymbol = Symbol.for("constructDateFrom");
@@ -78352,7 +78378,16 @@ var collectionHelpers = [
78352
78378
  ];
78353
78379
 
78354
78380
  // packages/engine/src/lib/helpers/document.ts
78355
- var CHART_PALETTE = ["#3E63DD", "#12A594", "#E5484D", "#F76B15", "#8E4EC6", "#FFC53D", "#0090FF", "#978365"];
78381
+ var CHART_PALETTE = [
78382
+ "#3E63DD",
78383
+ "#12A594",
78384
+ "#E5484D",
78385
+ "#F76B15",
78386
+ "#8E4EC6",
78387
+ "#FFC53D",
78388
+ "#0090FF",
78389
+ "#978365"
78390
+ ];
78356
78391
  var SLICED = /* @__PURE__ */ new Set(["pie", "doughnut", "polarArea"]);
78357
78392
  var colour = (value) => typeof value === "string" && value ? value : Array.isArray(value) && value.length ? value.map(String) : void 0;
78358
78393
  function chartDataFromPlain(spec) {
@@ -78366,8 +78401,13 @@ function chartDataFromPlain(spec) {
78366
78401
  });
78367
78402
  const own = colour(s.color);
78368
78403
  const fill = SLICED.has(type) ? own ?? labels.map((_2, i) => CHART_PALETTE[i % CHART_PALETTE.length]) : own ?? CHART_PALETTE[index % CHART_PALETTE.length];
78369
- const dataset = { data, backgroundColor: fill, borderColor: fill };
78370
- if (s.label !== void 0 && s.label !== null && s.label !== "") dataset["label"] = String(s.label);
78404
+ const dataset = {
78405
+ data,
78406
+ backgroundColor: fill,
78407
+ borderColor: fill
78408
+ };
78409
+ if (s.label !== void 0 && s.label !== null && s.label !== "")
78410
+ dataset["label"] = String(s.label);
78371
78411
  if (type === "line") dataset["fill"] = false;
78372
78412
  return dataset;
78373
78413
  });
@@ -78384,7 +78424,8 @@ function chartPayload(spec) {
78384
78424
  const options = { ...spec.options ?? {} };
78385
78425
  options["animation"] = false;
78386
78426
  options["responsive"] = false;
78387
- if (!isPlain(spec)) return { type: spec.type ?? "bar", data: spec.data ?? {}, options };
78427
+ if (!isPlain(spec))
78428
+ return { type: spec.type ?? "bar", data: spec.data ?? {}, options };
78388
78429
  const data = chartDataFromPlain(spec);
78389
78430
  const unnamed = data.datasets.length === 1 && data.datasets[0]?.["label"] === void 0;
78390
78431
  const plugins = asObject(options["plugins"]);
@@ -78397,7 +78438,8 @@ function chartMarkup(spec, id) {
78397
78438
  const height = toNumber(spec.height ?? 240) || 240;
78398
78439
  const payload = escapeHtml(JSON.stringify(chartPayload(spec)));
78399
78440
  const idAttr = id ? ` id="${escapeHtml(id)}"` : "";
78400
- return `<canvas class="ff-chart"${idAttr} width="${width}" height="${height}" style="width:${width}px;height:${height}px" data-ff-chart="${payload}"></canvas>`;
78441
+ const alt = typeof spec.alt === "string" && spec.alt.trim() ? ` role="img" aria-label="${escapeHtml(spec.alt.trim())}"` : "";
78442
+ return `<canvas class="ff-chart"${idAttr}${alt} width="${width}" height="${height}" style="width:${width}px;height:${height}px" data-ff-chart="${payload}"></canvas>`;
78401
78443
  }
78402
78444
  function imageMarkup(url, options = {}) {
78403
78445
  const styles = [
@@ -78418,18 +78460,21 @@ var documentHelpers = [
78418
78460
  name: "chart",
78419
78461
  html: true,
78420
78462
  doc: {
78421
- signature: "chart({ type, labels, values, label, color } | { type, labels, series } | { type, data, options }, { width, height })",
78422
- description: "Draws a Chart.js chart (bar, line, pie, doughnut, radar, \u2026) from plain data, `labels` with `values` or several `series` of `{ label, values, color }`, or from a full Chart.js `data` and `options`; animations are disabled so PDF and preview match.",
78463
+ signature: "chart({ type, labels, values, label, color } | { type, labels, series } | { type, data, options }, { width, height, alt })",
78464
+ description: "Draws a Chart.js chart (bar, line, pie, doughnut, radar, \u2026) from plain data, `labels` with `values` or several `series` of `{ label, values, color }`, or from a full Chart.js `data` and `options`; animations are disabled so PDF and preview match. `alt` says in words what the chart shows: a screen reader reads it in place of the picture, and a tagged PDF needs it.",
78423
78465
  examples: {
78424
- jinja2: "{{ chart({ type: 'line', labels: sales.labels, values: sales.quarters, width: 480 }) }}",
78425
- liquid: "{{ '' | chart: type: 'line', labels: sales.labels, values: sales.quarters, width: 480 }}",
78426
- handlebars: "{{chart type='line' labels=sales.labels values=sales.quarters width=480}}"
78466
+ jinja2: "{{ chart({ type: 'line', labels: sales.labels, values: sales.quarters, width: 480, alt: 'Sales by quarter' }) }}",
78467
+ liquid: "{{ '' | chart: type: 'line', labels: sales.labels, values: sales.quarters, width: 480, alt: 'Sales by quarter' }}",
78468
+ handlebars: "{{chart type='line' labels=sales.labels values=sales.quarters width=480 alt='Sales by quarter'}}"
78427
78469
  },
78428
78470
  category: "document"
78429
78471
  },
78430
78472
  fn: (ctx, spec, extra) => {
78431
78473
  if (ctx.mode === "office") throw new Error(officeUnsupported("chart"));
78432
- return chartMarkup({ ...asObject(spec), ...asObject(extra) });
78474
+ return chartMarkup({
78475
+ ...asObject(spec),
78476
+ ...asObject(extra)
78477
+ });
78433
78478
  }
78434
78479
  },
78435
78480
  {
@@ -78437,18 +78482,24 @@ var documentHelpers = [
78437
78482
  html: true,
78438
78483
  doc: {
78439
78484
  signature: "image(url, { width, height, fit, alt })",
78440
- description: "Places an image with fixed size and crop mode (`cover`, `contain`, `fill`); the render-worker fetches it through its cache and blocks private hosts.",
78485
+ description: "Places an image with fixed size and crop mode (`cover`, `contain`, `fill`); the render-worker fetches it through its cache and blocks private hosts. `alt` says what it shows; without one the image counts as decoration and a screen reader passes over it.",
78441
78486
  examples: {
78442
- jinja2: "{{ image(product.photo, { width: 120, height: 80, fit: 'cover' }) }}",
78443
- liquid: "{{ product.photo | image: width: 120, height: 80, fit: 'cover' }}",
78444
- handlebars: "{{image product.photo width=120 height=80 fit='cover'}}"
78487
+ jinja2: "{{ image(product.photo, { width: 120, height: 80, fit: 'cover', alt: product.name }) }}",
78488
+ liquid: "{{ product.photo | image: width: 120, height: 80, fit: 'cover', alt: product.name }}",
78489
+ handlebars: "{{image product.photo width=120 height=80 fit='cover' alt=product.name}}"
78445
78490
  },
78446
78491
  category: "document"
78447
78492
  },
78448
78493
  fn: (ctx, url, options) => {
78449
78494
  const o = asObject(options);
78450
78495
  if (ctx.drawing)
78451
- return ctx.drawing({ kind: "url", url: resolveAsset(ctx.assetBaseUrl, String(url ?? "")), width: o.width, height: o.height, alt: o.alt });
78496
+ return ctx.drawing({
78497
+ kind: "url",
78498
+ url: resolveAsset(ctx.assetBaseUrl, String(url ?? "")),
78499
+ width: o.width,
78500
+ height: o.height,
78501
+ alt: o.alt
78502
+ });
78452
78503
  return imageMarkup(String(url ?? ""), o);
78453
78504
  }
78454
78505
  }
@@ -82514,7 +82565,7 @@ var encoder2 = new TextEncoder();
82514
82565
  // packages/mcp/src/lib/server.ts
82515
82566
  import { z as z3 } from "zod";
82516
82567
  var CONVERT_LIMIT_BYTES = 20 * 1024 * 1024;
82517
- var SERVER_INFO = { name: "formfeed", version: "0.3.12" };
82568
+ var SERVER_INFO = { name: "formfeed", version: "0.3.13" };
82518
82569
  var READS = {
82519
82570
  readOnlyHint: true,
82520
82571
  destructiveHint: false,
@@ -82549,7 +82600,10 @@ var renderSummary = (r) => ({
82549
82600
  units: r.units,
82550
82601
  environment: r.environment,
82551
82602
  template: r.template,
82552
- error: r.error
82603
+ error: r.error,
82604
+ // the validator's verdict of a render whose template declares PDF/UA-1 (plan 21): whether the
82605
+ // file passed, and for each failed rule what to change and in which elements
82606
+ ...r.accessibility ? { accessibility: r.accessibility } : {}
82553
82607
  });
82554
82608
  function text3(data) {
82555
82609
  return {
@@ -82559,8 +82613,15 @@ function text3(data) {
82559
82613
  }
82560
82614
  function failure(e) {
82561
82615
  const message2 = e instanceof FormfeedError ? `${e.code}: ${e.message}` : e instanceof Error ? e.message : String(e);
82616
+ const verdict = e instanceof FormfeedError && e.code === "pdfua_validation_failed" ? e.problem?.["accessibility"] : void 0;
82562
82617
  return {
82563
- content: [{ type: "text", text: message2 }],
82618
+ content: [
82619
+ {
82620
+ type: "text",
82621
+ text: verdict ? `${message2}
82622
+ ${JSON.stringify({ accessibility: verdict }, null, 2)}` : message2
82623
+ }
82624
+ ],
82564
82625
  isError: true
82565
82626
  };
82566
82627
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@formfeed/mcp",
3
- "version": "0.3.12",
3
+ "version": "0.3.13",
4
4
  "mcpName": "io.github.formfeed-dev/formfeed",
5
5
  "description": "Model Context Protocol server for Formfeed: list templates, read data schemas, validate and render documents from AI agents.",
6
6
  "license": "MIT",