@frontmcp/skills 1.8.6 → 1.9.0

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 (77) hide show
  1. package/README.md +107 -155
  2. package/catalog/create-tool/SKILL.md +24 -24
  3. package/catalog/create-tool/examples/21-tool-with-availability-constraints.md +1 -1
  4. package/catalog/create-tool/examples/22-tool-with-ui-html-template.md +0 -1
  5. package/catalog/create-tool/examples/23-tool-with-ui-filesource-tsx.md +1 -3
  6. package/catalog/create-tool/examples/24-tool-with-ui-csp-and-bridge.md +4 -6
  7. package/catalog/create-tool/references/availability.md +10 -10
  8. package/catalog/create-tool/references/decorator-options.md +1 -1
  9. package/catalog/create-tool/references/ui-widgets.md +91 -42
  10. package/catalog/frontmcp-authorities/SKILL.md +5 -0
  11. package/catalog/frontmcp-channels/SKILL.md +17 -16
  12. package/catalog/frontmcp-channels/references/channel-sources.md +4 -4
  13. package/catalog/frontmcp-channels/references/channel-two-way.md +1 -1
  14. package/catalog/frontmcp-config/examples/configure-deployment-targets/distributed-ha-config.md +2 -0
  15. package/catalog/frontmcp-config/examples/configure-session/vercel-kv-session.md +1 -2
  16. package/catalog/frontmcp-config/examples/configure-throttle/distributed-redis-throttle.md +3 -3
  17. package/catalog/frontmcp-config/examples/configure-transport/custom-protocol-flags.md +0 -1
  18. package/catalog/frontmcp-config/examples/configure-transport/distributed-sessions-redis.md +0 -1
  19. package/catalog/frontmcp-config/examples/configure-transport/stateless-serverless.md +2 -3
  20. package/catalog/frontmcp-config/examples/configure-transport-protocol-presets/stateless-api-serverless.md +2 -3
  21. package/catalog/frontmcp-config/references/configure-auth.md +1 -1
  22. package/catalog/frontmcp-config/references/configure-deployment-targets.md +68 -16
  23. package/catalog/frontmcp-config/references/configure-http.md +11 -6
  24. package/catalog/frontmcp-config/references/configure-security-headers.md +18 -13
  25. package/catalog/frontmcp-config/references/configure-throttle-guard-config.md +4 -4
  26. package/catalog/frontmcp-config/references/configure-throttle.md +4 -2
  27. package/catalog/frontmcp-config/references/configure-transport.md +4 -5
  28. package/catalog/frontmcp-deployment/SKILL.md +19 -19
  29. package/catalog/frontmcp-deployment/examples/build-for-browser/react-provider-setup.md +5 -3
  30. package/catalog/frontmcp-deployment/examples/deploy-to-node/docker-compose-with-redis.md +1 -1
  31. package/catalog/frontmcp-deployment/examples/deploy-to-node/pm2-with-nginx.md +1 -1
  32. package/catalog/frontmcp-deployment/examples/deploy-to-vercel/vercel-with-kv.md +2 -0
  33. package/catalog/frontmcp-deployment/references/build-for-browser.md +46 -9
  34. package/catalog/frontmcp-deployment/references/build-for-mcpb.md +15 -8
  35. package/catalog/frontmcp-deployment/references/build-for-sdk.md +11 -10
  36. package/catalog/frontmcp-deployment/references/deploy-to-cloudflare.md +5 -1
  37. package/catalog/frontmcp-deployment/references/deploy-to-lambda.md +11 -10
  38. package/catalog/frontmcp-deployment/references/deploy-to-node.md +11 -11
  39. package/catalog/frontmcp-deployment/references/deploy-to-vercel.md +15 -7
  40. package/catalog/frontmcp-deployment/references/mcp-client-integration.md +12 -7
  41. package/catalog/frontmcp-deployment/references/protocol-versions.md +7 -3
  42. package/catalog/frontmcp-development/examples/create-agent/nested-agents-with-swarm.md +50 -10
  43. package/catalog/frontmcp-development/examples/openapi-adapter/ref-security-and-filtering.md +13 -7
  44. package/catalog/frontmcp-development/references/create-adapter.md +14 -0
  45. package/catalog/frontmcp-development/references/create-agent.md +82 -48
  46. package/catalog/frontmcp-development/references/create-plugin-hooks.md +15 -5
  47. package/catalog/frontmcp-development/references/create-plugin.md +23 -2
  48. package/catalog/frontmcp-development/references/create-skill-with-tools.md +7 -2
  49. package/catalog/frontmcp-development/references/create-skill.md +4 -0
  50. package/catalog/frontmcp-development/references/decorators-guide.md +10 -11
  51. package/catalog/frontmcp-development/references/official-adapters.md +1 -1
  52. package/catalog/frontmcp-development/references/official-plugins.md +138 -28
  53. package/catalog/frontmcp-development/references/openapi-adapter.md +52 -2
  54. package/catalog/frontmcp-guides/references/example-task-manager.md +2 -2
  55. package/catalog/frontmcp-guides/references/example-weather-api.md +2 -2
  56. package/catalog/frontmcp-observability/references/metrics-endpoint.md +3 -1
  57. package/catalog/frontmcp-production-readiness/examples/distributed-ha/ha-kubernetes-3-replicas.md +13 -1
  58. package/catalog/frontmcp-production-readiness/examples/production-node-sdk/package-json-config.md +2 -2
  59. package/catalog/frontmcp-production-readiness/references/common-checklist.md +3 -2
  60. package/catalog/frontmcp-production-readiness/references/distributed-ha.md +54 -24
  61. package/catalog/frontmcp-production-readiness/references/health-readiness-endpoints.md +3 -1
  62. package/catalog/frontmcp-setup/examples/nx-workflow/build-test-affected.md +2 -2
  63. package/catalog/frontmcp-setup/examples/nx-workflow/multi-server-deployment.md +9 -5
  64. package/catalog/frontmcp-setup/examples/nx-workflow/scaffold-and-generate.md +1 -1
  65. package/catalog/frontmcp-setup/examples/project-structure-nx/nx-generator-scaffolding.md +2 -2
  66. package/catalog/frontmcp-setup/references/frontmcp-skills-usage.md +28 -15
  67. package/catalog/frontmcp-setup/references/multi-app-composition.md +11 -8
  68. package/catalog/frontmcp-setup/references/nx-workflow.md +68 -28
  69. package/catalog/frontmcp-setup/references/project-structure-nx.md +7 -4
  70. package/catalog/frontmcp-setup/references/setup-project.md +15 -0
  71. package/catalog/frontmcp-setup/references/setup-redis.md +12 -3
  72. package/catalog/frontmcp-setup/references/setup-sqlite.md +21 -14
  73. package/catalog/frontmcp-testing/SKILL.md +28 -23
  74. package/catalog/frontmcp-testing/references/setup-testing.md +39 -2
  75. package/catalog/frontmcp-testing/references/test-auth.md +8 -0
  76. package/catalog/skills-manifest.json +14 -12
  77. package/package.json +1 -1
