Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions .changeset/first-party-inputs.md

This file was deleted.

5 changes: 0 additions & 5 deletions .changeset/role-assignment-existing.md

This file was deleted.

5 changes: 0 additions & 5 deletions .changeset/windows-browser-launch.md

This file was deleted.

16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,10 @@ Neither install route asks for a URL. Both are resolved per working directory, s

```bash
# What does this directory resolve to?
npx -y @formio/mcp@0.14.0 project get --cwd "$(pwd)"
npx -y @formio/mcp@0.14.1 project get --cwd "$(pwd)"

# Record it for this machine…
npx -y @formio/mcp@0.14.0 project set --project-url "<project url>" --cwd "$(pwd)"
npx -y @formio/mcp@0.14.1 project set --project-url "<project url>" --cwd "$(pwd)"

# …or commit it with the application, tracked in git and shared with everyone who clones it:
# write a formio.json in the application's own folder (the server reads it, never writes it)
Expand All @@ -91,7 +91,7 @@ You usually only supply the first. The Base URL is worked out from the Project U
Those derivations are also enforced: a `*.form.io` project paired with anything but `https://api.form.io` is refused, because for every project on our SaaS environment that pairing is a mistake that surfaces later as unexplained 404s or a portal login sent to a deployment you do not use. One deployment shape is indistinguishable from that mistake — an internal, non-SaaS deployment served from a `*.form.io` domain, which our QA team tests — so `project set` takes a `--force` flag for it:

```bash
npx -y @formio/mcp@0.14.0 project set --force \
npx -y @formio/mcp@0.14.1 project set --force \
--project-url "https://myproject.form.io" --base-url "https://api.internal.example" --cwd "$(pwd)"
```

Expand All @@ -100,7 +100,7 @@ npx -y @formio/mcp@0.14.0 project set --force \
The override belongs to the pair, not to the directory. Re-pointing the directory at a different project drops it and the checks apply again, while a write that leaves both halves untouched keeps it — an agent re-recording the project it already resolved must not undo your decision. That also means re-recording the same pair without `--force` changes nothing, so clearing the record is the way back:

```bash
npx -y @formio/mcp@0.14.0 project set --reset --cwd "$(pwd)"
npx -y @formio/mcp@0.14.1 project set --reset --cwd "$(pwd)"
```

`--reset` removes this directory's entry from `~/.formio/projects.json` — nothing else — and prints what the directory resolves to afterwards, which may still be a committed `formio.json` or the environment. It takes no URLs, exits `0` whenever the record was cleared (run `project get` to see whether the directory is serviceable), and is what every forced report names as the way to put the checks back.
Expand Down Expand Up @@ -211,7 +211,7 @@ It is also listed in the [official MCP Registry](https://registry.modelcontextpr
"mcpServers": {
"formio-mcp": {
"command": "npx",
"args": ["-y", "@formio/mcp@0.14.0"]
"args": ["-y", "@formio/mcp@0.14.1"]
}
}
}
Expand All @@ -226,7 +226,7 @@ That is the **JSON `mcpServers`** shape — what Claude Code (`.mcp.json`), Curs
"servers": {
"formio-mcp": {
"command": "npx",
"args": ["-y", "@formio/mcp@0.14.0"]
"args": ["-y", "@formio/mcp@0.14.1"]
}
}
}
Expand All @@ -237,7 +237,7 @@ That is the **JSON `mcpServers`** shape — what Claude Code (`.mcp.json`), Curs
```toml
[mcp_servers.formio-mcp]
command = "npx"
args = ["-y", "@formio/mcp@0.14.0"]
args = ["-y", "@formio/mcp@0.14.1"]
```

Authentication is the same everywhere: the first authenticated tool call opens the browser portal-login flow, or set `FORMIO_API_KEY` to skip the browser entirely — required on any host with no browser, such as a cloud agent, a container, or CI.
Expand Down Expand Up @@ -339,7 +339,7 @@ The probe runs lazily — only when the local auth page is actually served.
| `FORMIO_AUTH_HOST` / `FORMIO_AUTH_PORT` | no | `127.0.0.1` / ephemeral | Bind address and port for the login server. Set both when running in a container so the login page is reachable through a published port. | — | `0.0.0.0` / `43117` |
| `FORMIO_INSECURE_TLS` | no | `false` | When `true`, skips TLS certificate verification (sets `NODE_TLS_REJECT_UNAUTHORIZED=0`) — for self-hosted deployments behind self-signed certs. Do not use against production. | — | `true` |

