@formio/mcp
Advanced tools
+39
-0
@@ -5,3 +5,42 @@ import { ResolvedFormioConfig } from './config.js'; | ||
| } | ||
| /** | ||
| * Every CDN asset the login page loads, with the Subresource Integrity digest of | ||
| * the exact bytes each URL is expected to serve. This page handles the user's | ||
| * portal credentials, so a third-party script on it is pinned twice: to a version | ||
| * (jsDelivr serves that release forever) and to a hash (the browser refuses the | ||
| * file if the bytes differ). | ||
| * | ||
| * The digests are data, not decoration — nothing here recomputes them at run time, | ||
| * and no test can tell a correct hash from a plausible one by looking. A wrong or | ||
| * stale digest costs the whole login flow: the browser blocks the renderer, | ||
| * `Formio.createForm` throws `ReferenceError: Formio is not defined`, nothing is | ||
| * ever POSTed to /callback, and `authenticate` hangs on a blank page until it | ||
| * times out. So the pairs live in one list with one job — `pnpm sync:sri` fetches | ||
| * each URL, digests what it gets, and rewrites this list when a bump changes it; | ||
| * `pnpm sync:sri --check` verifies it, and login-asset-integrity.test.ts runs that | ||
| * check whenever the network is reachable. | ||
| * | ||
| * The Open Sans stylesheet is deliberately absent. Google Fonts serves | ||
| * per-browser CSS, so its bytes vary by request and no fixed digest can match; | ||
| * it also carries no script and only chooses a typeface. | ||
| */ | ||
| export declare const LOGIN_PAGE_ASSETS: { | ||
| readonly bootstrapCss: { | ||
| readonly url: "https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css"; | ||
| readonly integrity: "sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB"; | ||
| }; | ||
| readonly bootstrapIconsCss: { | ||
| readonly url: "https://cdn.jsdelivr.net/npm/bootstrap-icons@1.13.1/font/bootstrap-icons.css"; | ||
| readonly integrity: "sha384-Bk5cbLkZQ5raZ0+H2/+VbfYx3WpvxvQK4zqXZr7sYODuaX7bKXoSOnipQxkaS8sv"; | ||
| }; | ||
| readonly formioCss: { | ||
| readonly url: "https://cdn.jsdelivr.net/npm/@formio/js@5.5.1/dist/formio.form.min.css"; | ||
| readonly integrity: "sha384-/zfd6nkJxXzqXliV/Jlki/NOl+E/K7FujopWT3gKLYXMlIwiratcqMESMZG9ICY2"; | ||
| }; | ||
| readonly formioJs: { | ||
| readonly url: "https://cdn.jsdelivr.net/npm/@formio/js@5.5.1/dist/formio.form.min.js"; | ||
| readonly integrity: "sha384-WI14pf615veSnkFtQYllUINR9h5mP1ukKxI47QtGb9DVDYvZlUeaOnWpK/G23Z5x"; | ||
| }; | ||
| }; | ||
| export declare function resetLoginFormCache(): void; | ||
| export declare function authenticate(config: ResolvedFormioConfig, options?: AuthenticateOptions): Promise<string>; |
+49
-4
@@ -11,2 +11,47 @@ import express from 'express'; | ||
| const DEFAULT_AUTH_TIMEOUT_MS = 15 * 60 * 1000; | ||
| /** | ||
| * Every CDN asset the login page loads, with the Subresource Integrity digest of | ||
| * the exact bytes each URL is expected to serve. This page handles the user's | ||
| * portal credentials, so a third-party script on it is pinned twice: to a version | ||
| * (jsDelivr serves that release forever) and to a hash (the browser refuses the | ||
| * file if the bytes differ). | ||
| * | ||
| * The digests are data, not decoration — nothing here recomputes them at run time, | ||
| * and no test can tell a correct hash from a plausible one by looking. A wrong or | ||
| * stale digest costs the whole login flow: the browser blocks the renderer, | ||
| * `Formio.createForm` throws `ReferenceError: Formio is not defined`, nothing is | ||
| * ever POSTed to /callback, and `authenticate` hangs on a blank page until it | ||
| * times out. So the pairs live in one list with one job — `pnpm sync:sri` fetches | ||
| * each URL, digests what it gets, and rewrites this list when a bump changes it; | ||
| * `pnpm sync:sri --check` verifies it, and login-asset-integrity.test.ts runs that | ||
| * check whenever the network is reachable. | ||
| * | ||
| * The Open Sans stylesheet is deliberately absent. Google Fonts serves | ||
| * per-browser CSS, so its bytes vary by request and no fixed digest can match; | ||
| * it also carries no script and only chooses a typeface. | ||
| */ | ||
| export const LOGIN_PAGE_ASSETS = { | ||
| bootstrapCss: { | ||
| url: 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css', | ||
| integrity: 'sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB', | ||
| }, | ||
| bootstrapIconsCss: { | ||
| url: 'https://cdn.jsdelivr.net/npm/bootstrap-icons@1.13.1/font/bootstrap-icons.css', | ||
| integrity: 'sha384-Bk5cbLkZQ5raZ0+H2/+VbfYx3WpvxvQK4zqXZr7sYODuaX7bKXoSOnipQxkaS8sv', | ||
| }, | ||
| formioCss: { | ||
| url: 'https://cdn.jsdelivr.net/npm/@formio/js@5.5.1/dist/formio.form.min.css', | ||
| integrity: 'sha384-/zfd6nkJxXzqXliV/Jlki/NOl+E/K7FujopWT3gKLYXMlIwiratcqMESMZG9ICY2', | ||
| }, | ||
| formioJs: { | ||
| url: 'https://cdn.jsdelivr.net/npm/@formio/js@5.5.1/dist/formio.form.min.js', | ||
| integrity: 'sha384-WI14pf615veSnkFtQYllUINR9h5mP1ukKxI47QtGb9DVDYvZlUeaOnWpK/G23Z5x', | ||
| }, | ||
| }; | ||
| function styleTag(asset) { | ||
| return `<link rel="stylesheet" href="${asset.url}" integrity="${asset.integrity}" crossorigin="anonymous">`; | ||
| } | ||
| function scriptTag(asset) { | ||
| return `<script src="${asset.url}" integrity="${asset.integrity}" crossorigin="anonymous"></script>`; | ||
| } | ||
| function buildLoginPage(loginFormUrl) { | ||
@@ -19,5 +64,5 @@ const domain = new URL(loginFormUrl).hostname; | ||
| <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:400,400italic,700"> | ||
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap/dist/css/bootstrap.min.css"> | ||
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css"> | ||
| <link rel="stylesheet" href="https://cdn.form.io/js/5.2.2/formio.full.min.css"> | ||
| ${styleTag(LOGIN_PAGE_ASSETS.bootstrapCss)} | ||
| ${styleTag(LOGIN_PAGE_ASSETS.bootstrapIconsCss)} | ||
| ${styleTag(LOGIN_PAGE_ASSETS.formioCss)} | ||
| <style> | ||
@@ -45,3 +90,3 @@ body { font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; background: #fff; margin: 0; padding: 2rem 1rem; color: #333; } | ||
| </div> | ||
| <script src="https://cdn.form.io/js/5.2.2/formio.form.min.js"></script> | ||
| ${scriptTag(LOGIN_PAGE_ASSETS.formioJs)} | ||
| <script> | ||
@@ -48,0 +93,0 @@ var statusEl = document.getElementById('status'); |
+1
-1
| { | ||
| "name": "@formio/mcp", | ||
| "version": "0.9.0", | ||
| "version": "0.10.0", | ||
| "mcpName": "io.form/formio-mcp", | ||
@@ -5,0 +5,0 @@ "description": "Form.io MCP Server", |
+4
-4
@@ -22,3 +22,3 @@ ## Formio MCP server | ||
| | --- | --- | --- | | ||
| | stdio | `npx -y @formio/mcp` (or `node dist/stdio.js`) | Claude Code, Claude Desktop, Cursor, VS Code, Codex, Windsurf, Cline — anything that speaks MCP over stdio | | ||
| | stdio | `npx -y @formio/mcp@0.10.0` (or `node dist/stdio.js`) | Claude Code, Claude Desktop, Cursor, VS Code, Codex, Windsurf, Cline — anything that speaks MCP over stdio | | ||
@@ -36,3 +36,3 @@ There is no HTTP or SSE transport. The server's only HTTP listener is the temporary browser-login page described under [Authentication](#authentication), which carries no MCP traffic. | ||
| "command": "npx", | ||
| "args": ["-y", "@formio/mcp"], | ||
| "args": ["-y", "@formio/mcp@0.10.0"], | ||
| "env": { | ||
@@ -290,3 +290,3 @@ "FORMIO_PROJECT_URL": "https://your-project.form.io" | ||
| <sub>\* Not at startup — the server starts, lists every tool, and answers `hello` without it; only the tools that read or write Form.io data error, naming `project_set` and this variable. The alternative is the `project_set` tool, which maps a working directory to a project in `~/.formio/projects.json`. Resolution order: `FORMIO_PROJECT_URL`, then the mapping for the caller's `cwd`, then the error. Map a directory before any client connects with `npx -y @formio/mcp project set --project-url <url> --base-url <url> --cwd <path>`; `project get --cwd <path>` prints what resolves and which source won. It exits `0` when it resolved, `1` when nothing is mapped for that directory, and `2` when the command could not answer (a usage error, a malformed URL, an unreadable `~/.formio/projects.json`) — so a caller can tell "nothing here yet" from "this failed".</sub> | ||
| <sub>\* Not at startup — the server starts, lists every tool, and answers `hello` without it; only the tools that read or write Form.io data error, naming `project_set` and this variable. The alternative is the `project_set` tool, which maps a working directory to a project in `~/.formio/projects.json`. Resolution order: `FORMIO_PROJECT_URL`, then the mapping for the caller's `cwd`, then the error. Map a directory before any client connects with `npx -y @formio/mcp@0.10.0 project set --project-url <url> --base-url <url> --cwd <path>`; `project get --cwd <path>` prints what resolves and which source won. It exits `0` when it resolved, `1` when nothing is mapped for that directory, and `2` when the command could not answer (a usage error, a malformed URL, an unreadable `~/.formio/projects.json`) — so a caller can tell "nothing here yet" from "this failed".</sub> | ||
@@ -314,3 +314,3 @@ --- | ||
| **Third parties.** The server contacts no third-party service. One exception is worth naming: the browser sign-in page is rendered from a local page that loads styling and the Form.io renderer from `cdn.form.io`, `cdn.jsdelivr.net`, and `fonts.googleapis.com`, so those hosts see your browser's IP address while that page is open. Set `FORMIO_API_KEY` to skip the browser flow entirely and avoid it. | ||
| **Third parties.** The server contacts no third-party service. One exception is worth naming: the browser sign-in page is served locally, and the page it renders loads the Form.io renderer and its stylesheets from `cdn.jsdelivr.net`, a webfont from `fonts.googleapis.com`, and the Form.io logo from `portal.form.io`, so those hosts see your browser's IP address while that page is open. Everything else on the page comes from your own deployment. Set `FORMIO_API_KEY` to skip the browser flow entirely and avoid it. | ||
@@ -317,0 +317,0 @@ **Retention.** The files above persist until you delete them. Data held in your Form.io project is governed by your own deployment's retention rules, and by the policy linked above for Form.io-hosted projects. |
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
200929
2.38%3597
2.39%25
4.17%