@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.
- package/README.md +107 -155
- package/catalog/create-tool/SKILL.md +24 -24
- package/catalog/create-tool/examples/21-tool-with-availability-constraints.md +1 -1
- package/catalog/create-tool/examples/22-tool-with-ui-html-template.md +0 -1
- package/catalog/create-tool/examples/23-tool-with-ui-filesource-tsx.md +1 -3
- package/catalog/create-tool/examples/24-tool-with-ui-csp-and-bridge.md +4 -6
- package/catalog/create-tool/references/availability.md +10 -10
- package/catalog/create-tool/references/decorator-options.md +1 -1
- package/catalog/create-tool/references/ui-widgets.md +91 -42
- package/catalog/frontmcp-authorities/SKILL.md +5 -0
- package/catalog/frontmcp-channels/SKILL.md +17 -16
- package/catalog/frontmcp-channels/references/channel-sources.md +4 -4
- package/catalog/frontmcp-channels/references/channel-two-way.md +1 -1
- package/catalog/frontmcp-config/examples/configure-deployment-targets/distributed-ha-config.md +2 -0
- package/catalog/frontmcp-config/examples/configure-session/vercel-kv-session.md +1 -2
- package/catalog/frontmcp-config/examples/configure-throttle/distributed-redis-throttle.md +3 -3
- package/catalog/frontmcp-config/examples/configure-transport/custom-protocol-flags.md +0 -1
- package/catalog/frontmcp-config/examples/configure-transport/distributed-sessions-redis.md +0 -1
- package/catalog/frontmcp-config/examples/configure-transport/stateless-serverless.md +2 -3
- package/catalog/frontmcp-config/examples/configure-transport-protocol-presets/stateless-api-serverless.md +2 -3
- package/catalog/frontmcp-config/references/configure-auth.md +1 -1
- package/catalog/frontmcp-config/references/configure-deployment-targets.md +68 -16
- package/catalog/frontmcp-config/references/configure-http.md +11 -6
- package/catalog/frontmcp-config/references/configure-security-headers.md +18 -13
- package/catalog/frontmcp-config/references/configure-throttle-guard-config.md +4 -4
- package/catalog/frontmcp-config/references/configure-throttle.md +4 -2
- package/catalog/frontmcp-config/references/configure-transport.md +4 -5
- package/catalog/frontmcp-deployment/SKILL.md +19 -19
- package/catalog/frontmcp-deployment/examples/build-for-browser/react-provider-setup.md +5 -3
- package/catalog/frontmcp-deployment/examples/deploy-to-node/docker-compose-with-redis.md +1 -1
- package/catalog/frontmcp-deployment/examples/deploy-to-node/pm2-with-nginx.md +1 -1
- package/catalog/frontmcp-deployment/examples/deploy-to-vercel/vercel-with-kv.md +2 -0
- package/catalog/frontmcp-deployment/references/build-for-browser.md +46 -9
- package/catalog/frontmcp-deployment/references/build-for-mcpb.md +15 -8
- package/catalog/frontmcp-deployment/references/build-for-sdk.md +11 -10
- package/catalog/frontmcp-deployment/references/deploy-to-cloudflare.md +5 -1
- package/catalog/frontmcp-deployment/references/deploy-to-lambda.md +11 -10
- package/catalog/frontmcp-deployment/references/deploy-to-node.md +11 -11
- package/catalog/frontmcp-deployment/references/deploy-to-vercel.md +15 -7
- package/catalog/frontmcp-deployment/references/mcp-client-integration.md +12 -7
- package/catalog/frontmcp-deployment/references/protocol-versions.md +7 -3
- package/catalog/frontmcp-development/examples/create-agent/nested-agents-with-swarm.md +50 -10
- package/catalog/frontmcp-development/examples/openapi-adapter/ref-security-and-filtering.md +13 -7
- package/catalog/frontmcp-development/references/create-adapter.md +14 -0
- package/catalog/frontmcp-development/references/create-agent.md +82 -48
- package/catalog/frontmcp-development/references/create-plugin-hooks.md +15 -5
- package/catalog/frontmcp-development/references/create-plugin.md +23 -2
- package/catalog/frontmcp-development/references/create-skill-with-tools.md +7 -2
- package/catalog/frontmcp-development/references/create-skill.md +4 -0
- package/catalog/frontmcp-development/references/decorators-guide.md +10 -11
- package/catalog/frontmcp-development/references/official-adapters.md +1 -1
- package/catalog/frontmcp-development/references/official-plugins.md +138 -28
- package/catalog/frontmcp-development/references/openapi-adapter.md +52 -2
- package/catalog/frontmcp-guides/references/example-task-manager.md +2 -2
- package/catalog/frontmcp-guides/references/example-weather-api.md +2 -2
- package/catalog/frontmcp-observability/references/metrics-endpoint.md +3 -1
- package/catalog/frontmcp-production-readiness/examples/distributed-ha/ha-kubernetes-3-replicas.md +13 -1
- package/catalog/frontmcp-production-readiness/examples/production-node-sdk/package-json-config.md +2 -2
- package/catalog/frontmcp-production-readiness/references/common-checklist.md +3 -2
- package/catalog/frontmcp-production-readiness/references/distributed-ha.md +54 -24
- package/catalog/frontmcp-production-readiness/references/health-readiness-endpoints.md +3 -1
- package/catalog/frontmcp-setup/examples/nx-workflow/build-test-affected.md +2 -2
- package/catalog/frontmcp-setup/examples/nx-workflow/multi-server-deployment.md +9 -5
- package/catalog/frontmcp-setup/examples/nx-workflow/scaffold-and-generate.md +1 -1
- package/catalog/frontmcp-setup/examples/project-structure-nx/nx-generator-scaffolding.md +2 -2
- package/catalog/frontmcp-setup/references/frontmcp-skills-usage.md +28 -15
- package/catalog/frontmcp-setup/references/multi-app-composition.md +11 -8
- package/catalog/frontmcp-setup/references/nx-workflow.md +68 -28
- package/catalog/frontmcp-setup/references/project-structure-nx.md +7 -4
- package/catalog/frontmcp-setup/references/setup-project.md +15 -0
- package/catalog/frontmcp-setup/references/setup-redis.md +12 -3
- package/catalog/frontmcp-setup/references/setup-sqlite.md +21 -14
- package/catalog/frontmcp-testing/SKILL.md +28 -23
- package/catalog/frontmcp-testing/references/setup-testing.md +39 -2
- package/catalog/frontmcp-testing/references/test-auth.md +8 -0
- package/catalog/skills-manifest.json +14 -12
- 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
|
|
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
|
|
322
|
-
| ------------------------------------ |
|
|
323
|
-
| Timeout errors | Function timeout too low or waiting on unreachable resource
|
|
324
|
-
| 502 Bad Gateway | Handler path mismatch, missing env vars, or unhandled exception
|
|
325
|
-
| `Cannot find module @codegenie/...` | Peer dep not installed locally or in Layer
|
|
326
|
-
| Cold starts too slow | Low memory, x86 architecture, or large bundle
|
|
327
|
-
| Redis connection refused from Lambda | Lambda not in the same VPC as ElastiCache
|
|
328
|
-
| `
|
|
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
|
|
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/
|
|
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/
|
|
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
|
|
227
|
-
| ------------------------- | ------------------------------------ |
|
|
228
|
-
| Build command | `frontmcp build --target node` | `tsc && node dist/main.js`
|
|
229
|
-
| Docker base image | `node:24-alpine` (multi-stage) | `node:24` (single stage with dev deps)
|
|
230
|
-
| Process manager | PM2 with `-i max` cluster mode | Running
|
|
231
|
-
| Redis hostname in Compose | Service name `redis` | `localhost` or `127.0.0.1`
|
|
232
|
-
| Environment config | `.env` file or orchestrator env vars | Hardcoded values in source code
|
|
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/
|
|
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
|
|
209
|
-
|
|
|
210
|
-
| Function timeout
|
|
211
|
-
| KV connection errors
|
|
212
|
-
| 404 on every route
|
|
213
|
-
| Bundle too large
|
|
214
|
-
| Cold starts too slow
|
|
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
|
|
106
|
-
-
|
|
107
|
-
-
|
|
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
|
-
|
|
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
|
-
>
|
|
188
|
-
>
|
|
189
|
-
>
|
|
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
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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 `
|
|
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
|
|
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 `
|
|
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 `
|
|
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
|
|
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
|
|
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 `
|
|
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
|
|
49
|
-
|
|
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
|
-
//
|
|
92
|
-
|
|
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
|