<sub>\* Not at startup — the server starts and lists every tool without it, and only errors when a tool actually needs a project. The alternative is the `project_set` tool, which maps a working directory to a project in `~/.formio/projects.json` so one server can serve several workspaces. Resolution runs by scope, narrowest first: a committed `formio.json` found by walking up from the caller's `cwd`, then the mapping for that `cwd`, then `FORMIO_PROJECT_URL` in the environment as the weakest source, then the error. Map a directory before any client connects with `npx -y @formio/mcp@0.14.0 project set --project-url <url> --cwd <path>` — the deployment is derived from the project URL wherever it can be, so add `--base-url <url>` only when the server says it cannot be determined. Add `--force` beside both URLs to record a pair the domain rules refuse — the one shape they cannot tell from a mistake is an internal deployment served from a `*.form.io` domain; it takes both URLs in the same call, is honoured on every later read, and is a shell-only flag the `project_set` tool does not have. `project set --reset --cwd <path>` clears a directory's entry, which is how a forced pair is un-forced: a write that leaves both halves untouched keeps the override, so there is nothing for an unforced re-record to change. `project get --cwd <path>` prints what resolves and which source won, exiting `0` when it resolved, `1` when nothing is mapped, `2` when the command itself failed, and `3` when a project resolved but its Base URL could not be determined — the half-configured directory, repaired by supplying that one value. `project set --cwd <path>` exits `0` when the directory is ready to serve a call, `1` when a named value is still missing, `2` when the command could not answer, and `3` when the record WAS written and the directory still resolves no Base URL — a committed `formio.json` governs it and supplies none, so the remedy is an edit to that file rather than another write.</sub>
<sub>\* Not at startup — the server starts and lists every tool without it, and only errors when a tool actually needs a project. The alternative is the `project_set` tool, which maps a working directory to a project in `~/.formio/projects.json` so one server can serve several workspaces. Resolution runs by scope, narrowest first: a committed `formio.json` found by walking up from the caller's `cwd`, then the mapping for that `cwd`, then `FORMIO_PROJECT_URL` in the environment as the weakest source, then the error. Map a directory before any client connects with `npx -y @formio/mcp@0.14.1 project set --project-url <url> --cwd <path>` — the deployment is derived from the project URL wherever it can be, so add `--base-url <url>` only when the server says it cannot be determined. Add `--force` beside both URLs to record a pair the domain rules refuse — the one shape they cannot tell from a mistake is an internal deployment served from a `*.form.io` domain; it takes both URLs in the same call, is honoured on every later read, and is a shell-only flag the `project_set` tool does not have. `project set --reset --cwd <path>` clears a directory's entry, which is how a forced pair is un-forced: a write that leaves both halves untouched keeps the override, so there is nothing for an unforced re-record to change. `project get --cwd <path>` prints what resolves and which source won, exiting `0` when it resolved, `1` when nothing is mapped, `2` when the command itself failed, and `3` when a project resolved but its Base URL could not be determined — the half-configured directory, repaired by supplying that one value. `project set --cwd <path>` exits `0` when the directory is ready to serve a call, `1` when a named value is still missing, `2` when the command could not answer, and `3` when the record WAS written and the directory still resolves no Base URL — a committed `formio.json` governs it and supplies none, so the remedy is an edit to that file rather than another write.</sub>

---

