@frontmcp/skills 1.8.7 → 1.9.1-rc.1

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.
Files changed (63) hide show
  1. package/README.md +107 -155
  2. package/catalog/create-tool/examples/21-tool-with-availability-constraints.md +1 -1
  3. package/catalog/create-tool/references/availability.md +10 -10
  4. package/catalog/create-tool/references/ui-widgets.md +30 -8
  5. package/catalog/frontmcp-authorities/SKILL.md +5 -0
  6. package/catalog/frontmcp-channels/SKILL.md +17 -16
  7. package/catalog/frontmcp-channels/references/channel-sources.md +4 -4
  8. package/catalog/frontmcp-channels/references/channel-two-way.md +1 -1
  9. package/catalog/frontmcp-config/examples/configure-deployment-targets/distributed-ha-config.md +2 -0
  10. package/catalog/frontmcp-config/examples/configure-session/vercel-kv-session.md +1 -2
  11. package/catalog/frontmcp-config/examples/configure-transport/custom-protocol-flags.md +0 -1
  12. package/catalog/frontmcp-config/examples/configure-transport/distributed-sessions-redis.md +0 -1
  13. package/catalog/frontmcp-config/examples/configure-transport/stateless-serverless.md +2 -3
  14. package/catalog/frontmcp-config/examples/configure-transport-protocol-presets/stateless-api-serverless.md +2 -3
  15. package/catalog/frontmcp-config/references/configure-auth.md +1 -1
  16. package/catalog/frontmcp-config/references/configure-deployment-targets.md +54 -20
  17. package/catalog/frontmcp-config/references/configure-http.md +5 -2
  18. package/catalog/frontmcp-config/references/configure-throttle-guard-config.md +1 -1
  19. package/catalog/frontmcp-config/references/configure-throttle.md +4 -2
  20. package/catalog/frontmcp-config/references/configure-transport.md +4 -5
  21. package/catalog/frontmcp-deployment/SKILL.md +19 -19
  22. package/catalog/frontmcp-deployment/examples/deploy-to-node/docker-compose-with-redis.md +1 -1
  23. package/catalog/frontmcp-deployment/examples/deploy-to-node/pm2-with-nginx.md +1 -1
  24. package/catalog/frontmcp-deployment/references/build-for-browser.md +38 -9
  25. package/catalog/frontmcp-deployment/references/build-for-mcpb.md +15 -9
  26. package/catalog/frontmcp-deployment/references/build-for-sdk.md +2 -1
  27. package/catalog/frontmcp-deployment/references/deploy-to-cloudflare.md +3 -1
  28. package/catalog/frontmcp-deployment/references/deploy-to-lambda.md +2 -2
  29. package/catalog/frontmcp-deployment/references/deploy-to-node.md +11 -11
  30. package/catalog/frontmcp-deployment/references/mcp-client-integration.md +12 -7
  31. package/catalog/frontmcp-deployment/references/protocol-versions.md +7 -3
  32. package/catalog/frontmcp-development/examples/create-agent/nested-agents-with-swarm.md +50 -10
  33. package/catalog/frontmcp-development/examples/openapi-adapter/ref-security-and-filtering.md +13 -7
  34. package/catalog/frontmcp-development/references/create-adapter.md +14 -0
  35. package/catalog/frontmcp-development/references/create-agent.md +82 -49
  36. package/catalog/frontmcp-development/references/create-plugin-hooks.md +15 -5
  37. package/catalog/frontmcp-development/references/create-plugin.md +8 -4
  38. package/catalog/frontmcp-development/references/create-skill-with-tools.md +7 -2
  39. package/catalog/frontmcp-development/references/create-skill.md +4 -0
  40. package/catalog/frontmcp-development/references/decorators-guide.md +10 -11
  41. package/catalog/frontmcp-development/references/official-adapters.md +1 -1
  42. package/catalog/frontmcp-development/references/official-plugins.md +127 -24
  43. package/catalog/frontmcp-development/references/openapi-adapter.md +52 -2
  44. package/catalog/frontmcp-observability/references/metrics-endpoint.md +3 -1
  45. package/catalog/frontmcp-production-readiness/examples/distributed-ha/ha-kubernetes-3-replicas.md +12 -1
  46. package/catalog/frontmcp-production-readiness/references/common-checklist.md +2 -1
  47. package/catalog/frontmcp-production-readiness/references/distributed-ha.md +48 -28
  48. package/catalog/frontmcp-setup/examples/multi-app-composition/local-apps-with-shared-tools.md +10 -6
  49. package/catalog/frontmcp-setup/examples/nx-workflow/build-test-affected.md +2 -2
  50. package/catalog/frontmcp-setup/examples/nx-workflow/multi-server-deployment.md +9 -5
  51. package/catalog/frontmcp-setup/examples/nx-workflow/scaffold-and-generate.md +1 -1
  52. package/catalog/frontmcp-setup/examples/project-structure-nx/nx-generator-scaffolding.md +1 -1
  53. package/catalog/frontmcp-setup/references/frontmcp-skills-usage.md +28 -15
  54. package/catalog/frontmcp-setup/references/multi-app-composition.md +25 -16
  55. package/catalog/frontmcp-setup/references/nx-workflow.md +53 -21
  56. package/catalog/frontmcp-setup/references/project-structure-nx.md +7 -4
  57. package/catalog/frontmcp-setup/references/setup-project.md +15 -0
  58. package/catalog/frontmcp-setup/references/setup-redis.md +12 -3
  59. package/catalog/frontmcp-setup/references/setup-sqlite.md +12 -8
  60. package/catalog/frontmcp-testing/SKILL.md +16 -12
  61. package/catalog/frontmcp-testing/references/setup-testing.md +14 -1
  62. package/catalog/skills-manifest.json +13 -11
  63. package/package.json +1 -1
package/README.md CHANGED
@@ -1,195 +1,147 @@
1
1
  <div align="center">
2
2
 
3
+ <a href="https://frontmcp.dev">
3
4
  <picture>
4
- <source width="400" media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/agentfront/frontmcp/refs/heads/main/docs/assets/logo/frontmcp.dark.svg">
5
- <source width="400" media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/agentfront/frontmcp/refs/heads/main/docs/assets/logo/frontmcp.light.svg">
6
- <img width="400" alt="FrontMCP Logo" src="https://raw.githubusercontent.com/agentfront/frontmcp/refs/heads/main/docs/assets/logo/frontmcp.light.svg">
5
+ <source media="(prefers-color-scheme: dark)" srcset="docs/assets/readme/hero.svg">
6
+ <img src="docs/assets/readme/hero.light.svg" alt="FrontMCP - The TypeScript way to build MCP servers" width="100%">
7
7
  </picture>
