@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.
- package/README.md +107 -155
- package/catalog/create-tool/examples/21-tool-with-availability-constraints.md +1 -1
- package/catalog/create-tool/references/availability.md +10 -10
- package/catalog/create-tool/references/ui-widgets.md +30 -8
- 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-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 +54 -20
- package/catalog/frontmcp-config/references/configure-http.md +5 -2
- package/catalog/frontmcp-config/references/configure-throttle-guard-config.md +1 -1
- 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/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/references/build-for-browser.md +38 -9
- package/catalog/frontmcp-deployment/references/build-for-mcpb.md +15 -9
- package/catalog/frontmcp-deployment/references/build-for-sdk.md +2 -1
- package/catalog/frontmcp-deployment/references/deploy-to-cloudflare.md +3 -1
- package/catalog/frontmcp-deployment/references/deploy-to-lambda.md +2 -2
- package/catalog/frontmcp-deployment/references/deploy-to-node.md +11 -11
- 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 -49
- package/catalog/frontmcp-development/references/create-plugin-hooks.md +15 -5
- package/catalog/frontmcp-development/references/create-plugin.md +8 -4
- 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 +127 -24
- package/catalog/frontmcp-development/references/openapi-adapter.md +52 -2
- package/catalog/frontmcp-observability/references/metrics-endpoint.md +3 -1
- package/catalog/frontmcp-production-readiness/examples/distributed-ha/ha-kubernetes-3-replicas.md +12 -1
- package/catalog/frontmcp-production-readiness/references/common-checklist.md +2 -1
- package/catalog/frontmcp-production-readiness/references/distributed-ha.md +48 -28
- package/catalog/frontmcp-setup/examples/multi-app-composition/local-apps-with-shared-tools.md +10 -6
- 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 +1 -1
- package/catalog/frontmcp-setup/references/frontmcp-skills-usage.md +28 -15
- package/catalog/frontmcp-setup/references/multi-app-composition.md +25 -16
- package/catalog/frontmcp-setup/references/nx-workflow.md +53 -21
- 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 +12 -8
- package/catalog/frontmcp-testing/SKILL.md +16 -12
- package/catalog/frontmcp-testing/references/setup-testing.md +14 -1
- package/catalog/skills-manifest.json +13 -11
- package/package.json +1 -1
|
@@ -73,7 +73,7 @@ The workspace generator creates the directory structure (`apps/`, `libs/`, `serv
|
|
|
73
73
|
nx g @frontmcp/nx:app my-app
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
-
Creates an `@App`-decorated class in `apps/my-app/` with a tools
|
|
76
|
+
Creates an `@App`-decorated class in `apps/my-app/` with a sample `hello` tool and its starter spec (`src/tools/hello.tool.spec.ts`, so `nx test my-app` has a test to run), and project configuration with `build`, `dev`, `serve`, `test`, `typecheck` and `inspector` targets. The `--project` flag is not needed for app generation since the app is the project.
|
|
77
77
|
|
|
78
78
|
### Generate a Shared Library
|
|
79
79
|
|
|
@@ -81,7 +81,12 @@ Creates an `@App`-decorated class in `apps/my-app/` with a tools directory, barr
|
|
|
81
81
|
nx g @frontmcp/nx:lib my-lib
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
Creates a shared library in `libs/my-lib/` with TypeScript configuration, Jest setup, and
|
|
84
|
+
Creates a shared library in `libs/my-lib/` with TypeScript configuration, Jest setup, barrel exports and a starter spec. Use libraries for shared providers, utilities, and types that multiple apps consume.
|
|
85
|
+
|
|
86
|
+
- `--libType plugin` / `--libType adapter` start from the same class the `plugin` / `adapter` generators write (an adapter declares `options: { name: string } & <Name>AdapterOptions`, which `DynamicAdapter` requires).
|
|
87
|
+
- The library's `test` target runs `@frontmcp/nx:test` (no `@nx/jest` needed), and `typecheck` runs `tsc --noEmit` on `tsconfig.lib.json` and `tsconfig.spec.json`. Libraries need no build target: apps import them through the path alias and `nx build <app>` bundles them.
|
|
88
|
+
- `--publishable` (with `--importPath @my-org/my-lib`) adds what publishing needs: a cached `build` target that runs `tsc -p tsconfig.lib.json` into `libs/my-lib/dist`, and a `package.json` named after the import path with `main`/`types` pointing at `./dist/index.js`/`./dist/index.d.ts`, `files: ["dist"]`, and dependencies on `tslib` and (plugin, adapter, tool-register) `@frontmcp/sdk` at the workspace ranges. `nx build my-lib`, then `npm publish libs/my-lib`.
|
|
89
|
+
- The import path (`@frontmcp/my-lib`, or `--importPath`) is registered in `tsconfig.base.json` as `["./libs/my-lib/src/index.ts"]`. The leading `./` matters: TypeScript rejects a bare `libs/...` target when the base config has no `baseUrl` (TS5090), which is the case in `create-nx-workspace --preset=ts` workspaces.
|
|
85
90
|
|
|
86
91
|
### Generate a Server (Deployment Shell)
|
|
87
92
|
|
|
@@ -89,7 +94,16 @@ Creates a shared library in `libs/my-lib/` with TypeScript configuration, Jest s
|
|
|
89
94
|
nx g @frontmcp/nx:server my-server --deploymentTarget=node --apps=my-app
|
|
90
95
|
```
|
|
91
96
|
|
|
92
|
-
Creates a `@FrontMcp`-decorated server class in `servers/my-server/` that composes one or more apps. The server is the deployment unit.
|
|
97
|
+
Creates a `@FrontMcp`-decorated server class in `servers/my-server/` that composes one or more apps. The server is the deployment unit; its Nx project is named `server-my-server`, with `build`, `dev`, `typecheck` and `deploy` targets.
|
|
98
|
+
|
|
99
|
+
`nx build server-my-server` runs `frontmcp build --target <deploymentTarget>` into `servers/my-server/dist`, and the generated deployment files point at what that build writes:
|
|
100
|
+
|
|
101
|
+
| Target | Build output | Generated file |
|
|
102
|
+
| ------------ | ------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
103
|
+
| `node` | `dist/node/server-my-server.bundle.js` | `Dockerfile` runs it (`CMD ["node", "dist/node/server-my-server.bundle.js"]`, `FRONTMCP_BIND_ADDRESS=all`); build context is the workspace root, ignore rules in `Dockerfile.dockerignore` |
|
|
104
|
+
| `vercel` | `.vercel/output/` (Build Output API) and `dist/vercel/handler.cjs` | `vercel.json` installs and runs `nx build server-my-server` from the workspace root; set the Vercel Root Directory to `servers/my-server` |
|
|
105
|
+
| `lambda` | `dist/lambda/handler.cjs`, exporting `handler` | `template.yaml`: `CodeUri: dist/lambda/`, `Handler: handler.handler`; the generator adds `@codegenie/serverless-express`, which the bundle loads at runtime (provide it with a Lambda layer) |
|
|
106
|
+
| `cloudflare` | `dist/cloudflare/index.js` | `wrangler.toml`: `main = "dist/cloudflare/index.js"` |
|
|
93
107
|
|
|
94
108
|
| Option | Type | Default | Description |
|
|
95
109
|
| ------------------ | ------------------------------------------------ | ---------------- | ------------------------------------- |
|
|
@@ -150,7 +164,7 @@ Creates a `SKILL.md`-based skill directory in `apps/my-app/src/skills/my-skill/`
|
|
|
150
164
|
nx g @frontmcp/nx:agent my-agent --project=my-app
|
|
151
165
|
```
|
|
152
166
|
|
|
153
|
-
Creates an `@Agent`-decorated class in `apps/my-app/src/agents/`. Agents are autonomous AI components with their own LLM providers and isolated scopes, automatically exposed as `
|
|
167
|
+
Creates an `@Agent`-decorated class in `apps/my-app/src/agents/`. Agents are autonomous AI components with their own LLM providers and isolated scopes, automatically exposed as `invoke_<agent_id>` tools. The generated `llm` block picks `anthropic` (`ANTHROPIC_API_KEY`) for `claude*` models and `openai` (`OPENAI_API_KEY`) otherwise, and `--tools a,b` imports each tool class from `../tools/<name>.tool` (de-duplicated) instead of using string names.
|
|
154
168
|
|
|
155
169
|
### Plugin
|
|
156
170
|
|
|
@@ -213,7 +227,7 @@ Creates an `@AuthProvider` class in `apps/my-app/src/auth-providers/`. Auth prov
|
|
|
213
227
|
### Build a Single Project
|
|
214
228
|
|
|
215
229
|
```bash
|
|
216
|
-
nx build my-server
|
|
230
|
+
nx build server-my-server
|
|
217
231
|
```
|
|
218
232
|
|
|
219
233
|
Builds the server and all its dependencies in the correct order. Nx caches build outputs so subsequent builds of unchanged projects are instant (generated projects set `cache: true`, and `init` covers existing workspaces).
|
|
@@ -226,10 +240,20 @@ The `@frontmcp/nx:build` executor runs `frontmcp build` from the project root us
|
|
|
226
240
|
nx test my-app
|
|
227
241
|
```
|
|
228
242
|
|
|
229
|
-
Runs `frontmcp test` from the project root. The generated `jest.config.cjs` uses the swc transform, loads `@frontmcp/testing/setup`, and maps the `tsconfig.base.json` path aliases so imports of workspace libraries resolve. Test files must use `.spec.ts` extension (not `.test.ts`).
|
|
243
|
+
Runs `frontmcp test` from the project root (apps and libraries alike; both are generated with a starter spec, so `nx run-many -t test` finds tests in a fresh workspace). The generated `jest.config.cjs` uses the swc transform, loads `@frontmcp/testing/setup`, and maps the `tsconfig.base.json` path aliases so imports of workspace libraries resolve. Test files must use `.spec.ts` extension (not `.test.ts`).
|
|
230
244
|
|
|
231
245
|
The `inspector` executor forwards its `port` option as the `CLIENT_PORT` environment variable (the `frontmcp inspector` command has no port flag).
|
|
232
246
|
|
|
247
|
+
### Type-check a Project
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
nx typecheck my-app
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Runs the project's own `typecheck` target: `tsc --noEmit` on `tsconfig.lib.json` and `tsconfig.spec.json`. The generated `tsconfig.json` sets `"nx": { "addTypecheckTarget": false }`, so a workspace that registers `@nx/js/typescript` does not infer a `tsc --build --emitDeclarationOnly` target for it (which fails with TS5069). It also makes the project build JavaScript on any base config: `module: commonjs` with `moduleResolution: node10` on TypeScript 5 and `bundler` on TypeScript 6+, `rootDir` at the workspace root, and `composite` / `declarationMap` / `emitDeclarationOnly` off for TS-solution bases.
|
|
254
|
+
|
|
255
|
+
Workspaces generated by `@frontmcp/nx` do not register `@nx/js/typescript`: its `typescript-sync` generator needs a solution-style root `tsconfig.json` and fails builds with `Missing root "tsconfig.json"` once a library is in the graph. A workspace generated by 1.8.7 or earlier should remove that entry from `nx.json` `plugins`, and give each library it publishes the `build` target and `package.json` that `--publishable` generates (the inferred `build` goes away with the plugin).
|
|
256
|
+
|
|
233
257
|
### Build All Projects
|
|
234
258
|
|
|
235
259
|
```bash
|
|
@@ -297,13 +321,16 @@ my-project/
|
|
|
297
321
|
my-lib/
|
|
298
322
|
src/
|
|
299
323
|
index.ts
|
|
324
|
+
my-lib.spec.ts # starter spec
|
|
300
325
|
project.json
|
|
326
|
+
jest.config.cjs
|
|
301
327
|
servers/
|
|
302
328
|
my-server/
|
|
303
329
|
src/
|
|
304
330
|
main.ts # @FrontMcp server (default export)
|
|
305
331
|
project.json
|
|
306
332
|
Dockerfile # (node target)
|
|
333
|
+
Dockerfile.dockerignore
|
|
307
334
|
nx.json
|
|
308
335
|
tsconfig.base.json
|
|
309
336
|
package.json
|
|
@@ -314,13 +341,13 @@ my-project/
|
|
|
314
341
|
### Serve in Development
|
|
315
342
|
|
|
316
343
|
```bash
|
|
317
|
-
nx
|
|
344
|
+
nx dev my-app
|
|
318
345
|
```
|
|
319
346
|
|
|
320
|
-
|
|
347
|
+
A server shell composes its apps; run it the same way through its `dev` target:
|
|
321
348
|
|
|
322
349
|
```bash
|
|
323
|
-
nx dev my-server
|
|
350
|
+
nx dev server-my-server
|
|
324
351
|
```
|
|
325
352
|
|
|
326
353
|
### Generate, Build, and Test a New Feature
|
|
@@ -337,7 +364,7 @@ nx g @frontmcp/nx:tool calculate-tax --project=billing-app
|
|
|
337
364
|
nx test billing-app
|
|
338
365
|
|
|
339
366
|
# 4. Build the server that includes this app
|
|
340
|
-
nx build billing
|
|
367
|
+
nx build server-billing
|
|
341
368
|
|
|
342
369
|
# 5. Or test everything affected by your changes
|
|
343
370
|
nx affected -t test
|
|
@@ -383,7 +410,7 @@ Complete list of all `@frontmcp/nx` generators from `generators.json`:
|
|
|
383
410
|
| Test file naming | `my-tool.tool.spec.ts` | `my-tool.tool.test.ts` | FrontMCP enforces `.spec.ts` extension; `.test.ts` files are not picked up by Jest config |
|
|
384
411
|
| Affected-only CI testing | `nx affected -t test` | `nx run-many -t test` | `affected` only runs tests for changed projects, saving CI time and compute |
|
|
385
412
|
| Server composition | `nx g @frontmcp/nx:server my-server --apps=app-a,app-b` | Manually importing apps in `main.ts` | The server generator wires app composition and deployment config automatically |
|
|
386
|
-
| Build before deploy | `nx build my-server` (builds server + all deps)
|
|
413
|
+
| Build before deploy | `nx build server-my-server` (builds server + all deps) | Building each lib and app individually | Nx resolves the dependency graph and builds in the correct order with caching |
|
|
387
414
|
|
|
388
415
|
## Verification Checklist
|
|
389
416
|
|
|
@@ -401,25 +428,30 @@ Complete list of all `@frontmcp/nx` generators from `generators.json`:
|
|
|
401
428
|
|
|
402
429
|
### Build and Test
|
|
403
430
|
|
|
404
|
-
- [ ] `nx build
|
|
431
|
+
- [ ] `nx build server-<name>` completes without TypeScript errors or warnings
|
|
432
|
+
- [ ] `nx typecheck <project>` passes for apps, libs and servers
|
|
405
433
|
- [ ] `nx test <app>` passes with 95%+ coverage
|
|
406
434
|
- [ ] `nx affected -t test` correctly identifies changed projects
|
|
407
435
|
|
|
408
436
|
### Development Workflow
|
|
409
437
|
|
|
410
|
-
- [ ] `nx
|
|
438
|
+
- [ ] `nx dev <app>` or `nx dev server-<name>` starts the server successfully
|
|
411
439
|
- [ ] `nx graph` renders the project dependency graph in the browser
|
|
412
440
|
|
|
413
441
|
## Troubleshooting
|
|
414
442
|
|
|
415
|
-
| Problem
|
|
416
|
-
|
|
|
417
|
-
| `Cannot find module '@frontmcp/nx'`
|
|
418
|
-
| Generator creates files in the wrong directory
|
|
419
|
-
| `nx affected` runs nothing despite changes
|
|
420
|
-
|
|
|
421
|
-
|
|
|
422
|
-
|
|
|
443
|
+
| Problem | Cause | Solution |
|
|
444
|
+
| ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
445
|
+
| `Cannot find module '@frontmcp/nx'` | Plugin not installed | Run `yarn add -D @frontmcp/nx` and ensure it appears in `devDependencies` |
|
|
446
|
+
| Generator creates files in the wrong directory | Missing or incorrect `--project` flag | Always pass `--project=<app-name>` for primitive generators; verify the app exists in `apps/` |
|
|
447
|
+
| `nx affected` runs nothing despite changes | Base branch not configured or no dependency link | Check `nx.json` for `defaultBase` setting; verify the changed file belongs to a project in the graph |
|
|
448
|
+
| `[@nx/js:typescript-sync]: Missing root "tsconfig.json"` on `nx build` | Workspace generated by `@frontmcp/nx` 1.8.7 or earlier registers `@nx/js/typescript` | Remove `@nx/js/typescript` from `plugins` in `nx.json`; FrontMCP projects declare their own `build`, `test` and `typecheck` targets |
|
|
449
|
+
| `nx typecheck` fails with TS5069 | Inferred `tsc --build --emitDeclarationOnly` target on a project without `declaration` | Regenerate the project, or add the `typecheck` target (`tsc --noEmit -p tsconfig.lib.json`) and `"nx": { "addTypecheckTarget": false }` in its `tsconfig.json` |
|
|
450
|
+
| TS5090 on a `tsconfig.base.json` path alias | Alias target written without `./` and no `baseUrl` | Write targets as `./libs/<name>/src/index.ts` (the `lib` and `ui-*` generators do) |
|
|
451
|
+
| `ERESOLVE` on `esbuild` after `nx g @frontmcp/nx:ui-shell` | An `esbuild` range below the `>=0.27` peer of `@frontmcp/uipack` | Re-run the UI generator (it raises an older `esbuild` range to `^0.27.3`) or set `esbuild` to `^0.27.3` |
|
|
452
|
+
| Build fails with circular dependency error | Library A imports from Library B and vice versa | Use `nx graph` to visualize the cycle; extract shared code into a new library |
|
|
453
|
+
| Cache not working (full rebuild every time) | Executor targets are not marked cacheable | Run `nx g @frontmcp/nx:init`, or set `cache: true` on the target / in `targetDefaults` |
|
|
454
|
+
| `Cannot find module '@scope/lib'` in Jest | Old `jest.config.ts` without the path-alias mapper | Use the generated `jest.config.cjs` (maps `tsconfig.base.json` paths) or add a `moduleNameMapper` |
|
|
423
455
|
|
|
424
456
|
## Examples
|
|
425
457
|
|
|
@@ -167,8 +167,11 @@ nx g @frontmcp/nx:lib shared-utils
|
|
|
167
167
|
## Build and Test Commands
|
|
168
168
|
|
|
169
169
|
```bash
|
|
170
|
-
# Build a specific server
|
|
171
|
-
nx build gateway
|
|
170
|
+
# Build a specific server (server projects are named server-<name>)
|
|
171
|
+
nx build server-gateway
|
|
172
|
+
|
|
173
|
+
# Type-check a project
|
|
174
|
+
nx typecheck billing
|
|
172
175
|
|
|
173
176
|
# Test a specific app
|
|
174
177
|
nx test billing
|
|
@@ -233,8 +236,8 @@ Use `nx graph` to visualize the dependency graph and ensure no circular imports
|
|
|
233
236
|
|
|
234
237
|
### Build and Test
|
|
235
238
|
|
|
236
|
-
- [ ] `nx build gateway` (or server
|
|
237
|
-
- [ ] `nx test billing` (or app name) passes all tests
|
|
239
|
+
- [ ] `nx build server-gateway` (or `server-<name>`) succeeds without errors
|
|
240
|
+
- [ ] `nx test billing` (or app name) passes all tests; new apps and libraries start with a spec
|
|
238
241
|
- [ ] `nx run-many -t test` runs all tests across the workspace
|
|
239
242
|
- [ ] `nx graph` shows no circular dependencies between apps
|
|
240
243
|
|
|
@@ -404,6 +404,19 @@ frontmcp dev --show-conflict
|
|
|
404
404
|
> `http.port: 4000` in metadata makes the child bind to 4000 regardless of
|
|
405
405
|
> `--port`/`PORT`, so the pre-flight probe becomes advisory only.
|
|
406
406
|
|
|
407
|
+
Ctrl+C, or `SIGINT` / `SIGTERM` sent to the `frontmcp dev` process alone
|
|
408
|
+
(`kill <pid>`), stops the whole tree — tsx, the server it forks and the type
|
|
409
|
+
checker — and the command returns once the port is free.
|
|
410
|
+
|
|
411
|
+
`frontmcp dev` and `frontmcp build` work from any subfolder: they find
|
|
412
|
+
`frontmcp.config.*` by walking up and run from its folder, so `entry`,
|
|
413
|
+
`tsconfig.json` and `.env` resolve from the project root.
|
|
414
|
+
|
|
415
|
+
`frontmcp init` edits `tsconfig.json` in place, keeping comments and trailing
|
|
416
|
+
commas; a file that is not valid JSON(C) is reported (line and column) and left
|
|
417
|
+
untouched, never overwritten. So is one that declares an option `init` must set
|
|
418
|
+
more than once — only the last occurrence takes effect, so remove the duplicate.
|
|
419
|
+
|
|
407
420
|
Test with curl:
|
|
408
421
|
|
|
409
422
|
```bash
|
|
@@ -468,6 +481,8 @@ nx g @frontmcp/nx:app my-app --directory apps/my-app
|
|
|
468
481
|
nx g @frontmcp/nx:server my-server --directory servers/my-server
|
|
469
482
|
```
|
|
470
483
|
|
|
484
|
+
This works in `create-nx-workspace --preset=ts` workspaces too: the generated project `tsconfig.json` compiles CommonJS JavaScript whatever the TS-solution base inherits (`composite`, `emitDeclarationOnly`, `customConditions`), resolving with `node10` on TypeScript 5 and `bundler` on TypeScript 6+, and library path aliases are written as `./libs/<name>/src/index.ts`, which needs no `baseUrl`.
|
|
485
|
+
|
|
471
486
|
### 7c. Nx project.json example
|
|
472
487
|
|
|
473
488
|
If manually configuring, add a `project.json`:
|
|
@@ -138,6 +138,15 @@ redis: {
|
|
|
138
138
|
},
|
|
139
139
|
```
|
|
140
140
|
|
|
141
|
+
From a single connection URL (what managed Redis providers hand out). It is read into `host` / `port` / `password` / `db` at parse time; `rediss://` sets `tls: true`; the user part must be empty or `default`; percent-encode reserved password characters (`@` as `%40`, `%` as `%25`), since a malformed escape is a validation error:
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
redis: {
|
|
145
|
+
url: process.env['REDIS_URL'], // redis://:password@host:6379/0 (or rediss:// for TLS)
|
|
146
|
+
keyPrefix: 'mcp:', // optional, as with the other forms
|
|
147
|
+
},
|
|
148
|
+
```
|
|
149
|
+
|
|
141
150
|
### For Vercel KV
|
|
142
151
|
|
|
143
152
|
```typescript
|
|
@@ -313,13 +322,13 @@ You should see session keys like `mcp:session:<session-id>`.
|
|
|
313
322
|
|
|
314
323
|
Every instance behind the load balancer needs the **same** values:
|
|
315
324
|
|
|
316
|
-
- `MCP_SESSION_SECRET` -- session ids are encrypted with it. An id minted under a different secret is answered with HTTP 404 and the client re-initializes (every auth mode, including `public`, where the id is the caller's only credential). An anonymous session minted by one instance is honored by any instance with the same secret.
|
|
325
|
+
- `MCP_SESSION_SECRET` -- session ids are encrypted with it. An id minted under a different secret is answered with HTTP 404 and the client re-initializes (every auth mode, including `public`, where the id is the caller's only credential). An anonymous session minted by one instance is honored by any instance with the same secret: the receiving instance recreates the transport from the stored session (in distributed mode it relays the request to the live node that owns the session, or takes the session over from a node that stopped).
|
|
317
326
|
- `VAULT_SECRET` (or `JWT_SECRET`) -- signs MCP 2026-07-28 `requestState`. Without either, each instance uses a random per-process key and a multi-round tool (`elicit()` / `sample()`) whose next round lands elsewhere asks its first question again. In production, `redis` or `transport.persistence` without either secret logs a startup warning; each rejected round logs `mcp-20260728: rejected requestState` with `reason: 'bad-signature'` and a `hint` naming `VAULT_SECRET`.
|
|
318
327
|
|
|
319
328
|
### What happens when Redis is down at startup
|
|
320
329
|
|
|
321
330
|
- `redis` and `transport.persistence` fall back to in-memory storage and log the failure (`[TransportService] Failed to connect to redis - session persistence disabled`); the server starts.
|
|
322
|
-
- `throttle.storage` fails closed: startup aborts with `GuardStorageUnavailableError` (`throttle.storage (redis) is unavailable: …`), the default in production. If Redis goes away while the server runs, a rate-limited call is refused with the same error, not `Internal FrontMCP error
|
|
331
|
+
- `throttle.storage` fails closed: startup aborts with `GuardStorageUnavailableError` (`throttle.storage (redis) is unavailable: …`), the default in production. If Redis goes away while the server runs, a rate-limited call is refused with the same error, not `Internal FrontMCP error` (HTTP 503 from the `global` check; an `isError` result with `_meta.code: 'GUARD_STORAGE_UNAVAILABLE'` inside `tools/call`). Opt in to per-instance counters explicitly (they also cover a mid-run outage):
|
|
323
332
|
|
|
324
333
|
```typescript
|
|
325
334
|
throttle: {
|
|
@@ -332,7 +341,7 @@ throttle: {
|
|
|
332
341
|
},
|
|
333
342
|
```
|
|
334
343
|
|
|
335
|
-
`throttle.storage` takes the `@frontmcp/utils` storage shape (`{ type: 'redis', redis: { config } }` or `{ type: 'redis', redis: { url } }`), not the top-level `redis` shape.
|
|
344
|
+
`throttle.storage` takes the `@frontmcp/utils` storage shape (`{ type: 'redis', redis: { config } }` or `{ type: 'redis', redis: { url } }`), not the top-level `redis` shape (which takes `{ host, ... }` or `{ url }`).
|
|
336
345
|
|
|
337
346
|
## Common Patterns
|
|
338
347
|
|
|
@@ -77,6 +77,9 @@ interface SqliteOptionsInput {
|
|
|
77
77
|
|
|
78
78
|
/** Interval in ms for purging expired keys (default: 60000) */
|
|
79
79
|
ttlCleanupIntervalMs?: number;
|
|
80
|
+
|
|
81
|
+
/** Ms to wait for a lock held by another process before SQLITE_BUSY (default: 5000) */
|
|
82
|
+
busyTimeoutMs?: number;
|
|
80
83
|
}
|
|
81
84
|
```
|
|
82
85
|
|
|
@@ -406,14 +409,15 @@ The change in `src/main.ts`:
|
|
|
406
409
|
|
|
407
410
|
## Troubleshooting
|
|
408
411
|
|
|
409
|
-
| Problem | Cause | Solution
|
|
410
|
-
| --------------------------------------- | --------------------------------------------------------------- |
|
|
411
|
-
| `Cannot find module 'better-sqlite3'` | Native module not installed | Run `yarn add @frontmcp/storage-sqlite better-sqlite3`
|
|
412
|
-
| `Could not locate the bindings file` | Native compilation failed | Ensure build tools are installed (Xcode CLI on macOS, `build-essential` on Linux), delete `node_modules` and reinstall
|
|
413
|
-
| `SQLITE_BUSY` errors | Multiple processes accessing the same database file | Enable WAL mode (`walMode: true`) or ensure only one process writes to the database
|
|
414
|
-
| `
|
|
415
|
-
|
|
|
416
|
-
|
|
|
412
|
+
| Problem | Cause | Solution |
|
|
413
|
+
| --------------------------------------- | --------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
414
|
+
| `Cannot find module 'better-sqlite3'` | Native module not installed | Run `yarn add @frontmcp/storage-sqlite better-sqlite3` |
|
|
415
|
+
| `Could not locate the bindings file` | Native compilation failed | Ensure build tools are installed (Xcode CLI on macOS, `build-essential` on Linux), delete `node_modules` and reinstall |
|
|
416
|
+
| `SQLITE_BUSY` errors | Multiple processes accessing the same database file | Enable WAL mode (`walMode: true`), raise `busyTimeoutMs` (default 5000), or ensure only one process writes to the database |
|
|
417
|
+
| `Failed to persist session to SQLite` | A session write waited longer than `busyTimeoutMs` for the lock | Raise `busyTimeoutMs`, or use Redis for many writer processes |
|
|
418
|
+
| `SQLITE_READONLY` | Insufficient file permissions | Check write permissions on the database file and its parent directory |
|
|
419
|
+
| WAL errors on network mount | WAL mode requires a local filesystem with shared-memory support | Move the database to a local disk or set `walMode: false` |
|
|
420
|
+
| Encrypted data unreadable after restart | Encryption secret changed or missing | The secret must be identical across restarts; if the original secret is lost, delete the database and let it be recreated |
|
|
417
421
|
|
|
418
422
|
## Examples
|
|
419
423
|
|
|
@@ -146,18 +146,22 @@ This is a router skill. Follow this order to pick a testing approach, then move
|
|
|
146
146
|
|
|
147
147
|
## Troubleshooting
|
|
148
148
|
|
|
149
|
-
| Problem | Cause
|
|
150
|
-
| --------------------------------------------------------------------------------- |
|
|
151
|
-
| Jest not finding test files | Wrong file extension (`.test.ts` instead of `.spec.ts`)
|
|
152
|
-
| `SyntaxError: Unexpected token 'export'` | An ESM-only dependency is being ignored instead of transpiled
|
|
153
|
-
| Coverage below 95% | Untested error paths or conditional branches
|
|
154
|
-
| E2E test timeout | Server startup too slow or port conflict
|
|
155
|
-
| DI resolution fails in tests | Provider not registered in test scope
|
|
156
|
-
| Istanbul shows 0% on async methods | TypeScript source-map mismatch with Istanbul
|
|
157
|
-
| Specs see no `.env` values | An older CLI did not load `.env` for `frontmcp test`, only for `dev`
|
|
158
|
-
| `Invalid first argument, true` at collection time | `test.skip(condition, reason)` reached Jest's `skip(name, fn)`
|
|
159
|
-
| `test.use()` in one `describe` leaks into another, or two files fight over a port | Older `@frontmcp/testing` kept one global config and per-process ports
|
|
160
|
-
| Every spec fails with `HTTP 404` after setting `http.entryPath` | The test client always connected to the server root
|
|
149
|
+
| Problem | Cause | Solution |
|
|
150
|
+
| --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
151
|
+
| Jest not finding test files | Wrong file extension (`.test.ts` instead of `.spec.ts`) | Rename to `.spec.ts`; check `testMatch` in jest.config |
|
|
152
|
+
| `SyntaxError: Unexpected token 'export'` | An ESM-only dependency is being ignored instead of transpiled | Add it to `test.esmPackages` in `frontmcp.config.ts`. With a hand-written `jest.config.ts`, use the pnpm-safe `transformIgnorePatterns` in [`setup-testing`](./references/setup-testing.md#jest-configuration) AND make sure `transform` matches `.js` (`^.+\.[tj]sx?$` + `allowJs`) — un-ignoring a file does nothing if no transform matches it |
|
|
153
|
+
| Coverage below 95% | Untested error paths or conditional branches | Run `frontmcp test --coverage` and inspect uncovered lines in the report |
|
|
154
|
+
| E2E test timeout | Server startup too slow or port conflict | Increase Jest timeout; use random port allocation |
|
|
155
|
+
| DI resolution fails in tests | Provider not registered in test scope | Register mock providers before creating the test context |
|
|
156
|
+
| Istanbul shows 0% on async methods | TypeScript source-map mismatch with Istanbul | Known issue with some TS compilation settings; verify coverage with actual test output |
|
|
157
|
+
| Specs see no `.env` values | An older CLI did not load `.env` for `frontmcp test`, only for `dev` | Upgrade the CLI — `frontmcp test` now loads `.env` / `.env.local` with the same precedence as `dev` (real environment wins, so CI secrets still override). Pass `--no-env` for a hermetic run |
|
|
158
|
+
| `Invalid first argument, true` at collection time | `test.skip(condition, reason)` reached Jest's `skip(name, fn)` | Upgrade the CLI — the Playwright signature is supported: `test.skip(!hasCredentials, 'credentials not set')` skips every test registered after it in the enclosing block |
|
|
159
|
+
| `test.use()` in one `describe` leaks into another, or two files fight over a port | Older `@frontmcp/testing` kept one global config and per-process ports | Upgrade — `test.use()` is scoped per `describe` and ports are locked across Jest workers; use `port: 0` |
|
|
160
|
+
| Every spec fails with `HTTP 404` after setting `http.entryPath` | The test client always connected to the server root | Upgrade the CLI — the client now follows the `entryPaths` a 404 reports, and `test.use({ entryPath: '/mcp' })` sets it explicitly |
|
|
161
|
+
| `test.beforeEach(async ({ mcp }) => …)` times out waiting for `done` | Older `@frontmcp/testing` handed the hook to Jest, which read the parameter as `done` | Upgrade — hooks that take a parameter now receive the test's fixtures (same `mcp` as the test); `beforeAll` / `afterAll` never get fixtures |
|
|
162
|
+
| `test.use({ transport: 'sse' })` throws "SSE transport not yet implemented" | Older `@frontmcp/testing` had no legacy SSE client | Upgrade — `'sse'` is the legacy HTTP+SSE transport; the server must enable it with `transport: { protocol: { legacy: true } }` |
|
|
163
|
+
| `Module @swc/jest in the transform option was not found` from `frontmcp test` | Older `@frontmcp/testing` did not install the transformer the injected config uses | Upgrade — `@frontmcp/testing` depends on `@swc/jest` / `@swc/core`, and `frontmcp test` resolves the transformer through it |
|
|
164
|
+
| `ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG` from a server started inside a spec | Older SDKs loaded the optional `vectoriadb` with `import()` at boot | Upgrade the SDK — `vectoriadb` loads on the first skill search, through `require` where available; no `--experimental-vm-modules` needed |
|
|
161
165
|
|
|
162
166
|
## Examples
|
|
163
167
|
|
|
@@ -399,6 +399,16 @@ You usually do not need to set this: when the client's first request 404s and th
|
|
|
399
399
|
### Scoping config, parameterised tests, ports and restarts
|
|
400
400
|
|
|
401
401
|
- `test.use()` is scoped to the `describe` block it is called in. Nested blocks merge over outer ones (`env` key by key); each distinct configuration gets its own server, stopped by the block that configured it. At file level it applies to the whole file.
|
|
402
|
+
- The `env` object is read when the server starts, so a `beforeAll` may fill in values known only later (e.g. the port of an upstream `TestServer` it started).
|
|
403
|
+
- `test.beforeEach` / `test.afterEach` callbacks that take a parameter get the test's fixtures, Playwright-style — the **same** `mcp` / `server` / `auth` as the test. `beforeEach` runs outer blocks first, `afterEach` inner blocks first, and fixtures are torn down after the last `afterEach`. A callback without parameters is a plain Jest hook; `test.beforeAll` / `test.afterAll` never receive fixtures. Jest's `done` style is not supported on `test.beforeEach` / `test.afterEach`.
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
test.beforeEach(async ({ mcp }) => {
|
|
407
|
+
await mcp.tools.call('reset_state', {});
|
|
408
|
+
});
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
- `transport: 'sse'` (in `test.use()` or `server.createClient()`) uses the legacy HTTP+SSE transport: `GET <entryPath>/sse`, messages POSTed to the endpoint the server names, responses read from the stream. The server must enable it (`transport: { protocol: { legacy: true } }`); otherwise connecting fails with the HTTP status.
|
|
402
412
|
- `port: 0` (or omitting `port`) picks any free port. Ports are reserved with a cross-process lock, so parallel Jest workers do not collide.
|
|
403
413
|
- `auth: { mode, type }` reaches the server process as `FRONTMCP_TEST_AUTH_MODE` / `FRONTMCP_TEST_AUTH_TYPE`; `mode: 'public'` also keeps the `mcp` client anonymous.
|
|
404
414
|
- `test.each` / `test.describe.each` pass the row values to the callback (fixtures first for `test.each`):
|
|
@@ -518,7 +528,10 @@ In standalone projects driven by `frontmcp test`, prefer `test.esmPackages` in
|
|
|
518
528
|
`frontmcp.config.ts` — the injected config already carries the pattern above,
|
|
519
529
|
and already transpiles `jose`, `@noble/hashes` and `@noble/ciphers` (CodeCall).
|
|
520
530
|
`frontmcp test` does not set `NODE_OPTIONS=--experimental-vm-modules` (it would make Jest
|
|
521
|
-
load ESM natively and bypass these transforms); use Jest 30 (what `frontmcp create` scaffolds)
|
|
531
|
+
load ESM natively and bypass these transforms); use Jest 30 (what `frontmcp create` scaffolds).
|
|
532
|
+
A server created inside the test process (`FrontMcpInstance.createHandler()`, `createDirect()`,
|
|
533
|
+
`create()`) works without the flag too. `@frontmcp/testing` brings `@swc/jest` and `@swc/core`, so a
|
|
534
|
+
bare project needs only `@frontmcp/testing` and `jest` installed:
|
|
522
535
|
|
|
523
536
|
```typescript
|
|
524
537
|
// frontmcp.config.ts
|
|
@@ -998,7 +998,7 @@
|
|
|
998
998
|
"features": [
|
|
999
999
|
"The `'stateless-api'` preset disables SSE, streaming, and sessions entirely",
|
|
1000
1000
|
"Each request is standalone with no server-side state",
|
|
1001
|
-
"
|
|
1001
|
+
"No `sessionMode` needed: sessions follow the protocol preset",
|
|
1002
1002
|
"Required for Vercel, Lambda, Cloudflare Workers where persistent connections are not allowed"
|
|
1003
1003
|
]
|
|
1004
1004
|
}
|
|
@@ -1040,7 +1040,7 @@
|
|
|
1040
1040
|
"level": "basic",
|
|
1041
1041
|
"tags": ["config", "vercel", "lambda", "cloudflare", "session", "transport"],
|
|
1042
1042
|
"features": [
|
|
1043
|
-
"
|
|
1043
|
+
"Serving without sessions: whether the server keeps sessions follows `transport.protocol` (`sessionMode` has no effect)",
|
|
1044
1044
|
"Using the `'stateless-api'` preset: no SSE, no streaming, pure request/response",
|
|
1045
1045
|
"Each request is standalone with no server-side state between invocations",
|
|
1046
1046
|
"Required for serverless targets (Vercel, Lambda, Cloudflare Workers)"
|
|
@@ -1619,13 +1619,15 @@
|
|
|
1619
1619
|
},
|
|
1620
1620
|
{
|
|
1621
1621
|
"name": "nested-agents-with-swarm",
|
|
1622
|
-
"description": "Composing specialized agents into a swarm where an orchestrator can discover and call peers at runtime as `
|
|
1622
|
+
"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.",
|
|
1623
1623
|
"level": "advanced",
|
|
1624
1624
|
"tags": ["development", "agent", "nested", "agents", "swarm"],
|
|
1625
1625
|
"features": [
|
|
1626
|
-
"Setting `swarm: { canSeeOtherAgents: true, visibleAgents: [...] }` on the orchestrator so peers
|
|
1626
|
+
"Setting `swarm: { canSeeOtherAgents: true, visibleAgents: [...] }` on the orchestrator so peers are offered to its model as `invoke_*` tools",
|
|
1627
1627
|
"Setting `swarm: { isVisible: true }` (the default) on specialist peers so they can be called",
|
|
1628
|
-
"Routing is driven by the orchestrator LLM choosing among `
|
|
1628
|
+
"Routing is driven by the orchestrator LLM choosing among `invoke_<peer>` tools, not by a declarative handoff table",
|
|
1629
|
+
"A nested sub-agent (`agents: [...]`) private to the orchestrator, called from code with `this.invokeAgent()`",
|
|
1630
|
+
"`swarm.maxCallDepth` bounding how deep agents may call each other",
|
|
1629
1631
|
"Each agent has its own `llm` config, `tools`, and `systemInstructions` for specialization"
|
|
1630
1632
|
]
|
|
1631
1633
|
}
|
|
@@ -2061,7 +2063,7 @@
|
|
|
2061
2063
|
},
|
|
2062
2064
|
{
|
|
2063
2065
|
"name": "official-plugins",
|
|
2064
|
-
"description": "Guide to the
|
|
2066
|
+
"description": "Guide to the 7 official plugins for discovery, memory, auth, caching, flags, monitoring, and WebMCP",
|
|
2065
2067
|
"examples": [
|
|
2066
2068
|
{
|
|
2067
2069
|
"name": "cache-and-feature-flags",
|
|
@@ -2173,7 +2175,7 @@
|
|
|
2173
2175
|
"Secure defaults: external `$ref` resolution off, spec-URL redirects not followed, internal/private targets blocked (DNS-resolved) \u2014 on `mcp-from-openapi` >= 2.5.0",
|
|
2174
2176
|
"Opting back into external refs with `allowedProtocols`, and restricting the spec URL + `$ref`s with `allowedHosts`",
|
|
2175
2177
|
"Using `allowInternalIPs` for trusted internal/local targets (governs the spec URL and `$ref`s)",
|
|
2176
|
-
"Filtering operations with `includeOperations`, `excludeOperations`, and `filterFn`",
|
|
2178
|
+
"Filtering operations with `readOnlyOnly`, `includeTags`, `includePaths`, `excludeMethods`, `includeOperations`, `excludeOperations`, and `filterFn`",
|
|
2177
2179
|
"Combining security hardening with operation filtering for a production-ready setup"
|
|
2178
2180
|
]
|
|
2179
2181
|
}
|
|
@@ -2474,7 +2476,7 @@
|
|
|
2474
2476
|
},
|
|
2475
2477
|
{
|
|
2476
2478
|
"name": "distributed-ha",
|
|
2477
|
-
"description": "Deploy FrontMCP across multiple pods with heartbeat, session takeover, and notification relay for zero-downtime failover",
|
|
2479
|
+
"description": "Deploy FrontMCP across multiple pods with heartbeat, cross-pod request relay, session takeover, and notification relay for zero-downtime failover",
|
|
2478
2480
|
"examples": [
|
|
2479
2481
|
{
|
|
2480
2482
|
"name": "ha-kubernetes-3-replicas",
|
|
@@ -2912,10 +2914,10 @@
|
|
|
2912
2914
|
"level": "basic",
|
|
2913
2915
|
"tags": ["setup", "multi-app", "local", "multi", "app", "composition"],
|
|
2914
2916
|
"features": [
|
|
2915
|
-
"Multiple `@App` classes with unique `id` fields
|
|
2916
|
-
"Server-level `tools` array for shared tools
|
|
2917
|
+
"Multiple `@App` classes with unique `id` fields, which prefix their tools when two tools share a name (`billing:charge`)",
|
|
2918
|
+
"Server-level `tools` array for shared tools every app serves under their own name (`server:<name>` when an app tool has the same name)",
|
|
2917
2919
|
"Each app is self-contained with its own tools array",
|
|
2918
|
-
"The `id` field on `@App`
|
|
2920
|
+
"The `id` field on `@App` is the prefix of its tools when names collide"
|
|
2919
2921
|
]
|
|
2920
2922
|
},
|
|
2921
2923
|
{
|