Expand Down
10 changes: 5 additions & 5 deletions llms-install.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Otherwise write this block into the client's MCP config file, replacing the proj
"mcpServers": {
"formio-mcp": {
"command": "npx",
"args": ["-y", "@formio/mcp@0.14.0"],
"args": ["-y", "@formio/mcp@0.14.1"],
"env": {
"FORMIO_PROJECT_URL": "https://your-project.form.io"
}
Expand All @@ -74,7 +74,7 @@ Merge into the existing object rather than overwriting the file. For VS Code, pu
```toml
[mcp_servers.formio-mcp]
command = "npx"
args = ["-y", "@formio/mcp@0.14.0"]
args = ["-y", "@formio/mcp@0.14.1"]
```

There is no universal `.mcp.json`: a root `.mcp.json` is read by Claude Code only. Writing one file per client is the reliable approach when you do not know which client the user runs.
Expand All @@ -86,16 +86,16 @@ The skills installer never writes MCP configuration — it handles skills only.
Whichever way, the entry is the same command with no environment block required:

```json
{ "command": "npx", "args": ["-y", "@formio/mcp@0.14.0"] }
{ "command": "npx", "args": ["-y", "@formio/mcp@0.14.1"] }
```

After writing any MCP configuration, tell the user to reload: MCP servers are read at session start, not at tool-call time.

If the user has volunteered a project URL, you can record it before the reload so the first tool call works:

```sh
npx -y @formio/mcp@0.14.0 project set --project-url <url> --base-url <url> --cwd <absolute path>
npx -y @formio/mcp@0.14.0 project get --cwd <absolute path>
npx -y @formio/mcp@0.14.1 project set --project-url <url> --base-url <url> --cwd <absolute path>
npx -y @formio/mcp@0.14.1 project get --cwd <absolute path>
```

`project get` prints what the server resolves and which source supplied it. Empty output is not an answer: the `project` command shipped in 0.9.0, and an older `@formio/mcp` ignores these arguments, starts its stdio server, reads end-of-input and exits 0 with no output — a silent no-op that reads as success. Do not invent either URL — if the user has not given them, skip this and let the agent ask when a project is first needed.
Expand Down
6 changes: 6 additions & 0 deletions packages/mcp-server/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# @formio/mcp

## 0.14.1

### Patch Changes

- 757ee8a: Open the portal-login and revisions-consent pages in the browser on Windows. Both pages were launched with `exec('start "<url>"')`, and `start` reads its first quoted argument as a window title, so Windows opened an empty console window titled with the URL and no browser — the tool call then waited out the login timeout with no page in front of the user. Both pages now go through one launcher that runs no shell: `open` on macOS, `xdg-open` on Linux, and `rundll32 url.dll,FileProtocolHandler` on Windows, each handed the URL as its own argument. The consent page also now reports a failed launch on stderr with its URL, as the login page already did, instead of ignoring it.

## 0.14.0

### Minor Changes
Expand Down
6 changes: 3 additions & 3 deletions packages/mcp-server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Run that way it waits for MCP traffic on stdin, which only tells you it starts c

| Transport | Command | Compatible with |
| --- | --- | --- |
| stdio | `npx -y @formio/mcp@0.14.0` (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.14.1` (or `node dist/stdio.js`) | Claude Code, Claude Desktop, Cursor, VS Code, Codex, Windsurf, Cline — anything that speaks MCP over stdio |

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.

Expand All @@ -32,7 +32,7 @@ The same stdio entry works everywhere, but the file it goes in **and the key it
"mcpServers": {
"formio-mcp": {
"command": "npx",
"args": ["-y", "@formio/mcp@0.14.0"],
"args": ["-y", "@formio/mcp@0.14.1"],
"env": {
"FORMIO_PROJECT_URL": "https://your-project.form.io"
}
Expand Down Expand Up @@ -287,7 +287,7 @@ The probe runs lazily — only when the local auth page is actually served.
| `FORMIO_INSECURE_TLS` | no | `undefined` | Set to `1` to skip TLS verification. Local development only — never against production. | | |
| `FORMIO_FORCE_BROWSER` | no | `0` | Set to `1` to attempt the browser login even where the server detects no browser (CI, a container, SSH with no display). | | |

<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 runs by scope, narrowest first: a committed `formio.json` found by walking up from the caller's `cwd`, then the mapping for that `cwd`, then `FORMIO_PROJECT_URL` in the environment as the weakest source, then the error. Map a directory before any client connects with `npx -y @formio/mcp@0.14.0 project set --project-url <url> --cwd <path>` — the deployment is derived from the project URL wherever it can be, so add `--base-url <url>` only when the server says it cannot be determined. Add `--force` beside both URLs to record a pair the domain rules refuse — the one shape they cannot tell from a mistake is an internal deployment served from a `*.form.io` domain; it takes both URLs in the same call, is honoured on every later read, and is a shell-only flag the `project_set` tool does not have. `project set --reset --cwd <path>` clears a directory's entry, which is how a forced pair is un-forced: a write that leaves both halves untouched keeps the override, so there is nothing for an unforced re-record to change. `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, `2` when the command could not answer (a usage error, a malformed URL, an unreadable `~/.formio/projects.json`), and `3` when a project resolved but its Base URL could not be determined — so a caller can tell "nothing here yet" from "this failed" from "half configured, and here is the one value missing". `project set --cwd <path>` exits `0` when the directory is ready to serve a call, `1` when a named value is still missing, `2` when the command could not answer, and `3` when the record WAS written and the directory still resolves no Base URL — a committed `formio.json` governs it and supplies none, so the remedy is an edit to that file rather than another write.</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 runs by scope, narrowest first: a committed `formio.json` found by walking up from the caller's `cwd`, then the mapping for that `cwd`, then `FORMIO_PROJECT_URL` in the environment as the weakest source, then the error. Map a directory before any client connects with `npx -y @formio/mcp@0.14.1 project set --project-url <url> --cwd <path>` — the deployment is derived from the project URL wherever it can be, so add `--base-url <url>` only when the server says it cannot be determined. Add `--force` beside both URLs to record a pair the domain rules refuse — the one shape they cannot tell from a mistake is an internal deployment served from a `*.form.io` domain; it takes both URLs in the same call, is honoured on every later read, and is a shell-only flag the `project_set` tool does not have. `project set --reset --cwd <path>` clears a directory's entry, which is how a forced pair is un-forced: a write that leaves both halves untouched keeps the override, so there is nothing for an unforced re-record to change. `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, `2` when the command could not answer (a usage error, a malformed URL, an unreadable `~/.formio/projects.json`), and `3` when a project resolved but its Base URL could not be determined — so a caller can tell "nothing here yet" from "this failed" from "half configured, and here is the one value missing". `project set --cwd <path>` exits `0` when the directory is ready to serve a call, `1` when a named value is still missing, `2` when the command could not answer, and `3` when the record WAS written and the directory still resolves no Base URL — a committed `formio.json` governs it and supplies none, so the remedy is an edit to that file rather than another write.</sub>

---

Expand Down
2 changes: 1 addition & 1 deletion packages/mcp-server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@formio/mcp",
"version": "0.14.0",
"version": "0.14.1",
"mcpName": "io.form/formio-mcp",
"description": "Form.io MCP Server",
"type": "module",
Expand Down
Loading
Loading