@@ -123,6 +123,10 @@ npx wrangler kv:namespace create FRONTMCP_KV
123
123
 
124
124
  Copy the returned `id` into your `wrangler.toml`.
125
125
 
126
+ Cloudflare storage: `redis: { provider: 'vercel-kv' }` (the HTTP-based Upstash/Vercel KV client) is accepted by `--target cloudflare`; only TCP `redis` and `sqlite` configs are rejected at build time, since Workers cannot open raw sockets or load native modules.
127
+
128
+ To use it for sessions, install `@vercel/kv` in the project (the build bundles it into the worker), set `KV_REST_API_URL` / `KV_REST_API_TOKEN` as `[vars]` or secrets, and set `compatibility_date` to `2024-11-11` or later: the Upstash client sends `cache: 'no-store'` on every request, which Workers reject before that date (`The 'cache' field on 'RequestInitializerDict' is not implemented`). The build accepts the provider when it can read it, either from the evaluated config or written literally as `redis: { provider: 'vercel-kv' }` in `@FrontMcp({...})`; a `redis` whose provider it can read neither way is refused, and the error says so.
129
+
126
130
  ## Step 4: Configure the Server
127
131
 
128
132
  ```typescript
@@ -149,7 +153,7 @@ For session storage, use Upstash Redis (HTTP) via `redis: { provider: 'vercel-kv
149
153
 
150
154
  ### Secrets, vars and `process.env`
151
155
 
152
- Worker bindings arrive as an argument to `fetch`, not as environment variables. The generated entry copies every **string** binding into `process.env` on the first request (existing values are never overwritten), so ordinary `process.env.MY_API_KEY` reads behave the same on Workers as under `frontmcp dev`. Non-string bindings (KV, D1, R2, Durable Objects) stay on `env`, which the entry forwards to the handler along with `ctx`.
156
+ Worker bindings arrive as an argument to `fetch`, not as environment variables. The generated entry copies every **string** binding into `process.env` on the first request (existing values are never overwritten), so ordinary `process.env.MY_API_KEY` reads behave the same on Workers as under `frontmcp dev`. Non-string bindings (KV, D1, R2, Durable Objects) stay on `env`, which the entry forwards to the handler along with `ctx`. Inside a tool, resource, prompt, agent or job, read them with `this.workerEnv` (e.g. `this.workerEnv?.MY_KV as KVNamespace | undefined`) — it is the request's Worker `env` (string bindings included), read-only, and `undefined` where the request carries no `env` (Node/Express, stdio, `create()`/`connect()` direct servers); a job run by `execute_job` reads the `env` of the request that ran it. The `process.env` copy is the generated entry's doing: the SDK never writes `process.env`, so a hand-written entry around `createWebFetchHandler` / `getServerlessHandlerAsync()` does not get it. `NODE_ENV = "production"` set in `wrangler.toml` `[vars]` is read live by the runtime context, so production mode takes effect even though the value reaches `process.env` only on the first request. Wrangler inlines the literal `process.env.NODE_ENV` at build time (`"development"` under `wrangler dev`, `"production"` under `wrangler deploy`, unless the shell sets `NODE_ENV`); FrontMCP reads the `[vars]` value past that, so `this.runtimeContext.env` / `this.isEnv('production')` follow `[vars]` under `wrangler dev` too. A `process.env.NODE_ENV` in your own tool code is still wrangler's constant — read `this.runtimeContext.env`, or run `NODE_ENV=production npx wrangler dev`.
153
157
 
154
158
  A value read at module-eval time — inside the `@FrontMcp({...})` argument itself — is still `undefined`, because the copy happens on the first request. Read configuration inside `execute()` / `read()`, or rely on `nodejs_compat_populate_process_env` (emitted by default), which populates `process.env` before your module evaluates.
155
159
 
@@ -36,13 +36,13 @@ This skill walks you through deploying a FrontMCP server to AWS Lambda with API
36
36
  - SAM CLI installed: `brew install aws-sam-cli` (macOS) or see AWS docs
37
37
  - Node.js 24 or later
38
38
  - A FrontMCP project ready to build
39
- - **Peer dependency:** `@codegenie/serverless-express` installed in your project. The Lambda adapter externalizes it at bundle time and validates its presence at build time:
39
+ - **Peer dependency:** `@codegenie/serverless-express` installed in your project. The Lambda adapter validates its presence at build time and bundles it into `handler.cjs`, so `dist/lambda/` deploys on its own (`CodeUri: dist/lambda/`, no `node_modules` or Layer needed):
40
40
 
41
41
  ```bash
42
42
  npm install @codegenie/serverless-express
43
43
  ```
44
44
 
45
- If it isn't installed, `frontmcp build --target lambda` fails with a clear error before producing artifacts.
45
+ If it isn't installed, `frontmcp build --target lambda` fails with a clear error before producing artifacts. `frontmcp create --target lambda` adds it to `dependencies` for you.
46
46
 
47
47
  ## Step 1: Build for Lambda
48
48
 
@@ -318,14 +318,15 @@ Lambda cold starts occur when a new execution environment is initialized. Strate
318
318
 
319
319
  ## Troubleshooting
320
320
 
321
- | Problem | Cause | Solution |
322
- | ------------------------------------ | --------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
323
- | Timeout errors | Function timeout too low or waiting on unreachable resource | Increase `Timeout` in the SAM template; verify network connectivity to dependencies |
324
- | 502 Bad Gateway | Handler path mismatch, missing env vars, or unhandled exception | Check CloudWatch Logs; confirm `Handler: handler.handler` and `CodeUri: dist/lambda/` |
325
- | `Cannot find module @codegenie/...` | Peer dep not installed locally or in Layer | `npm install @codegenie/serverless-express` (the build's `validate` hook checks for it) |
326
- | Cold starts too slow | Low memory, x86 architecture, or large bundle | Increase memory to 512+ MB, use `arm64`, or enable provisioned concurrency |
327
- | Redis connection refused from Lambda | Lambda not in the same VPC as ElastiCache | Place the Lambda in the ElastiCache VPC with appropriate security group rules |
328
- | `sam deploy` fails with IAM error | Insufficient permissions for CloudFormation stack creation | Ensure the deploying IAM user/role has `cloudformation:*`, `lambda:*`, `apigateway:*`, and `iam:PassRole` |
321
+ | Problem | Cause | Solution |
322
+ | ------------------------------------ | ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- |
323
+ | Timeout errors | Function timeout too low or waiting on unreachable resource | Increase `Timeout` in the SAM template; verify network connectivity to dependencies |
324
+ | 502 Bad Gateway | Handler path mismatch, missing env vars, or unhandled exception | Check CloudWatch Logs; confirm `Handler: handler.handler` and `CodeUri: dist/lambda/` |
325
+ | `Cannot find module @codegenie/...` | Peer dep not installed locally or in Layer | `npm install @codegenie/serverless-express` (the build's `validate` hook checks for it) |
326
+ | Cold starts too slow | Low memory, x86 architecture, or large bundle | Increase memory to 512+ MB, use `arm64`, or enable provisioned concurrency |
327
+ | Redis connection refused from Lambda | Lambda not in the same VPC as ElastiCache | Place the Lambda in the ElastiCache VPC with appropriate security group rules |
328
+ | 500 `server_misconfigured` | A required secret is missing (`SESSION_SECRET_REQUIRED`, `JWT_SECRET_REQUIRED`, ...) | The response body names the `code` and the remedy; set the variable in the function's environment and redeploy |
329
+ | `sam deploy` fails with IAM error | Insufficient permissions for CloudFormation stack creation | Ensure the deploying IAM user/role has `cloudformation:*`, `lambda:*`, `apigateway:*`, and `iam:PassRole` |
329
330
 
330
331
  ## Examples
331
332
 
@@ -41,7 +41,7 @@ This skill walks you through deploying a FrontMCP server as a standalone Node.js
41
41
  frontmcp build --target node
42
42
  ```
43
43
 
44
- This compiles your TypeScript source, bundles dependencies, and produces a production-ready output in `dist/`. The build output includes compiled JavaScript optimized for Node.js, a `package.json` with production dependencies only, and any static assets.
44
+ This compiles your TypeScript source, bundles dependencies, and writes the output to `dist/node/`: a CommonJS single-file bundle at `dist/node/<name>.bundle.js` (`<name>` is the `name` in `frontmcp.config`, or the unscoped `package.json` name without a config file), a `dist/node/<name>` runner script, and any static assets. The examples below use `my-server` as `<name>`.
45
45
 
46
46
  ## Step 2: Dockerfile (Multi-Stage)
47
47
 
@@ -66,7 +66,7 @@ RUN yarn install --frozen-lockfile --production && yarn cache clean
66
66
  EXPOSE 3000
67
67
  HEALTHCHECK --interval=30s --timeout=5s --retries=3 --start-period=10s \
68
68
  CMD wget -qO- http://localhost:3000/healthz || exit 1
69
- CMD ["node", "dist/main.js"]
69
+ CMD ["node", "dist/node/my-server.bundle.js"]
70
70
  ```
71
71
 
72
72
  The first stage installs all dependencies and builds the project. The second stage copies only the compiled output and production dependencies into a slim image.
@@ -173,7 +173,7 @@ When running without Docker, use PM2 as a process manager:
173
173
  npm install -g pm2
174
174
 
175
175
  # Start the server with cluster mode (one instance per CPU core)
176
- pm2 start dist/main.js --name frontmcp-server -i max
176
+ pm2 start dist/node/my-server.bundle.js --name frontmcp-server -i max
177
177
 
178
178
  # Save the process list for auto-restart on reboot
179
179
  pm2 save
@@ -223,20 +223,20 @@ services:
223
223
 
224
224
  ## Common Patterns
225
225
 
226
- | Pattern | Correct | Incorrect | Why |
227
- | ------------------------- | ------------------------------------ | ------------------------------------------------ | ------------------------------------------------------------------- |
228
- | Build command | `frontmcp build --target node` | `tsc && node dist/main.js` | The FrontMCP build bundles deps and produces an optimized output |
229
- | Docker base image | `node:24-alpine` (multi-stage) | `node:24` (single stage with dev deps) | Multi-stage keeps the production image small and secure |
230
- | Process manager | PM2 with `-i max` cluster mode | Running `node dist/main.js` directly via `nohup` | PM2 handles restarts, logging, and multi-core clustering |
231
- | Redis hostname in Compose | Service name `redis` | `localhost` or `127.0.0.1` | Containers communicate via Docker's internal DNS, not localhost |
232
- | Environment config | `.env` file or orchestrator env vars | Hardcoded values in source code | Keeps secrets out of the codebase and allows per-environment config |
226
+ | Pattern | Correct | Incorrect | Why |
227
+ | ------------------------- | ------------------------------------ | --------------------------------------- | ------------------------------------------------------------------- |
228
+ | Build command | `frontmcp build --target node` | `tsc && node dist/main.js` | The FrontMCP build bundles deps and produces an optimized output |
229
+ | Docker base image | `node:24-alpine` (multi-stage) | `node:24` (single stage with dev deps) | Multi-stage keeps the production image small and secure |
230
+ | Process manager | PM2 with `-i max` cluster mode | Running the bundle directly via `nohup` | PM2 handles restarts, logging, and multi-core clustering |
231
+ | Redis hostname in Compose | Service name `redis` | `localhost` or `127.0.0.1` | Containers communicate via Docker's internal DNS, not localhost |
232
+ | Environment config | `.env` file or orchestrator env vars | Hardcoded values in source code | Keeps secrets out of the codebase and allows per-environment config |
233
233
 
234
234
  ## Verification Checklist
235
235
 
236
236
  **Build**
237
237
 
238
238
  - [ ] `frontmcp build --target node` completes without errors
239
- - [ ] `dist/main.js` exists and is runnable with `node dist/main.js`
239
+ - [ ] `dist/node/<name>.bundle.js` exists and is runnable with `node dist/node/<name>.bundle.js`
240
240
 
241
241
  **Docker**
242
242
 
@@ -90,6 +90,8 @@ export default MyServer;
90
90
 
91
91
  Provision the KV store in the Vercel dashboard under **Storage > Create Database > KV (Redis)**, then link it to your project. Vercel automatically injects the required environment variables.
92
92
 
93
+ > **Tasks and elicitation on Vercel KV:** Vercel KV has no pub/sub, so background tasks and elicitation cannot use it. The server still starts — tasks are skipped with a `[tasks]` startup warning unless `tasks: { enabled: true }` is set (which keeps `TaskStoreNotSupportedError`), and elicitation throws `ElicitationNotSupportedError` only when its store actually resolves to Vercel KV. Give tasks their own backend with `tasks: { redis }` or `tasks: { sqlite }` (an explicit backend ignores the ambient `KV_REST_API_URL`).
94
+
93
95
  ## Step 3: Environment Variables
94
96
 
95
97
  Vercel KV variables are injected automatically when the store is linked. For manual setup or additional configuration, set them in the Vercel dashboard (**Settings > Environment Variables**) or via the CLI:
@@ -178,6 +180,11 @@ Serverless functions are stateless between invocations. All persistent state mus
178
180
  | Routing | `.vercel/output/config.json` (auto) | Hand-written `rewrites` to `/api/frontmcp` | The adapter writes `routes: [{ src: '/(.*)', dest: '/index' }]` for you |
179
181
  | Environment variables | Link KV store in dashboard (auto-injected) | Hardcode `KV_REST_API_URL` in source | Linked stores inject vars automatically and rotate tokens safely |
180
182
 
183
+ ### Bundle-time behavior of serverless builds
184
+
185
+ - `process.env.NODE_ENV` stays a runtime lookup in the Vercel and Lambda bundles. It is not inlined as `"production"` at build time, so a deployment's own `NODE_ENV` (and the production-only checks that read it) behave the same as on a Node server.
186
+ - Optional packages the server may or may not use (`@frontmcp/storage-sqlite`, `@frontmcp/observability`, `@vercel/kv`, `@opentelemetry/sdk-trace-base`) are bundled when installed and left as a lazy `require()` when they are not, so a missing optional package no longer fails the build with "Module not found". `better-sqlite3` (a native addon) is always left external.
187
+
181
188
  ## Verification Checklist
182
189
 
183
190
  **Build**
@@ -205,13 +212,14 @@ Serverless functions are stateless between invocations. All persistent state mus
205
212
 
206
213
  ## Troubleshooting
207
214
 
208
- | Problem | Cause | Solution |
209
- | -------------------- | --------------------------------------- | ---------------------------------------------------------------------------------------------- |
210
- | Function timeout | Operation exceeds plan's max duration | Check plan limits (Hobby: 10s, Pro: 60s); upgrade plan or refactor the slow tool |
211
- | KV connection errors | KV store not linked or env vars missing | Re-link the KV store in the Vercel dashboard; verify `KV_REST_API_URL` and `KV_REST_API_TOKEN` |
212
- | 404 on every route | Build did not produce `.vercel/output/` | Ensure `frontmcp build --target vercel` ran before `vercel` deploys |
213
- | Bundle too large | Unnecessary dependencies included | Review dependencies and remove unused packages to reduce bundle size |
214
- | Cold starts too slow | Heavy decorator initialization | Lazy-load providers; defer heavy work until first tool call |
215
+ | Problem | Cause | Solution |
216
+ | -------------------------- | ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------- |
217
+ | Function timeout | Operation exceeds plan's max duration | Check plan limits (Hobby: 10s, Pro: 60s); upgrade plan or refactor the slow tool |
218
+ | KV connection errors | KV store not linked or env vars missing | Re-link the KV store in the Vercel dashboard; verify `KV_REST_API_URL` and `KV_REST_API_TOKEN` |
219
+ | 404 on every route | Build did not produce `.vercel/output/` | Ensure `frontmcp build --target vercel` ran before `vercel` deploys |
220
+ | Bundle too large | Unnecessary dependencies included | Review dependencies and remove unused packages to reduce bundle size |
221
+ | Cold starts too slow | Heavy decorator initialization | Lazy-load providers; defer heavy work until first tool call |
222
+ | 500 `server_misconfigured` | A required secret is missing (`SESSION_SECRET_REQUIRED`, `JWT_SECRET_REQUIRED`, ...) | The response body names the `code` and the remedy; set the variable in Vercel Project Settings and redeploy |
215
223
 
216
224
  ## Examples
217
225
 
@@ -102,12 +102,17 @@ Wire the client at the bridge:
102
102
 
103
103
  Bridge guarantees:
104
104
 
105
- - Stdout is 100% JSON-RPC frames; diagnostics go to `./.frontmcp/dev.log` (override with `--log-file`).
106
- - Session id survives reload (pinned via `FRONTMCP_DEV_FORCE_SESSION_ID`).
107
- - Buffered RPCs during reload drain in FIFO once the child reports ready.
105
+ - Stdout is 100% JSON-RPC frames; diagnostics go to `.frontmcp/dev.log` in the project root (override with `--log-file`), start-up notices (port picked, `.env` loaded) to stderr.
106
+ - Same project setup as `frontmcp dev`: `frontmcp.config.*` (from any subfolder), `entry`, `transport.http.port` / `path`, `env` overlays and `.env`. The server gets `PORT` + `FRONTMCP_HTTP_ENTRY_PATH`; with no port chosen anywhere the bridge picks a free loopback port.
107
+ - The server reports the port and MCP path it really serves (`__FRONTMCP_BOOTSTRAP_COMPLETE__ {"port":…,"path":…}` on stderr), so values hard-coded in `@FrontMcp({ http })` work.
108
+ - The client stays connected across reloads: the bridge uses the `mcp-session-id` the server issues, replays the client's `initialize` handshake on the restarted server, then sends `notifications/tools/list_changed` (and resources/prompts when advertised). Server-side session state starts fresh on each reload.
109
+ - Buffered RPCs during reload drain in FIFO once the new server is ready and initialized. A server that rejects the replayed handshake is a failed reload: it is stopped, and buffered RPCs wait for the next save or answer `dev_reload_deadline`.
108
110
  - Reload deadline + buffer overflow surface structured errors (`dev_server_unreachable` / `dev_buffer_full` / `dev_reload_deadline` — codes -32099 / -32098 / -32097) so the client spinner clears instead of hanging.
111
+ - Closing stdin or `SIGINT` / `SIGTERM` stops the server and everything it started.
109
112
 
110
- Flags: `--stdio`, `--serve`, `--log-file <path>`, `--buffer-size <n>` (default 8), `--reload-deadline-ms <ms>` (default 30000), `-p <port>` (HTTP-mode loopback, default 3000).
113
+ `--serve` runs the entry with the project's `tsx` (`node --import tsx`) so the server owns the IPC channel — keep `tsx` in devDependencies (scaffolded projects have it).
114
+
115
+ Flags: `--stdio`, `--serve`, `--log-file <path>`, `--buffer-size <n>` (default 8), `--reload-deadline-ms <ms>` (default 30000), `-p <port>` (HTTP-mode loopback; default `transport.http.port`, then `PORT`, then a free port), `--auto-port`.
111
116
 
112
117
  ## Stdio Transport
113
118
 
@@ -184,9 +189,9 @@ itself before any framework initialization:
184
189
  }
185
190
  ```
186
191
 
187
- > Do not run the raw `--target node` bundle as `node dist/node/my-server.bundle.js --stdio`
188
- > — that bundle is your `@FrontMcp` server module and starts the HTTP server on
189
- > import. Use the runner above, or set `FRONTMCP_STDIO=1` before the bundle loads.
192
+ > The bundle itself honors the flag too: `node dist/node/my-server.bundle.js --stdio`
193
+ > serves stdio and binds no port (and, run directly, it serves MCP at
194
+ > `transport.http.path` like the runner does).
190
195
 
191
196
  ## HTTP Transport
192
197
 
@@ -188,11 +188,15 @@ resolves with the final result either way.
188
188
  For a remote app, negotiate per remote:
189
189
 
190
190
  ```ts
191
- transportOptions: {
192
- protocolVersion: 'auto';
193
- } // 'legacy' (default) | '2026-07-28' | 'auto'
191
+ App.remote('https://example.com/mcp', {
192
+ transportOptions: {
193
+ protocolVersion: 'auto', // 'legacy' (default) | '2026-07-28' | 'auto'
194
+ },
195
+ });
194
196
  ```
195
197
 
198
+ `'auto'` probes `server/discover` and falls back to the session transports; `'2026-07-28'` always uses the stateless client and requires a URL remote (other transports are refused at connect time).
199
+
196
200
  ## Deprecated in this revision
197
201
 
198
202
  Still functional; do not adopt in new servers:
@@ -2,7 +2,7 @@
2
2
  name: nested-agents-with-swarm
3
3
  reference: create-agent
4
4
  level: advanced
5
- description: Composing specialized agents into a swarm where an orchestrator can discover and call peers at runtime as `use-agent:<id>` tools. Routing is driven by the orchestrator's LLM, not a declarative handoff table.
5
+ description: Composing specialized agents into a swarm where an orchestrator can discover and call peers at runtime as `invoke_<id>` tools, plus a nested sub-agent and `this.invokeAgent()`. Routing is driven by the orchestrator's LLM, not a declarative handoff table.
6
6
  tags:
7
7
  - development
8
8
  - agent
@@ -10,15 +10,17 @@ tags:
10
10
  - agents
11
11
  - swarm
12
12
  features:
13
- - 'Setting `swarm: { canSeeOtherAgents: true, visibleAgents: [...] }` on the orchestrator so peers appear as `use-agent:*` tools'
13
+ - 'Setting `swarm: { canSeeOtherAgents: true, visibleAgents: [...] }` on the orchestrator so peers are offered to its model as `invoke_*` tools'
14
14
  - 'Setting `swarm: { isVisible: true }` (the default) on specialist peers so they can be called'
15
- - Routing is driven by the orchestrator LLM choosing among `use-agent:<peer>` tools, not by a declarative handoff table
15
+ - Routing is driven by the orchestrator LLM choosing among `invoke_<peer>` tools, not by a declarative handoff table
16
+ - 'A nested sub-agent (`agents: [...]`) private to the orchestrator, called from code with `this.invokeAgent()`'
17
+ - '`swarm.maxCallDepth` bounding how deep agents may call each other'
16
18
  - Each agent has its own `llm` config, `tools`, and `systemInstructions` for specialization
17
19
  ---
18
20
 
19
21
  # Multi-Agent Swarm Visibility
20
22
 
21
- Composing specialized agents into a swarm where an orchestrator can discover and call peers at runtime as `use-agent:<id>` tools. Routing is driven by the orchestrator's LLM, not a declarative handoff table.
23
+ Composing specialized agents into a swarm where an orchestrator can discover and call peers at runtime as `invoke_<id>` tools, plus a nested sub-agent and `this.invokeAgent()`. Routing is driven by the orchestrator's LLM, not a declarative handoff table.
22
24
 
23
25
  ## Code
24
26
 
@@ -42,6 +44,8 @@ class LookupInvoiceTool extends ToolContext {
42
44
  name: 'billing_agent',
43
45
  description: 'Handles billing and payment inquiries',
44
46
  llm: { provider: 'anthropic', model: 'claude-sonnet-4-20250514', apiKey: { env: 'ANTHROPIC_API_KEY' } },
47
+ // The arguments of its invoke_billing_agent tool: an agent without an inputSchema receives {}.
48
+ inputSchema: { request: z.string().describe('The billing request') },
45
49
  tools: [LookupInvoiceTool],
46
50
  // isVisible defaults to true; specialists do not need swarm config to be callable.
47
51
  swarm: { isVisible: true },
@@ -51,23 +55,45 @@ class BillingAgent extends AgentContext {}
51
55
 
52
56
  ```typescript
53
57
  // src/apps/support/agents/technical.agent.ts
54
- import { Agent, AgentContext } from '@frontmcp/sdk';
58
+ import { Agent, AgentContext, z } from '@frontmcp/sdk';
55
59
 
56
60
  @Agent({
57
61
  id: 'technical_agent',
58
62
  name: 'technical_agent',
59
63
  description: 'Handles technical support issues',
60
64
  llm: { provider: 'anthropic', model: 'claude-sonnet-4-20250514', apiKey: { env: 'ANTHROPIC_API_KEY' } },
65
+ inputSchema: { request: z.string().describe('The technical issue') },
61
66
  systemInstructions: 'You are a technical support specialist. Diagnose issues and provide solutions.',
62
67
  swarm: { isVisible: true },
63
68
  })
64
69
  class TechnicalAgent extends AgentContext {}
65
70
  ```
66
71
 
72
+ ```typescript
73
+ // src/apps/support/agents/sentiment.agent.ts
74
+ import { Agent, AgentContext, z } from '@frontmcp/sdk';
75
+
76
+ // Nested in the triage agent below: private to it, never offered to clients.
77
+ @Agent({
78
+ id: 'sentiment_agent',
79
+ name: 'sentiment_agent',
80
+ description: 'Rates how upset a customer is',
81
+ llm: { provider: 'anthropic', model: 'claude-sonnet-4-20250514', apiKey: { env: 'ANTHROPIC_API_KEY' } },
82
+ inputSchema: { request: z.string() },
83
+ outputSchema: { urgency: z.enum(['low', 'high']) },
84
+ // The model's reply is parsed as the output: ask for JSON that matches outputSchema.
85
+ systemInstructions:
86
+ 'Rate how urgent the request is. Reply only with JSON: {"urgency": "low"} or {"urgency": "high"}.',
87
+ })
88
+ export class SentimentAgent extends AgentContext {}
89
+ ```
90
+
67
91
  ```typescript
68
92
  // src/apps/support/agents/triage.agent.ts
69
93
  import { Agent, AgentContext, z } from '@frontmcp/sdk';
70
94
 
95
+ import { SentimentAgent } from './sentiment.agent';
96
+
71
97
  @Agent({
72
98
  id: 'triage_agent',
73
99
  name: 'triage_agent',
@@ -76,16 +102,28 @@ import { Agent, AgentContext, z } from '@frontmcp/sdk';
76
102
  inputSchema: {
77
103
  request: z.string().describe('The incoming user request'),
78
104
  },
105
+ // Nested sub-agent: offered to this agent's model as invoke_sentiment_agent, and callable from code.
106
+ agents: [SentimentAgent],
79
107
  // Orchestrator: opts in to seeing peers and (optionally) restricts to a whitelist.
80
108
  swarm: {
81
109
  canSeeOtherAgents: true,
82
110
  visibleAgents: ['billing_agent', 'technical_agent'],
83
- maxCallDepth: 3,
111
+ maxCallDepth: 3, // a deeper agent-to-agent chain fails with AGENT_CALL_DEPTH_EXCEEDED
84
112
  },
85
113
  systemInstructions:
86
- 'Analyze the request and delegate by calling either use-agent:billing_agent (for billing/payments) or use-agent:technical_agent (for technical issues).',
114
+ 'Analyze the request and delegate by calling either invoke_billing_agent (for billing/payments) or invoke_technical_agent (for technical issues).',
87
115
  })
88
- class TriageAgent extends AgentContext {}
116
+ class TriageAgent extends AgentContext {
117
+ async execute(input: { request: string }) {
118
+ // Code can call a nested agent, or a peer it sees, and gets its output back.
119
+ const sentiment = (await this.invokeAgent('sentiment_agent', { request: input.request })) as { urgency: string };
120
+ if (sentiment.urgency === 'high') {
121
+ return this.invokeAgent('technical_agent', { request: input.request });
122
+ }
123
+ // Otherwise let the model route among invoke_billing_agent / invoke_technical_agent.
124
+ return super.execute(input);
125
+ }
126
+ }
89
127
  ```
90
128
 
91
129
  ```typescript
@@ -101,9 +139,11 @@ class SupportApp {}
101
139
 
102
140
  ## What This Demonstrates
103
141
 
104
- - Setting `swarm: { canSeeOtherAgents: true, visibleAgents: [...] }` on the orchestrator so peers appear as `use-agent:*` tools
142
+ - Setting `swarm: { canSeeOtherAgents: true, visibleAgents: [...] }` on the orchestrator so peers are offered to its model as `invoke_*` tools
105
143
  - Setting `swarm: { isVisible: true }` (the default) on specialist peers so they can be called
106
- - Routing is driven by the orchestrator LLM choosing among `use-agent:<peer>` tools, not by a declarative handoff table
144
+ - Routing is driven by the orchestrator LLM choosing among `invoke_<peer>` tools, not by a declarative handoff table
145
+ - A nested sub-agent (`agents: [...]`) private to the orchestrator, called from code with `this.invokeAgent()`
146
+ - `swarm.maxCallDepth` bounding how deep agents may call each other
107
147
  - Each agent has its own `llm` config, `tools`, and `systemInstructions` for specialization
108
148
 
109
149
  ## Related
@@ -8,7 +8,7 @@ features:
8
8
  - 'Secure defaults: external `$ref` resolution off, spec-URL redirects not followed, internal/private targets blocked (DNS-resolved) — on `mcp-from-openapi` >= 2.5.0'
9
9
  - 'Opting back into external refs with `allowedProtocols`, and restricting the spec URL + `$ref`s with `allowedHosts`'
10
10
  - 'Using `allowInternalIPs` for trusted internal/local targets (governs the spec URL and `$ref`s)'
11
- - 'Filtering operations with `includeOperations`, `excludeOperations`, and `filterFn`'
11
+ - 'Filtering operations with `readOnlyOnly`, `includeTags`, `includePaths`, `excludeMethods`, `includeOperations`, `excludeOperations`, and `filterFn`'
12
12
  - 'Combining security hardening with operation filtering for a production-ready setup'
13
13
  ---
14
14
 
@@ -20,8 +20,8 @@ Demonstrates configuring $ref / spec-URL resolution security to prevent SSRF att
20
20
 
21
21
  ```typescript
22
22
  // src/server.ts
23
- import { FrontMcp, App } from '@frontmcp/sdk';
24
23
  import { OpenapiAdapter } from '@frontmcp/adapters';
24
+ import { App, FrontMcp } from '@frontmcp/sdk';
25
25
 
26
26
  @App({
27
27
  name: 'secure-app',
@@ -45,8 +45,10 @@ import { OpenapiAdapter } from '@frontmcp/adapters';
45
45
  },
46
46
  },
47
47
  generateOptions: {
48
- // Only expose read operations to MCP clients
49
- filterFn: (op) => op.method === 'get',
48
+ // Only expose read-only operations (readOnlyHint: true — GET/HEAD/OPTIONS/TRACE by default)
49
+ readOnlyOnly: true,
50
+ // ...and only the partner's public tag
51
+ includeTags: ['public'],
50
52
  // Skip deprecated endpoints
51
53
  includeDeprecated: false,
52
54
  },
@@ -88,8 +90,12 @@ import { OpenapiAdapter } from '@frontmcp/adapters';
88
90
  generateOptions: {
89
91
  // Exclude admin and dangerous operations
90
92
  excludeOperations: ['deleteAll', 'resetDatabase', 'adminPanel'],
91
- // Only include billing-related paths
92
- filterFn: (op) => op.path.startsWith('/billing') || op.path.startsWith('/invoices'),
93
+ // Never expose deletes (lower-case HTTP method names)
94
+ excludeMethods: ['delete'],
95
+ // Only include billing-related paths (globs: `*` within a segment, `**` across segments)
96
+ includePaths: ['/billing/**', '/invoices/**'],
97
+ // Anything a declarative filter can't express goes in filterFn (runs after the others)
98
+ filterFn: (op) => !op.summary?.toLowerCase().includes('experimental'),
93
99
  },
94
100
  }),
95
101
  ],
@@ -109,7 +115,7 @@ class MyServer {}
109
115
  - Secure defaults: external `$ref` resolution off, spec-URL redirects not followed, internal/private targets blocked (DNS-resolved) — on `mcp-from-openapi` >= 2.5.0
110
116
  - Opting back into external refs with `allowedProtocols`, and restricting the spec URL + `$ref`s with `allowedHosts`
111
117
  - Using `allowInternalIPs` for trusted internal/local targets (governs the spec URL and `$ref`s)
112
- - Filtering operations with `includeOperations`, `excludeOperations`, and `filterFn`
118
+ - Filtering operations with `readOnlyOnly`, `includeTags`, `includePaths`, `excludeMethods`, `includeOperations`, `excludeOperations`, and `filterFn`
113
119
  - Combining security hardening with operation filtering for a production-ready setup
114
120
 
115
121
  ## Related
@@ -92,6 +92,8 @@ class MyApiAdapter extends DynamicAdapter<MyAdapterOptions> {
92
92
  class MyApp {}
93
93
  ```
94
94
 
95
+ To serve the adapter's tools, resources and prompts from every app, register it on the server instead with `@FrontMcp({ adapters: [MyApiAdapter.init({ ... })] })`. Like a server-level plugin, each scope (a standalone or `splitByApp` app gets its own) builds its own adapter from the `init()` options and runs `fetch()`; a hand-written `{ provide, useValue }` record is one adapter that every scope shares. Disposing the server calls each adapter's `stopPolling()` and drops its `onUpdate()` subscription. Up to 1.8.7 `@FrontMcp({ adapters })` was silently dropped, and disposing did not stop adapter polling.
96
+
95
97
  ## FrontMcpAdapterResponse
96
98
 
97
99
  The `fetch()` method returns tools, resources, and prompts to register:
@@ -120,6 +122,18 @@ const adapter = MyApiAdapter.init({
120
122
  @App({ adapters: [adapter] })
121
123
  ```
122
124
 
125
+ When the options depend on a provider, use `init({ name, inject, useFactory })`. The factory returns the adapter's **options** (or a promise of them) and the adapter is built from them, named by the `name` given to `init()`:
126
+
127
+ ```typescript
128
+ MyApiAdapter.init({
129
+ name: 'my-api',
130
+ inject: () => [ApiConfig] as const,
131
+ useFactory: (config: ApiConfig) => ({ name: 'my-api', endpoint: config.endpoint, apiKey: config.apiKey }),
132
+ });
133
+ ```
134
+
135
+ A factory may instead return an adapter instance, used as is when its `options.name` is the `name` given to `init()`; an instance named otherwise, or anything else, fails startup with an `InvalidEntityError`.
136
+
123
137
  ## Nx Generator
124
138
 
125
139
  ```bash