8
- <hr>
8
+ </a>
9
9
 
10
- **The production-grade, TypeScript-first framework for building MCP servers — decorators, DI, auth, and Streamable HTTP, batteries included.**
10
+ [![NPM](https://img.shields.io/npm/v/@frontmcp/sdk.svg?style=flat-square&color=16A34A&labelColor=0b1117&label=npm)](https://www.npmjs.com/package/@frontmcp/sdk)
11
+ [![Node](https://img.shields.io/badge/node-%E2%89%A5%2024-16A34A?style=flat-square&labelColor=0b1117&logo=node.js&logoColor=white)](https://nodejs.org)
12
+ [![License](https://img.shields.io/github/license/agentfront/frontmcp.svg?style=flat-square&color=16A34A&labelColor=0b1117)](./LICENSE)
13
+ [![Snyk](https://img.shields.io/badge/snyk-monitored-16A34A?style=flat-square&labelColor=0b1117&logo=snyk&logoColor=white)](https://snyk.io/test/github/agentfront/frontmcp)
14
+ [![Discord](https://img.shields.io/badge/discord-join-16A34A?style=flat-square&labelColor=0b1117&logo=discord&logoColor=white)](https://discord.gg/53AHnJnmwR)
11
15
 
12
- [![NPM - @frontmcp/sdk](https://img.shields.io/npm/v/@frontmcp/sdk.svg?v=2)](https://www.npmjs.com/package/@frontmcp/sdk)
13
- [![Node](https://img.shields.io/badge/node-%3E%3D24-339933?logo=node.js&logoColor=white)](https://nodejs.org)
14
- [![License](https://img.shields.io/github/license/agentfront/frontmcp.svg?v=1)](https://github.com/agentfront/frontmcp/blob/main/LICENSE)
15
- [![Snyk](https://snyk.io/test/github/agentfront/frontmcp/badge.svg)](https://snyk.io/test/github/agentfront/frontmcp)
16
+ **[frontmcp.dev](https://frontmcp.dev)** &nbsp;&middot;&nbsp; **[Learn][docs-learn]** &nbsp;&middot;&nbsp; **[Reference][docs-reference]** &nbsp;&middot;&nbsp; **[Examples][docs-examples]** &nbsp;&middot;&nbsp; **[Playground][docs-playground]** &nbsp;&middot;&nbsp; **[Blog][docs-blog]**
16
17
 
17
- [Docs][docs-home] &bull; [Quickstart][docs-quickstart] &bull; [API Reference][docs-sdk-ref] &bull; [Discord](https://discord.gg/53AHnJnmwR)
18
+ <picture>
19
+ <source media="(prefers-color-scheme: dark)" srcset="docs/assets/readme/terminal.svg">
20
+ <img src="docs/assets/readme/terminal.light.svg" alt="npx frontmcp create my-app, then npm run dev: an MCP server running on localhost:3000" width="760">
21
+ </picture>
18
22
 
19
23
  </div>
20
24
 
21
- ---
25
+ <br>
26
+
27
+ ## Write a tool. Ship a server.
22
28
 
23
- FrontMCP turns the [Model Context Protocol](https://modelcontextprotocol.io) into a
24
- typed, declarative framework. You write clean `@Tool`, `@Resource`, and `@App`
25
- classes; FrontMCP handles the protocol, transport, dependency injection, sessions,
26
- auth, and execution flow — and the **same server runs locally and ships to
27
- production unchanged**.
29
+ Classes in, protocol out. FrontMCP handles transport, DI, sessions, auth and execution flow, and the same server runs locally and in production unchanged.
28
30
 
29
31
  ```ts
30
32
  import 'reflect-metadata';
31
33
 
32
- import { FrontMcp, LogLevel } from '@frontmcp/sdk';
34
+ import { App, FrontMcp, Tool, ToolContext, z } from '@frontmcp/sdk';
35
+
36
+ @Tool({
37
+ name: 'add',
38
+ description: 'Adds two numbers together',
39
+ inputSchema: { a: z.number(), b: z.number() },
40
+ })
41
+ class AddTool extends ToolContext {
42
+ async execute(input: { a: number; b: number }) {
43
+ return input.a + input.b;
44
+ }
45
+ }
33
46
 
34
- import HelloApp from './hello.app';
47
+ @App({ id: 'calc', name: 'Calculator', tools: [AddTool] })
48
+ class CalcApp {}
35
49
 
36
50
  @FrontMcp({
37
51
  info: { name: 'Demo', version: '0.1.0' },
38
- apps: [HelloApp],
52
+ apps: [CalcApp],
39
53
  http: { port: 3000 },
40
- logging: { level: LogLevel.Info },
41
54
  })
42
55
  export default class Server {}
43
56
  ```
44
57
 
45
- ## Why FrontMCP
46
-
47
- - **Typed by default** — decorators + Zod schemas give end-to-end types from input to output, with editor autocomplete and compile-time checks.
48
- - **Batteries included** — auth (OAuth/JWKS/DCR), sessions, transport, discovery, and DI are built in, not bolted on.
49
- - **Ship anywhere** — one codebase deploys to Node, Vercel, AWS Lambda, Cloudflare Workers, or a serverless bundle.
50
- - **Production-minded** — stateful/stateless sessions, high-availability transport, structured observability, and a 95%+ tested core.
51
- - **Extensible** — plugins, lifecycle hooks, OpenAPI adapters, and external MCP sub-apps when you outgrow the defaults.
58
+ <div align="center">
59
+ <picture>
60
+ <source media="(prefers-color-scheme: dark)" srcset="docs/assets/readme/features.svg">
61
+ <img src="docs/assets/readme/features.light.svg" alt="Typed end to end, auth built in, ship anywhere, every stage is a hook, tools with a face, one endpoint for any client" width="100%">
62
+ </picture>
63
+ </div>
52
64
 
53
- ## Installation
65
+ <br>
54
66
 
55
- **Node.js 24+** required.
67
+ ## Start
56
68
 
57
69
  ```bash
58
- # New project (recommended)
59
- npx frontmcp create my-app
60
-
61
- # Existing project
62
- npm i -D frontmcp @types/node@^24
63
- npx frontmcp init
70
+ npx frontmcp create my-app # new project (Node 24+)
71
+ npx frontmcp init # or add FrontMCP to an existing one
64
72
  ```
65
73
 
66
- > Full setup guide: [Installation][docs-install] &middot; [Quickstart][docs-quickstart]
67
-
68
- ## Capabilities
69
-
70
- **Build** — decorator-configured [`@FrontMcp` server][docs-server] and [`@App`][docs-apps]
71
- domains; typed [`@Tool`][docs-tools], [`@Resource`][docs-resources], and
72
- [`@Prompt`][docs-prompts] primitives; [`@Agent`][docs-agents] multi-step chains; and
73
- scoped [Providers / DI][docs-providers].
74
-
75
- **Secure** — [Remote & Local OAuth, JWKS, DCR, per-app auth][docs-auth] with
76
- stateful / stateless [sessions][docs-server] (JWT or UUID transport IDs).
77
-
78
- **Connect & operate** — [Streamable HTTP + SSE transport][docs-transport],
79
- every [MCP protocol revision][docs-protocol] from `2024-11-05` through
80
- `2026-07-28` on one endpoint, capability [discovery][docs-discovery],
81
- [elicitation][docs-elicitation], [hooks][docs-hooks], HTTP-discoverable
82
- [skills][docs-skills], [tool UI / MCP Apps][docs-ext-apps], an in-process
83
- [Direct Client][docs-direct] (`connectOpenAI` / `connectClaude`), and
84
- first-class [deployment][docs-deploy].
85
-
86
- **Extend & tooling** — official [plugins][docs-plugins] (Cache, Remember, CodeCall,
87
- Dashboard), the [OpenAPI adapter][docs-adapters], a [UI library][docs-ui] (HTML/React
88
- widgets, SSR, MCP Bridge), an [E2E testing framework][docs-testing], and a
89
- [CLI][docs-install] (`create`, `init`, `dev`, `build`, `inspect`, `doctor`).
90
-
91
- → Full reference: **[docs.agentfront.dev/frontmcp][docs-home]**
74
+ Then follow **[Learn][docs-learn]** on [frontmcp.dev](https://frontmcp.dev), or browse [Tools][docs-tools], [Resources][docs-resources], [Prompts][docs-prompts], [Agents][docs-agents], [Auth][docs-auth], [Plugins][docs-plugins], [Tool UI][docs-ext-apps], [Testing][docs-testing] and [Deployment][docs-deploy]. Try it live in the [Playground][docs-playground].
92
75
 
93
76
  ## Packages
94
77
 
95
- You install `frontmcp` (the CLI) and `@frontmcp/sdk`. Everything else is either
96
- pulled in for you or opt-in.
78
+ Install `frontmcp` (CLI) and `@frontmcp/sdk`. The rest is pulled in for you, or opt-in. Keep every `@frontmcp/*` package on the same version; a mismatch fails fast at boot ([why][docs-production]).
79
+
80
+ <details>
81
+ <summary><b>Core and extensions</b></summary>
82
+ <br>
83
+
84
+ | Package | What it does |
85
+ | --------------------------------------------------- | --------------------------------------------------------------- |
86
+ | [`frontmcp`](libs/cli) | CLI: `create`, `init`, `dev`, `build`, `inspect`, `doctor` |
87
+ | [`@frontmcp/sdk`](libs/sdk) | Core framework: decorators, DI, flows, transport, MCP protocol |
88
+ | [`@frontmcp/auth`](libs/auth) | OAuth, JWKS, DCR/CIMD, sessions, credential vault |
89
+ | [`@frontmcp/testing`](libs/testing) | E2E test framework with fixtures and matchers |
90
+ | [`@frontmcp/adapters`](libs/adapters) | Generate tools from an OpenAPI spec |
91
+ | [`@frontmcp/skills`](libs/skills) | Curated `SKILL.md` catalog for scaffolding and `skills install` |
92
+ | [`@frontmcp/guard`](libs/guard) | Rate limits, concurrency and policy guards |
93
+ | [`@frontmcp/observability`](libs/observability) | Structured logging, metrics and tracing |
94
+ | [`@frontmcp/react`](libs/react) | React hooks and client for a FrontMCP server |
95
+ | [`@frontmcp/ui`](libs/ui) / [`uipack`](libs/uipack) | Widgets, SSR renderers, MCP Bridge, themes |
96
+ | [`@frontmcp/edge`](libs/edge) | Run a server on Cloudflare Workers / V8 isolates |
97
+ | [`@frontmcp/storage-sqlite`](libs/storage-sqlite) | SQLite session, task and elicitation stores |
98
+ | [`@frontmcp/nx`](libs/nx-plugin) | Nx generators and executors |
99
+
100
+ Internal, published so the above resolve: [`protocol`](libs/protocol), [`di`](libs/di), [`utils`](libs/utils), [`lazy-zod`](libs/lazy-zod).
101
+
102
+ </details>
103
+
104
+ <details>
105
+ <summary><b>Official plugins</b></summary>
106
+ <br>
107
+
108
+ | Package | What it does |
109
+ | -------------------------------------------------------------------- | ----------------------------------------------- |
110
+ | [`@frontmcp/plugin-cache`](plugins/plugin-cache) | Cache tool results with a TTL |
111
+ | [`@frontmcp/plugin-remember`](plugins/plugin-remember) | Per-session memory (`this.remember`) |
112
+ | [`@frontmcp/plugin-approval`](plugins/plugin-approval) | Human approval gates before a tool runs |
113
+ | [`@frontmcp/plugin-codecall`](plugins/plugin-codecall) | Let the model compose tool calls as code |
114
+ | [`@frontmcp/plugin-dashboard`](plugins/plugin-dashboard) | Built-in web dashboard |
115
+ | [`@frontmcp/plugin-feature-flags`](plugins/plugin-feature-flags) | Toggle tools and apps at runtime |
116
+ | [`@frontmcp/plugin-skilled-openapi`](plugins/plugin-skilled-openapi) | OpenAPI to skills and meta-tools for big APIs |
117
+ | [`@frontmcp/plugin-webmcp`](plugins/plugin-webmcp) | Expose in-page tools to browser agents (WebMCP) |
118
+
119
+ </details>
120
+
121
+ <br>
97
122
 
98
- ### Core
99
-
100
- | Package | Description |
101
- | ----------------------------------- | --------------------------------------------------------------- |
102
- | [`frontmcp`](libs/cli) | The CLI — `create`, `init`, `dev`, `build`, `inspect`, `doctor` |
103
- | [`@frontmcp/sdk`](libs/sdk) | Core framework — decorators, DI, flows, transport, MCP protocol |
104
- | [`@frontmcp/auth`](libs/auth) | Authentication, OAuth, JWKS, DCR/CIMD, credential vault |
105
- | [`@frontmcp/testing`](libs/testing) | E2E test framework with fixtures and matchers |
106
-
107
- ### Extend
108
-
109
- | Package | Description |
110
- | ----------------------------------------------- | ------------------------------------------------------------- |
111
- | [`@frontmcp/plugins`](libs/plugins) | Plugin authoring toolkit + official plugin re-exports |
112
- | [`@frontmcp/adapters`](libs/adapters) | OpenAPI adapter — generate tools from an OpenAPI spec |
113
- | [`@frontmcp/skills`](libs/skills) | Curated SKILL.md catalog for scaffolding and `skills install` |
114
- | [`@frontmcp/guard`](libs/guard) | Policy/guard rules for tool inputs and outputs |
115
- | [`@frontmcp/observability`](libs/observability) | Structured logging, metrics, and tracing helpers |
116
-
117
- ### UI
118
-
119
- | Package | Description |
120
- | --------------------------------- | ----------------------------------------------------- |
121
- | [`@frontmcp/react`](libs/react) | React hooks + client for talking to a FrontMCP server |
122
- | [`@frontmcp/ui`](libs/ui) | React components, SSR renderers, MCP Bridge |
123
- | [`@frontmcp/uipack`](libs/uipack) | React-free themes, build tools, platform adapters |
124
-
125
- ### Runtime & storage
126
-
127
- | Package | Description |
128
- | ------------------------------------------------- | -------------------------------------------------------------- |
129
- | [`@frontmcp/edge`](libs/edge) | Run a server on Cloudflare Workers / V8 isolates from a config |
130
- | [`@frontmcp/storage-sqlite`](libs/storage-sqlite) | SQLite-backed session, task, and elicitation stores |
131
- | [`@frontmcp/nx`](libs/nx-plugin) | Nx generators and executors for FrontMCP workspaces |
132
-
133
- ### Internal
134
-
135
- Published so the packages above resolve, but not intended for direct use:
136
-
137
- | Package | Description |
138
- | ------------------------------------- | ------------------------------------------------------------ |
139
- | [`@frontmcp/protocol`](libs/protocol) | The single boundary to the upstream MCP SDK — protocol types |
140
- | [`@frontmcp/di`](libs/di) | Dependency injection container |
141
- | [`@frontmcp/utils`](libs/utils) | Shared utilities — naming, URI, crypto, FS |
142
- | [`@frontmcp/lazy-zod`](libs/lazy-zod) | Lazily-loaded Zod wrapper that keeps cold starts small |
143
-
144
- ### Official plugins
145
-
146
- | Package | Description |
147
- | -------------------------------------------------------------------- | -------------------------------------------- |
148
- | [`@frontmcp/plugin-cache`](plugins/plugin-cache) | Cache tool results with a TTL |
149
- | [`@frontmcp/plugin-remember`](plugins/plugin-remember) | Per-session memory (`this.remember`) |
150
- | [`@frontmcp/plugin-approval`](plugins/plugin-approval) | Human approval gates before a tool runs |
151
- | [`@frontmcp/plugin-codecall`](plugins/plugin-codecall) | Let the model compose tool calls as code |
152
- | [`@frontmcp/plugin-dashboard`](plugins/plugin-dashboard) | Built-in web dashboard |
153
- | [`@frontmcp/plugin-feature-flags`](plugins/plugin-feature-flags) | Toggle tools and apps at runtime |
154
- | [`@frontmcp/plugin-skilled-openapi`](plugins/plugin-skilled-openapi) | OpenAPI → skills + meta-tools for large APIs |
155
-
156
- ## Version Alignment
157
-
158
- Keep all `@frontmcp/*` packages on the same version. A clear **"version mismatch"** error is thrown at boot if versions drift. ([Production Build][docs-production])
159
-
160
- ## Contributing
123
+ <div align="center">
161
124
 
162
- PRs welcome! See [CONTRIBUTING.md](./CONTRIBUTING.md) for workflow, coding standards, and the PR checklist.
125
+ PRs welcome: see [CONTRIBUTING](./CONTRIBUTING.md). Released under the [Apache-2.0](./LICENSE) license.
163
126
 
164
- ## License
127
+ Docs also mirrored at [docs.agentfront.dev/frontmcp](https://docs.agentfront.dev/frontmcp).
165
128
 
166
- [Apache-2.0](./LICENSE)
129
+ </div>
167
130
 
168
131
  <!-- docs links -->
169
132
 
170
- [docs-home]: https://docs.agentfront.dev/frontmcp 'FrontMCP Docs'
171
- [docs-install]: https://docs.agentfront.dev/frontmcp/getting-started/installation 'Installation'
172
- [docs-quickstart]: https://docs.agentfront.dev/frontmcp/getting-started/quickstart 'Quickstart'
173
- [docs-sdk-ref]: https://docs.agentfront.dev/frontmcp/sdk-reference/decorators/overview 'SDK Reference'
174
- [docs-server]: https://docs.agentfront.dev/frontmcp/servers/server 'The FrontMCP Server'
175
- [docs-apps]: https://docs.agentfront.dev/frontmcp/servers/apps 'Apps'
176
- [docs-tools]: https://docs.agentfront.dev/frontmcp/servers/tools 'Tools'
177
- [docs-resources]: https://docs.agentfront.dev/frontmcp/servers/resources 'Resources'
178
- [docs-prompts]: https://docs.agentfront.dev/frontmcp/servers/prompts 'Prompts'
179
- [docs-agents]: https://docs.agentfront.dev/frontmcp/servers/agents 'Agents'
180
- [docs-elicitation]: https://docs.agentfront.dev/frontmcp/servers/elicitation 'Elicitation'
181
- [docs-skills]: https://docs.agentfront.dev/frontmcp/servers/skills 'Skills'
182
- [docs-discovery]: https://docs.agentfront.dev/frontmcp/servers/discovery 'Discovery'
183
- [docs-protocol]: https://docs.agentfront.dev/frontmcp/fundamentals/protocol-versions 'Protocol Versions'
184
- [docs-auth]: https://docs.agentfront.dev/frontmcp/authentication/overview 'Authentication'
185
- [docs-direct]: https://docs.agentfront.dev/frontmcp/deployment/direct-client 'Direct Client'
186
- [docs-transport]: https://docs.agentfront.dev/frontmcp/deployment/transport-security 'Transport'
187
- [docs-ext-apps]: https://docs.agentfront.dev/frontmcp/guides/building-tool-ui 'Tool UI / MCP Apps'
188
- [docs-hooks]: https://docs.agentfront.dev/frontmcp/sdk-reference/decorators/hooks 'Hooks'
189
- [docs-providers]: https://docs.agentfront.dev/frontmcp/extensibility/providers 'Providers'
190
- [docs-plugins]: https://docs.agentfront.dev/frontmcp/plugins/overview 'Plugins'
191
- [docs-adapters]: https://docs.agentfront.dev/frontmcp/adapters/overview 'Adapters'
192
- [docs-testing]: https://docs.agentfront.dev/frontmcp/testing/overview 'Testing'
193
- [docs-ui]: https://docs.agentfront.dev/frontmcp/react/overview 'React SDK'
194
- [docs-deploy]: https://docs.agentfront.dev/frontmcp/deployment/local-dev-server 'Deployment'
195
- [docs-production]: https://docs.agentfront.dev/frontmcp/deployment/production-build 'Production Build'
133
+ [docs-learn]: https://frontmcp.dev/learn 'Learn FrontMCP'
134
+ [docs-reference]: https://frontmcp.dev/reference 'Reference'
135
+ [docs-examples]: https://frontmcp.dev/examples 'Examples'
136
+ [docs-playground]: https://frontmcp.dev/playground 'Playground'
137
+ [docs-blog]: https://frontmcp.dev/blog 'Blog'
138
+ [docs-tools]: https://frontmcp.dev/reference/sdk/tool 'Tools'
139
+ [docs-resources]: https://frontmcp.dev/reference/sdk/resource 'Resources'
140
+ [docs-prompts]: https://frontmcp.dev/reference/sdk/prompt 'Prompts'
141
+ [docs-agents]: https://frontmcp.dev/reference/sdk/agent 'Agents'
142
+ [docs-auth]: https://frontmcp.dev/reference/auth/modes 'Authentication'
143
+ [docs-ext-apps]: https://frontmcp.dev/learn/tools-with-a-ui 'Tool UI / MCP Apps'
144
+ [docs-plugins]: https://frontmcp.dev/reference/plugins 'Plugins'
145
+ [docs-testing]: https://frontmcp.dev/reference/testing 'Testing'
146
+ [docs-deploy]: https://frontmcp.dev/reference/deployment/node 'Deployment'
147
+ [docs-production]: https://frontmcp.dev/reference/deployment/production-build 'Production Build'
@@ -83,7 +83,7 @@ export class RotateSecretsTool extends ToolContext {
83
83
  | `deployment` | `'serverless'`, `'standalone'`, `'distributed'`, `'browser'` |
84
84
  | `provider` | `'bare'`, `'docker'`, `'vercel'`, `'lambda'`, `'cloudflare'`, … |
85
85
  | `target` | `'cli'`, `'node'`, `'vercel'`, `'lambda'`, `'cloudflare'`, … (set by `frontmcp build --target`) |
86
- | `surface` | `'mcp'`, `'cli'`, `'agent'`, `'job'`, `'http-trigger'` — per-call axis |
86
+ | `surface` | `'mcp'`, `'cli'`, `'agent'`, `'job'`, `'http-trigger'`, `'webmcp'` — per-call axis |
87
87
  | `env` | `'production'`, `'development'`, `'test'` |
88
88
 
89
89
  Multiple axes are AND-ed. Multiple values within an axis are OR-ed.
@@ -26,15 +26,15 @@ On Linux / Windows servers, this tool simply doesn't exist — it's not in `tool
26
26
 
27
27
  ## Axes
28
28
 
29
- | Axis | Values | Source |
30
- | ------------ | ------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
31
- | `os` | `'darwin'`, `'linux'`, `'win32'` | `process.platform` (since #417 — was previously `platform`) |
32
- | `runtime` | `'node'`, `'browser'`, `'edge'`, `'bun'`, `'deno'` | Detected at boot |
33
- | `deployment` | `'serverless'`, `'standalone'`, `'distributed'`, `'browser'` | Detected from `frontmcp.config` / env |
34
- | `provider` | `'bare'`, `'docker'`, `'vercel'`, `'lambda'`, `'cloudflare'`, `'netlify'`, `'azure'`, `'gcp'`, `'fly'`, `'render'`, `'railway'` | Auto-detected; override with `FRONTMCP_PROVIDER=<name>` |
35
- | `target` | `'cli'`, `'node'`, `'vercel'`, `'lambda'`, `'cloudflare'`, `'browser'`, `'sdk'`, `'mcpb'`, `'distributed'` | Set by `frontmcp build --target <x>`; `'unknown'` in dev |
36
- | `surface` | `'mcp'`, `'cli'`, `'agent'`, `'job'`, `'http-trigger'` | Per-call axis — which entry point is invoking the tool |
37
- | `env` | `'production'`, `'development'`, `'test'` | `process.env.NODE_ENV` |
29
+ | Axis | Values | Source |
30
+ | ------------ | ------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
31
+ | `os` | `'darwin'`, `'linux'`, `'win32'` | `process.platform` (since #417 — was previously `platform`) |
32
+ | `runtime` | `'node'`, `'browser'`, `'edge'`, `'bun'`, `'deno'` | Detected at boot |
33
+ | `deployment` | `'serverless'`, `'standalone'`, `'distributed'`, `'browser'` | Detected from `frontmcp.config` / env |
34
+ | `provider` | `'bare'`, `'docker'`, `'vercel'`, `'lambda'`, `'cloudflare'`, `'netlify'`, `'azure'`, `'gcp'`, `'fly'`, `'render'`, `'railway'` | Auto-detected; override with `FRONTMCP_PROVIDER=<name>` |
35
+ | `target` | `'cli'`, `'node'`, `'vercel'`, `'lambda'`, `'cloudflare'`, `'browser'`, `'sdk'`, `'mcpb'`, `'distributed'` | Set by `frontmcp build --target <x>` in every artifact (`globalThis.FRONTMCP_BUILD_TARGET`; first to run wins, so a `cli` binary loading its server bundle stays `'cli'`); `'unknown'` in dev |
36
+ | `surface` | `'mcp'`, `'cli'`, `'agent'`, `'job'`, `'http-trigger'`, `'webmcp'` | Per-call axis — which entry point is invoking the tool |
37
+ | `env` | `'production'`, `'development'`, `'test'` | `NODE_ENV`, read live (a Worker's `[vars]` beats a bundler's inlined constant) |
38
38
 
39
39
  ## Semantics
40
40
 
@@ -100,7 +100,7 @@ These are fine for ergonomic branching. For tools that **shouldn't exist at all*
100
100
 
101
101
  This is the safest way to expose internal-only tools that you want an agent / job to call but don't want a user to invoke from a chat UI.
102
102
 
103
- An MCP client (and the in-process client of a CLI build, surface `'cli'`) never sees such a tool: it is absent from `tools/list`, and `tools/call` answers `Tool "rotate_secrets" not found`, exactly as for a tool that doesn't exist. Resources, resource templates, prompts, agents and skills (including the skills HTTP endpoints, which count as `'mcp'`) follow the same rule, and CodeCall applies its caller's surface to the tools it reaches. An agent's model calls its tools on `'agent'` (and is only offered those its `surface` allows), a job's or workflow step's `this.callTool()` on `'job'`, and a `@Channel` handling a webhook on `'http-trigger'`. A tool, resource or prompt calling `this.callTool()` is in-process dispatch: that call carries no surface and is not restricted. Code reads its call's surface with `getCallSurface()`. The process-wide axes (`os`, `runtime`, ...) answer `EntryUnavailableError` instead.
103
+ An MCP client (and the in-process client of a CLI build, surface `'cli'`) never sees such a tool: it is absent from `tools/list`, and `tools/call` answers `Tool "rotate_secrets" not found`, exactly as for a tool that doesn't exist. Resources, resource templates, prompts, agents and skills (including the skills HTTP endpoints, which count as `'mcp'`) follow the same rule, and CodeCall applies its caller's surface to the tools it reaches. An agent's model calls its tools on `'agent'` (and is only offered those its `surface` allows), a job's or workflow step's `this.callTool()` on `'job'`, a `@Channel` handling a webhook on `'http-trigger'`, and an in-browser agent calling through WebMCP (`@frontmcp/plugin-webmcp`) on `'webmcp'`. A tool, resource or prompt calling `this.callTool()` is in-process dispatch: that call carries no surface and is not restricted. Code reads its call's surface with `getCallSurface()`. The process-wide axes (`os`, `runtime`, ...) answer `EntryUnavailableError` instead.
104
104
 
105
105
  ## See also
106
106
 
@@ -39,12 +39,12 @@ That's it. The framework:
39
39
 
40
40
  ## Template formats
41
41
 
42
- | Format | Shape | When |
43
- | ---------------------------- | --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
44
- | **FileSource (recommended)** | `{ file: widgetPath }` | `.tsx` / `.jsx` / `.html` source files. Anchor with `import.meta.url`. |
45
- | **Function** | `` (ctx) => ctx.helpers.html`…` `` | Quick demo / one-liner HTML. Annotate `ctx: TemplateContext<In, Out>` ([why](#typescript-gotcha-ts7006)). |
46
- | **HTML / Markdown string** | `'<div>…</div>'` or `'# Title\n- item'` | A string with both `<` and `>` is HTML as written; any other string is Markdown, converted on the server. MDX is **not** compiled. |
47
- | **React component** | `MyWidget` | A bare component reference cannot be bundled for the widget; use the `{ file }` form instead. |
42
+ | Format | Shape | When |
43
+ | ---------------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
44
+ | **FileSource (recommended)** | `{ file: widgetPath }` | `.tsx` / `.jsx` / `.html` source files. Anchor with `import.meta.url`. |
45
+ | **Function** | `` (ctx) => ctx.helpers.html`…` `` | Quick demo / one-liner HTML. Annotate `ctx: TemplateContext<In, Out>` ([why](#typescript-gotcha-ts7006)). |
46
+ | **HTML / Markdown string** | `'<div>…</div>'` or `'# Title\n- item'` | A string with both `<` and `>` is HTML as written; any other string is Markdown, converted on the server. MDX is **not** compiled. |
47
+ | **React component** | `MyWidget` | Not supported: a bare component reference cannot be bundled, so the page is an empty root. Startup warns once per tool; use `{ file }`. |
48
48
 
49
49
  The renderer auto-detects which one you passed.
50
50
 
@@ -74,7 +74,7 @@ Or use the FileSource form — it sidesteps the issue.
74
74
  | `minHeight` / `maxHeight` | — | `number` (px) or CSS string. Clamp the widget height; auto-resize never reports outside this range. |
75
75
  | `aspectRatio` | — | CSS `aspect-ratio` (`'16 / 9'` or `1.5`). Hosts that honor it size by ratio instead of measured height. |
76
76
  | `autoResize` | `true` | Report the document height (margins included) to the host after the handshake. Set `false` to opt out (CSS still applies). |
77
- | `csp` | — | `{ connectDomains?, resourceDomains? }` — emitted on the resource content's `_meta.ui.csp` (#455). Claude honors CSP only here. |
77
+ | `csp` | — | `{ connectDomains?, resourceDomains? }` — emitted on the resource content's `_meta.ui.csp` (#455). Claude honors CSP only here. See [CSP origins](#csp-origins). |
78
78
  | `contentSecurity` | strict | **No effect yet.** |
79
79
  | `escapeStringResults` | unset | `true` escapes plain string results of a template function; `html` / `trustedHtml` stay markup. Default in 1.9. |
80
80
  | `widgetAccessible` | `false` | **No effect yet.** |
@@ -117,9 +117,29 @@ export default function Widget({ output }: { output: { id: string } | null }) {
117
117
 
118
118
  - `resources/read ui://widget/{toolName}.html` serves only what was compiled at startup (`static`, the `hybrid` shell), rendered without caller data, or a data-free placeholder that gets the result through the bridge.
119
119
  - An `inline` render embeds the call's input and output. It is returned only in that call's `_meta['ui/html']` and is never cached where `resources/read` can serve it, so one caller can't read another caller's widget (GHSA-rhr9-vhpf-jqp7).
120
+ - A page compiled at startup injects no call data (`window.__mcpToolInput` / `window.__mcpToolOutput` are `null`), so the bridge takes the result from the host: `window.openai.toolOutput` (ChatGPT, read at load and followed after) or `ui/notifications/tool-result` (MCP Apps). A `.tsx` widget renders with `loading: true` until it arrives, and `useToolOutput()` returns `null` until then.
120
121
  - Hosts that load the widget via `resources/read` (MCP Apps hosts such as Claude) need `servingMode: 'static'` and a template that reads data from `window.FrontMcpBridge`; set `resourceMode: 'inline'` explicitly for Claude in static mode.
122
+ - With the default `servingMode`, every `tools/call` result carries the page in `_meta['ui/html']` — hosts that load the `ui://` resource don't read it, so use `servingMode: 'static'` to leave it out. A call the widget makes back through `ui/callServerTool` gets the data only.
121
123
  - The advertised URI percent-encodes the tool name (`app:tool` → `ui://widget/app%3Atool.html`). Encoded and raw forms both read back; a name that decodes to anything outside `A-Z a-z 0-9 _ - . / : @` is rejected.
122
124
 
125
+ ## CSP origins
126
+
127
+ The page FrontMCP writes also carries its own Content-Security-Policy built from `ui.csp`:
128
+
129
+ - An origin must be `https://` or `wss://` (a WebSocket API needs `wss://` in `connectDomains`), a `https://*.` / `wss://*.` wildcard, or `http://` / `ws://` on `localhost` / `127.0.0.1` / `[::1]`.
130
+ - Declared origins are added to what the page already reaches: the CDNs and `resourceDomains` stay in `connect-src`.
131
+ - Any other origin (bare host, `ftp://`, plain `http://` host, a value with a `?query` or `#fragment`) is left out of the page policy; startup logs a warning naming the tool and the origin. The resource `_meta.ui.csp` keeps the origins as written.
132
+
133
+ ```typescript
134
+ ui: {
135
+ template: { file: widgetPath },
136
+ csp: {
137
+ connectDomains: ['https://api.example.com', 'wss://live.example.com'],
138
+ resourceDomains: ['https://cdn.example.com'],
139
+ },
140
+ }
141
+ ```
142
+
123
143
  ## Trusted markup and escaping template results
124
144
 
125
145
  Build function-template markup with the `ctx.helpers.html` tagged template. Literal parts stay markup; every interpolated value is HTML-escaped unless it is itself trusted markup:
@@ -193,7 +213,7 @@ Match the version to `@frontmcp/sdk`. Without it, server-side bundling fails wit
193
213
  npm install esbuild # in "dependencies", not "devDependencies"
194
214
  ```
195
215
 
196
- Projects created with `frontmcp create` already have it through the `frontmcp` package. `@frontmcp/uipack` declares it as an optional peer dependency (`>=0.27.0 <1`). Without it, the call fails with an error naming the widget.
216
+ Projects created with `frontmcp create` already have it through the `frontmcp` package. `@frontmcp/uipack` declares it as an optional peer dependency (`>=0.27.0 <1`). Without it, the call fails with an error naming the widget. Bundling works the same in CommonJS and ES-module (`"type": "module"`) projects.
197
217
 
198
218
  ## Widget bridge — `window.FrontMcpBridge`
199
219
 
@@ -244,6 +264,8 @@ The page follows the host theme. When an MCP Apps host sends `theme: 'light' | '
244
264
  | **MCP Inspector** | Useful for local development. Static mode works fine. |
245
265
  | **Gemini / unknown** | `ui` is ignored — JSON output is returned. |
246
266
 
267
+ The host is decided when the client connects: a `transport.platformDetection.mappings` entry matching the client name wins; then a client that declares the MCP Apps extension (`io.modelcontextprotocol/ui`) is `ext-apps` whatever its name (`gemini-cli` included); then the client name. Opt a client out of MCP Apps with a mapping: `platformDetection: { mappings: [{ pattern: 'gemini-cli', platform: 'gemini' }] }`.
268
+
247
269
  ## Widget sizing
248
270
 
249
271
  Set sizing in the `ui` config — no hand-rolled `ui/notifications/size-changed` + `ResizeObserver` needed:
@@ -287,6 +287,11 @@ authorities: {
287
287
  }
288
288
  ```
289
289
 
290
+ Tools, resources, agents and jobs (workflow steps included) run the pipes when their context is built, before any hook
291
+ or `execute()` reads `this.auth`, so `this.auth.tenantId` is set there. Pipes can be async; one that throws is logged
292
+ and leaves its fields `undefined`. Pipes extend `this.auth` only -- authority policies still evaluate the claims from
293
+ `claimsMapping` / `claimsResolver`. Up to 1.8.7 `pipes` never ran (their fields were always `undefined`).
294
+
290
295
  ### Skill Authorities (`@Skill({ authorities })`)
291
296
 
292
297
  `authorities` on `@Skill` is enforced exactly like the other entry types, across
@@ -75,13 +75,13 @@ Build push-based notification channels that stream real-time events into Claude
75
75
 
76
76
  ## Common Patterns
77
77
 
78
- | Pattern | Correct | Incorrect | Why |
79
- | -------------- | --------------------------------------- | -------------------------------------------- | ------------------------------------------------------------------ |
80
- | Meta keys | `meta: { env: 'prod' }` | `meta: { 'my-env': 'prod' }` | Meta keys must be valid identifiers (letters, digits, underscores) |
81
- | Source naming | `name: 'deploy-alerts'` | `name: 'Deploy Alerts!'` | Channel names should be kebab-case identifiers |
82
- | Two-way gating | Check sender identity before emitting | Trust room/group membership | Prevent prompt injection from untrusted group members |
83
- | Error channels | Use `app-event` source with event bus | Poll for errors in a loop | Event bus is push-based and efficient |
84
- | Manual push | Use `scope.channelNotifications.send()` | Call `pushNotification` on instance directly | Service handles capability filtering |
78
+ | Pattern | Correct | Incorrect | Why |
79
+ | -------------- | ------------------------------------------ | ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
80
+ | Meta keys | `meta: { env: 'prod' }` | `meta: { 'my-env': 'prod' }` | Meta keys must be valid identifiers (letters, digits, underscores) |
81
+ | Source naming | `name: 'deploy-alerts'` | `name: 'Deploy Alerts!'` | Channel names should be kebab-case identifiers |
82
+ | Two-way gating | Check sender identity before emitting | Trust room/group membership | Prevent prompt injection from untrusted group members |
83
+ | Error channels | Use `app-event` source with event bus | Poll for errors in a loop | Event bus is push-based and efficient |
84
+ | Manual push | `await scope.channelNotifications?.send()` | `sendToSubscribedSessions()` for a regular push | `send()` runs the hookable `channels:send-notification` flow (defaultMeta, channel meta, hooks); the `sendTo*` methods deliver directly |
85
85
 
86
86
  ## Verification Checklist
87
87
 
@@ -102,24 +102,25 @@ Build push-based notification channels that stream real-time events into Claude
102
102
 
103
103
  - [ ] `twoWay: true` is set on channels that need replies
104
104
  - [ ] `channel-reply` tool appears in tool list
105
- - [ ] `onReply()` is implemented and forwards to external system
105
+ - [ ] `onReply()` is implemented and forwards to external system (if it throws, `channel-reply` answers an error result)
106
106
  - [ ] Sender authentication is enforced before emitting events
107
107
 
108
108
  ### Sources
109
109
 
110
110
  - [ ] Webhook endpoints return 200 on success
111
- - [ ] Event bus subscriptions are cleaned up on scope teardown
111
+ - [ ] Event bus subscriptions are cleaned up on scope teardown, and service channels get `onDisconnect()` on shutdown and on `dispose()`
112
112
  - [ ] Agent/job completion filters match expected IDs
113
113
 
114
114
  ## Troubleshooting
115
115
 
116
- | Problem | Cause | Solution |
117
- | ---------------------------- | ------------------------------- | ------------------------------------------------------------------ |
118
- | No notifications arrive | Client doesn't support channels | Check client capabilities include `experimental['claude/channel']` |
119
- | `channel-reply` tool missing | No two-way channels registered | Set `twoWay: true` on at least one channel |
120
- | Webhook returns 500 | `onEvent()` throws | Check channel handler error logs |
121
- | Duplicate notifications | Multiple sessions subscribed | This is correct behavior -- each session gets its own copy |
122
- | Events lost on reconnect | Channels are in-memory | Channel state resets on server restart; use persistent sources |
116
+ | Problem | Cause | Solution |
117
+ | ---------------------------- | -------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
118
+ | No notifications arrive | Client doesn't support channels | Check client capabilities include `experimental['claude/channel']` |
119
+ | `channel-reply` tool missing | No two-way channels registered | Set `twoWay: true` on at least one channel |
120
+ | Webhook returns 500 | `onEvent()` throws | Check channel handler error logs |
121
+ | A notification never arrives | A `ChannelSendHook` stopped it, or `ChannelListHook` left the channel out of the session's subscriptions | Check plugins hooking `channels:send-notification` / `channels:list` |
122
+ | Duplicate notifications | Multiple sessions subscribed | This is correct behavior -- each session gets its own copy |
123
+ | Events lost on reconnect | Channels are in-memory | Channel state resets on server restart; use persistent sources |
123
124
 
124
125
  ## Examples
125
126
 
@@ -59,8 +59,8 @@ class ErrorAlertChannel extends ChannelContext {
59
59
  }
60
60
  }
61
61
 
62
- // In your application code:
63
- scope.channelEventBus.emit('app:error', {
62
+ // In your application code (in a tool: this.scope.channelEventBus):
63
+ scope.channelEventBus?.emit('app:error', {
64
64
  message: 'Connection refused',
65
65
  stack: 'Error: ECONNREFUSED...',
66
66
  level: 'critical',
@@ -181,7 +181,7 @@ class LogWatcherChannel extends ChannelContext {
181
181
 
182
182
  ## Manual Source
183
183
 
184
- No automatic wiring. Push notifications programmatically via `scope.channelNotifications.send()`.
184
+ No automatic wiring. Push notifications programmatically via `scope.channelNotifications.send()`, which runs the hookable `channels:send-notification` flow like every channel notification: `channels.defaultMeta`, then the channel's `meta`, then the notification's own meta, with `source` set to the channel name.
185
185
 
186
186
  ```typescript
187
187
  const StatusChannel = channel({
@@ -193,7 +193,7 @@ const StatusChannel = channel({
193
193
  }));
194
194
 
195
195
  // Push from anywhere with scope access:
196
- scope.channelNotifications.send('status-updates', 'Server maintenance starting in 5 minutes');
196
+ await scope.channelNotifications?.send('status-updates', 'Server maintenance starting in 5 minutes');
197
197
  ```
198
198
 
199
199
  ## Replay Buffer
@@ -13,7 +13,7 @@ Two-way channels let external users communicate with Claude Code through messagi
13
13
  2. **Transform**: `onEvent()` converts to `ChannelNotification`
14
14
  3. **Push**: Notification sent to Claude Code session
15
15
  4. **Reply**: Claude calls `channel-reply` tool with response text
16
- 5. **Forward**: `onReply()` sends the reply back to the external platform
16
+ 5. **Forward**: `onReply()` sends the reply back to the external platform. If it throws, `channel-reply` answers an error result with the message, so Claude knows the reply did not go out (up to 1.8.7 the tool reported success)
17
17
 
18
18
  ## API Surface
19
19
 
@@ -70,6 +70,8 @@ frontmcp build --target node
70
70
  FRONTMCP_DEPLOYMENT_MODE=distributed frontmcp build --target distributed
71
71
  ```
72
72
 
73
+ The distributed build writes the `ha` block to `FRONTMCP_HA_HEARTBEAT_INTERVAL_MS`, `FRONTMCP_HA_HEARTBEAT_TTL_MS`, `FRONTMCP_HA_TAKEOVER_GRACE_MS` and `FRONTMCP_HA_KEY_PREFIX` in the generated setup file (only where the platform has not set them); each pod reads them at startup. Give every pod the same `MCP_SESSION_SECRET` so any pod can route a session's request to the pod that owns it.
74
+
73
75
  ### Server Code
74
76
 
75
77
  ```typescript
@@ -19,7 +19,7 @@ Configure Vercel KV for session storage in serverless Vercel deployments.
19
19
 
20
20
  ```typescript
21
21
  // src/server.ts
22
- import { FrontMcp, App } from '@frontmcp/sdk';
22
+ import { App, FrontMcp } from '@frontmcp/sdk';
23
23
 
24
24
  @App({ name: 'my-app' })
25
25
  class MyApp {}
@@ -33,7 +33,6 @@ class MyApp {}
33
33
  },
34
34
  transport: {
35
35
  protocol: 'stateless-api',
36
- sessionMode: 'stateless',
37
36
  },
38
37
  })
39
38
  class Server {}
@@ -44,7 +44,6 @@ class DevtoolsApp {}
44
44
  info: { name: 'custom-protocol-server', version: '1.0.0' },
45
45
  apps: [DevtoolsApp],
46
46
  transport: {
47
- sessionMode: 'stateful',
48
47
  protocol: {
49
48
  sse: true, // SSE endpoint enabled
50
49
  streamable: true, // Streamable HTTP POST enabled
@@ -44,7 +44,6 @@ class ReportsApp {}
44
44
  info: { name: 'distributed-server', version: '1.0.0' },
45
45
  apps: [ReportsApp],
46
46
  transport: {
47
- sessionMode: 'stateful',
48
47
  protocol: 'modern',
49
48
  distributedMode: true,
50
49
  persistence: {