@formfeed/mcp 0.3.9 → 0.3.10

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 CHANGED
@@ -1,5 +1,7 @@
1
1
  # @formfeed/mcp
2
2
 
3
+ [![formfeed MCP server – quality and maintenance score on Glama](https://glama.ai/mcp/servers/formfeed-dev/formfeed/badges/score.svg)](https://glama.ai/mcp/servers/formfeed-dev/formfeed)
4
+
3
5
  Model Context Protocol server for [Formfeed](https://formfeed.dev/?utm_source=npm&utm_content=mcp).
4
6
  Gives Claude, Cursor and other agents six tools: `list_templates`, `get_template_schema`,
5
7
  `validate_template` (free: the data against the template's schema, ad-hoc HTML offline), `render`, `convert_to_pdf` (Word, Excel,
@@ -81449,7 +81449,7 @@ var encoder2 = new TextEncoder();
81449
81449
  // packages/mcp/src/lib/server.ts
81450
81450
  import { z as z2 } from "zod";
81451
81451
  var CONVERT_LIMIT_BYTES = 20 * 1024 * 1024;
81452
- var SERVER_INFO = { name: "formfeed", version: "0.3.9" };
81452
+ var SERVER_INFO = { name: "formfeed", version: "0.3.10" };
81453
81453
  var READS = { readOnlyHint: true, destructiveHint: false, openWorldHint: false };
81454
81454
  var CREATES = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false };
81455
81455
  var engineSchema = z2.enum(["jinja2", "liquid", "handlebars"]);
@@ -81499,12 +81499,12 @@ function createFormfeedServer(options) {
81499
81499
  "list_templates",
81500
81500
  {
81501
81501
  title: "List templates",
81502
- description: "Templates of the workspace the API key belongs to, with slug, kind (pdf, image, docx for Word, pptx for PowerPoint), engine and published version.",
81502
+ description: "Lists the templates of the workspace the API key belongs to, most recently changed first: id, slug, name, kind (pdf, image, docx for Word, pptx for PowerPoint), engine, tags and published_version. Call it first to find the slug that get_template_schema, validate_template and render take; skip it when the user already named the template. Returns every match in one answer, without archived templates, and an empty list when nothing matches. A template whose published_version is null has never been published, so render cannot use it yet. Read-only and free.",
81503
81503
  inputSchema: {
81504
- kind: kindSchema.optional(),
81505
- engine: engineSchema.optional(),
81506
- tag: z2.string().optional(),
81507
- q: z2.string().optional().describe("Search in name and slug")
81504
+ kind: kindSchema.optional().describe("Only templates that produce this kind of document"),
81505
+ engine: engineSchema.optional().describe("Only templates written in this template language"),
81506
+ tag: z2.string().optional().describe("Only templates with this tag; exact match, tags are lowercase with dashes (e.g. billing)"),
81507
+ q: z2.string().optional().describe("Text to find in the name or slug, case-insensitive (e.g. invoice)")
81508
81508
  },
81509
81509
  annotations: READS
81510
81510
  },
@@ -81521,8 +81521,8 @@ function createFormfeedServer(options) {
81521
81521
  "get_template_schema",
81522
81522
  {
81523
81523
  title: "Get the data schema of a template",
81524
- description: "JSON Schema of the `data` object a template expects, plus the sample data it was designed with. Use it to build the data for render.",
81525
- inputSchema: { template: z2.string().describe("Template slug or tpl_ id") },
81524
+ description: "Returns the JSON Schema of the `data` object a template expects and the sample data it was designed with, from its published version, else its latest draft. Call it after list_templates and before render or validate_template, to build data that fits; ad-hoc html has no stored schema to read. When the template stores no schema, one is inferred from the sample data: treat that as a guide, not a contract. Read-only and free; an unknown slug returns an error.",
81525
+ inputSchema: { template: z2.string().describe("Slug (e.g. invoice) or tpl_ id, as list_templates returns them") },
81526
81526
  annotations: READS
81527
81527
  },
81528
81528
  async ({ template }) => {
@@ -81542,12 +81542,12 @@ function createFormfeedServer(options) {
81542
81542
  "validate_template",
81543
81543
  {
81544
81544
  title: "Validate a template with data",
81545
- description: "Checks a template with data without rendering; free. With a template slug the API checks the version a render uses (the published one, else the latest) as the render would: syntax, header and footer, errors that happen only with this data, and the data against the template's stored JSON Schema (a wrong type or a missing required field is an error). With html plus engine it compiles offline and reports syntax errors, unknown filters and variables missing from the data. Warnings leave ok true: treat each one as a question to settle before rendering. For Word and PowerPoint templates the findings name the document part and paragraph.",
81545
+ description: "Checks a template and its data without rendering: free, no units, nothing stored, no document produced. Call it before render when you assembled the data yourself, or after writing ad-hoc html. With template, the API checks the published version (else the latest) as a render would: syntax, header and footer, errors only this data triggers, and the data against the stored JSON Schema (a wrong type or a missing required field is an error). With html and engine it compiles offline and reports syntax errors, unknown filters and variables missing from data. Returns ok and diagnostics (severity, code, message, plus path, line and column, or part and paragraph for Word and PowerPoint). Warnings leave ok true: settle each before rendering.",
81546
81546
  inputSchema: {
81547
- template: z2.string().optional().describe("Template slug or tpl_ id"),
81548
- html: z2.string().optional().describe("Template source when no slug is given"),
81549
- engine: engineSchema.optional().describe("Required with html"),
81550
- data: z2.record(z2.string(), z2.unknown()).optional()
81547
+ template: z2.string().optional().describe("Slug or tpl_ id of a stored template; pass this or html"),
81548
+ html: z2.string().optional().describe("Template source to check offline, when there is no stored template; pass this or template"),
81549
+ engine: engineSchema.optional().describe("Template language of html; required with html"),
81550
+ data: z2.record(z2.string(), z2.unknown()).optional().describe("The data the document will be rendered with, shaped as get_template_schema describes. Left out, a stored template is checked with its sample data")
81551
81551
  },
81552
81552
  annotations: READS
81553
81553
  },
@@ -81594,18 +81594,18 @@ function createFormfeedServer(options) {
81594
81594
  "render",
81595
81595
  {
81596
81596
  title: "Render a document",
81597
- description: "Renders a template (with data) or raw HTML to a PDF or image, or a Word or PowerPoint template to DOCX, PPTX or PDF, and returns a download URL. Live keys consume units; test keys render free with a watermark.",
81597
+ description: "Creates a document by filling a stored template, or ad-hoc html, with data: a PDF or image, or DOCX, PPTX or PDF from Word and PowerPoint templates. Returns the render with status and download_url. Use it to make a document from data; to turn an existing office file into a PDF, use convert_to_pdf. Stored templates render their published version, so one whose published_version is null fails with template_not_found. Every call is a new render: a live key consumes units, a test key is free (watermarked on the Free plan). A render that fails returns status failed and an error with code and message; validate_template finds most causes beforehand. The link expires at expires_at; get_render issues a fresh one.",
81598
81598
  inputSchema: {
81599
- template: z2.string().optional().describe("Template slug or tpl_ id"),
81600
- html: z2.string().optional().describe("Raw HTML instead of a template"),
81601
- engine: engineSchema.optional().describe("Engine for html that contains template syntax"),
81602
- data: z2.record(z2.string(), z2.unknown()).optional(),
81599
+ template: z2.string().optional().describe("Slug or tpl_ id of a stored template; pass this or html"),
81600
+ html: z2.string().optional().describe("A complete HTML document to render instead of a stored template, up to 5 MB"),
81601
+ engine: engineSchema.optional().describe("Template language of html, when it contains expressions to fill from data"),
81602
+ data: z2.record(z2.string(), z2.unknown()).optional().describe("Values for the template, shaped as get_template_schema describes; leave out when it needs none"),
81603
81603
  output: outputSchema.optional().describe(
81604
81604
  "Left out, the template decides: PDF for PDF templates and html, its image format for image templates, its default output for Word and PowerPoint templates. Word templates render docx or pdf, PowerPoint templates pptx or pdf"
81605
81605
  ),
81606
- filename: z2.string().optional(),
81606
+ filename: z2.string().optional().describe("Name of the file behind download_url, e.g. invoice-2026-0042.pdf"),
81607
81607
  locale: z2.string().optional().describe("BCP 47 tag for helpers and translations, e.g. de-DE"),
81608
- wait: z2.boolean().default(true).describe("Wait for the render to finish (sync renders finish immediately)")
81608
+ wait: z2.boolean().default(true).describe("Most renders are finished when the call returns; true also waits for one that continues in the background, false returns it as it is (queued or rendering) to check later with get_render")
81609
81609
  },
81610
81610
  annotations: CREATES
81611
81611
  },
@@ -81634,20 +81634,21 @@ function createFormfeedServer(options) {
81634
81634
  {
81635
81635
  title: "Convert an office document to PDF",
81636
81636
  description: [
81637
- "Converts a Word, Excel, PowerPoint, OpenDocument, RTF or HTML document (up to 20 MB) to a PDF and returns a download URL (Starter plan and above; units follow the PDF rule).",
81637
+ "Converts an existing Word, Excel, PowerPoint, OpenDocument, RTF or HTML document (up to 20 MB) to a PDF as it is, without filling in data, and returns the render with status and download_url.",
81638
+ "To make a document from a template and data, use render instead.",
81638
81639
  "Pass exactly one source: render_id, the render of a Word or PowerPoint template (output docx or pptx);",
81639
81640
  options.localFiles ? "path, a file on this machine; or file_base64 with file_name." : "or file_base64 with file_name, the document itself.",
81640
- "The document is converted and not kept."
81641
+ "Every call is a new render that counts units like a PDF; the Free plan cannot convert (Starter and above). The document is converted and not kept."
81641
81642
  ].join(" "),
81642
81643
  inputSchema: {
81643
- render_id: z2.string().optional().describe("rnd_ id of a render whose output is docx or pptx"),
81644
- ...options.localFiles ? { path: z2.string().optional().describe("Absolute path of a document on this machine") } : {},
81645
- file_base64: z2.string().optional().describe("The document, base64 encoded"),
81644
+ render_id: z2.string().optional().describe("rnd_ id of a render whose output is docx or pptx, to get that document as a PDF"),
81645
+ ...options.localFiles ? { path: z2.string().optional().describe("Absolute path of the document on this machine") } : {},
81646
+ file_base64: z2.string().optional().describe("The document to convert, base64 encoded; needs file_name"),
81646
81647
  file_name: z2.string().optional().describe("Name of the document, e.g. report.xlsx; the type is read from the content"),
81647
- page_ranges: z2.string().regex(/^\d+(-\d+)?(,\d+(-\d+)?)*$/).optional().describe("Pages to convert, e.g. 1-3,5"),
81648
+ page_ranges: z2.string().regex(/^\d+(-\d+)?(,\d+(-\d+)?)*$/).optional().describe("Pages to convert, e.g. 1-3,5; all pages when left out"),
81648
81649
  landscape: z2.boolean().optional().describe("Landscape for spreadsheets and documents without their own page setup"),
81649
81650
  single_page_sheets: z2.boolean().optional().describe("Each spreadsheet sheet on one page"),
81650
- filename: z2.string().optional().describe("Name of the PDF")
81651
+ filename: z2.string().optional().describe("Name of the PDF behind download_url, e.g. report.pdf")
81651
81652
  },
81652
81653
  annotations: CREATES
81653
81654
  },
@@ -81696,8 +81697,8 @@ function createFormfeedServer(options) {
81696
81697
  "get_render",
81697
81698
  {
81698
81699
  title: "Get a render",
81699
- description: "Status, download URL and error of a render by id (rnd_\u2026).",
81700
- inputSchema: { id: z2.string() },
81700
+ description: "Returns the current state of a render: status (queued, rendering, succeeded or failed), download_url with expires_at, page_count, units and error. Use it to follow a render started with wait false, or to get a fresh link once the old one has expired; render and convert_to_pdf already return this for the render they just made. Read-only and free; an unknown id returns an error.",
81701
+ inputSchema: { id: z2.string().describe("rnd_ id of the render, as render or convert_to_pdf returned it") },
81701
81702
  annotations: READS
81702
81703
  },
81703
81704
  async ({ id }) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@formfeed/mcp",
3
- "version": "0.3.9",
3
+ "version": "0.3.10",
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",