@frontmcp/skills 1.8.6 → 1.8.7

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 (40) hide show
  1. package/catalog/create-tool/SKILL.md +24 -24
  2. package/catalog/create-tool/examples/22-tool-with-ui-html-template.md +0 -1
  3. package/catalog/create-tool/examples/23-tool-with-ui-filesource-tsx.md +1 -3
  4. package/catalog/create-tool/examples/24-tool-with-ui-csp-and-bridge.md +4 -6
  5. package/catalog/create-tool/references/decorator-options.md +1 -1
  6. package/catalog/create-tool/references/ui-widgets.md +68 -41
  7. package/catalog/frontmcp-config/examples/configure-throttle/distributed-redis-throttle.md +3 -3
  8. package/catalog/frontmcp-config/references/configure-deployment-targets.md +19 -1
  9. package/catalog/frontmcp-config/references/configure-http.md +6 -4
  10. package/catalog/frontmcp-config/references/configure-security-headers.md +18 -13
  11. package/catalog/frontmcp-config/references/configure-throttle-guard-config.md +4 -4
  12. package/catalog/frontmcp-config/references/configure-throttle.md +1 -1
  13. package/catalog/frontmcp-deployment/SKILL.md +19 -19
  14. package/catalog/frontmcp-deployment/examples/build-for-browser/react-provider-setup.md +5 -3
  15. package/catalog/frontmcp-deployment/examples/deploy-to-vercel/vercel-with-kv.md +2 -0
  16. package/catalog/frontmcp-deployment/references/build-for-browser.md +8 -0
  17. package/catalog/frontmcp-deployment/references/build-for-mcpb.md +9 -8
  18. package/catalog/frontmcp-deployment/references/build-for-sdk.md +9 -9
  19. package/catalog/frontmcp-deployment/references/deploy-to-cloudflare.md +3 -1
  20. package/catalog/frontmcp-deployment/references/deploy-to-lambda.md +9 -8
  21. package/catalog/frontmcp-deployment/references/deploy-to-vercel.md +15 -7
  22. package/catalog/frontmcp-development/references/create-agent.md +8 -7
  23. package/catalog/frontmcp-development/references/create-plugin.md +17 -0
  24. package/catalog/frontmcp-development/references/official-plugins.md +12 -5
  25. package/catalog/frontmcp-guides/references/example-task-manager.md +2 -2
  26. package/catalog/frontmcp-guides/references/example-weather-api.md +2 -2
  27. package/catalog/frontmcp-production-readiness/examples/distributed-ha/ha-kubernetes-3-replicas.md +1 -0
  28. package/catalog/frontmcp-production-readiness/examples/production-node-sdk/package-json-config.md +2 -2
  29. package/catalog/frontmcp-production-readiness/references/common-checklist.md +1 -1
  30. package/catalog/frontmcp-production-readiness/references/distributed-ha.md +17 -7
  31. package/catalog/frontmcp-production-readiness/references/health-readiness-endpoints.md +3 -1
  32. package/catalog/frontmcp-setup/examples/project-structure-nx/nx-generator-scaffolding.md +1 -1
  33. package/catalog/frontmcp-setup/references/nx-workflow.md +25 -17
  34. package/catalog/frontmcp-setup/references/setup-redis.md +1 -1
  35. package/catalog/frontmcp-setup/references/setup-sqlite.md +9 -6
  36. package/catalog/frontmcp-testing/SKILL.md +24 -23
  37. package/catalog/frontmcp-testing/references/setup-testing.md +26 -2
  38. package/catalog/frontmcp-testing/references/test-auth.md +8 -0
  39. package/catalog/skills-manifest.json +4 -4
  40. package/package.json +1 -1
@@ -127,12 +127,13 @@ export default class Server {}
127
127
 
128
128
  Configuration reference:
129
129
 
130
- | Option | Type | Default | Description |
131
- | ---------------------- | -------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------- |
132
- | `path` | `string` | (optional) | Absolute or `~`-prefixed path to the `.sqlite` file. Auto-resolved when omitted — see the default-path policy below. |
133
- | `walMode` | `boolean` | `true` | Enable WAL mode for better read concurrency |
134
- | `encryption` | `{ secret: string }` | `undefined` | AES-256-GCM encryption for values at rest |
135
- | `ttlCleanupIntervalMs` | `number` | `60000` | Interval for purging expired keys (milliseconds) |
130
+ | Option | Type | Default | Description |
131
+ | ---------------------- | -------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
132
+ | `path` | `string` | (optional) | Absolute or `~`-prefixed path to the `.sqlite` file. Auto-resolved when omitted — see the default-path policy below. |
133
+ | `walMode` | `boolean` | `true` | Enable WAL mode for better read concurrency |
134
+ | `busyTimeoutMs` | `number` | `5000` | How long a connection waits on a lock held by another process before `SQLITE_BUSY`. Set before WAL and table creation, so two processes starting on one file wait for each other |
135
+ | `encryption` | `{ secret: string }` | `undefined` | AES-256-GCM encryption for values at rest |
136
+ | `ttlCleanupIntervalMs` | `number` | `60000` | Interval for purging expired keys (milliseconds) |
136
137
 
137
138
  ### With at-rest encryption
138
139
 
@@ -155,6 +156,8 @@ If the database stores sensitive session data (tokens, credentials), enable encr
155
156
  export default class Server {}
156
157
  ```
157
158
 
159
+ Reading a value written under a different secret throws `SqliteDecryptionError` (code `SQLITE_DECRYPTION_FAILED`) whose message names the secret as the likely cause; restore the original secret or delete the database file.
160
+
158
161
  The encryption uses HKDF-SHA256 for key derivation and AES-256-GCM for value encryption. The secret should be at least 32 characters. Store it in environment variables, never in source code.
159
162
 
160
163
  ### For a unix-socket daemon
@@ -95,18 +95,18 @@ This is a router skill. Follow this order to pick a testing approach, then move
95
95
 
96
96
  ## Cross-Cutting Testing Patterns
97
97
 
98
- | Pattern | Rule |
99
- | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
100
- | File naming | Always `.spec.ts` (not `.test.ts`); E2E uses `.e2e.spec.ts` |
101
- | File organization | Split E2E tests by app/feature: `e2e/calc.e2e.spec.ts`, `e2e/ecommerce.e2e.spec.ts`. Never put all tests in a single `server.e2e.spec.ts` |
102
- | Environment | `frontmcp test` loads `.env` / `.env.local` into the Jest child, same precedence as `dev` (real environment wins over the files, config `env.shared` + `env.test` underneath). `--no-env` skips it |
103
- | Test runner | Standalone projects: use `frontmcp test` (auto-generates Jest/SWC config; discovers `src/**/*.spec.ts(x)`, `__tests__/**/*.spec.ts(x)`, and `e2e/**/*.e2e.spec.ts(x)`; transforms both `.ts` and `.tsx` with the automatic JSX runtime; transpiles ESM-only deps such as `jose` under npm, yarn AND pnpm's `node_modules/.pnpm/` store — add your own via `test.esmPackages` in `frontmcp.config.ts`; delegates to a user-provided `jest.config.{ts,js,mjs,cjs,json}` if present, which drops the injected ESM transforms). Nx monorepos: use `nx test <lib>` (resolves the project's `jest.config.ts`). Never invoke `jest --config ...` directly |
104
- | Coverage threshold | 95%+ across statements, branches, functions, lines |
105
- | Test descriptions | Plain English, no prefixes like "PT-001"; describe behavior not implementation |
106
- | Mocking | Mock providers via DI token replacement, never mock the framework |
107
- | httpMock scope | `httpMock` intercepts HTTP in the **test process** only, NOT in the MCP server subprocess. Do not use httpMock to intercept server-to-API calls — those happen in the child process. Use httpMock for verifying client-to-server request shapes or mocking external APIs called from the test itself |
108
- | Error testing | Assert `instanceof` specific error class AND MCP error code |
109
- | Async | Always `await` async operations; use `expect(...).rejects.toThrow()` for async errors |
98
+ | Pattern | Rule |
99
+ | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
100
+ | File naming | Always `.spec.ts` (not `.test.ts`); E2E uses `.e2e.spec.ts` |
101
+ | File organization | Split E2E tests by app/feature: `e2e/calc.e2e.spec.ts`, `e2e/ecommerce.e2e.spec.ts`. Never put all tests in a single `server.e2e.spec.ts` |
102
+ | Environment | `frontmcp test` loads `.env` / `.env.local` into the Jest child, same precedence as `dev` (real environment wins over the files, config `env.shared` + `env.test` underneath). `--no-env` skips it |
103
+ | Test runner | Standalone projects: use `frontmcp test` (auto-generates Jest/SWC config; discovers `src/**/*.spec.ts(x)`, `__tests__/**/*.spec.ts(x)`, and `e2e/**/*.e2e.spec.ts(x)`; transforms both `.ts` and `.tsx` with the automatic JSX runtime; transpiles ESM-only deps such as `jose`, `@noble/hashes` and `@noble/ciphers` (CodeCall) under npm, yarn AND pnpm's `node_modules/.pnpm/` store — add your own via `test.esmPackages` in `frontmcp.config.ts`; does not force `NODE_OPTIONS=--experimental-vm-modules` (it would bypass the ESM transforms; scaffolded projects use Jest 30); delegates to a user-provided `jest.config.{ts,js,mjs,cjs,json}` if present, which drops the injected ESM transforms). Nx monorepos: use `nx test <lib>` (resolves the project's `jest.config.ts`). Never invoke `jest --config ...` directly |
104
+ | Coverage threshold | 95%+ across statements, branches, functions, lines |
105
+ | Test descriptions | Plain English, no prefixes like "PT-001"; describe behavior not implementation |
106
+ | Mocking | Mock providers via DI token replacement, never mock the framework |
107
+ | httpMock scope | `httpMock` intercepts HTTP in the **test process** only, NOT in the MCP server subprocess. Do not use httpMock to intercept server-to-API calls — those happen in the child process. Use httpMock for verifying client-to-server request shapes or mocking external APIs called from the test itself |
108
+ | Error testing | Assert `instanceof` specific error class AND MCP error code |
109
+ | Async | Always `await` async operations; use `expect(...).rejects.toThrow()` for async errors |
110
110
 
111
111
  ## Common Patterns
112
112
 
@@ -146,17 +146,18 @@ 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 | 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
- | 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 |
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 |
160
161
 
161
162
  ## Examples
162
163
 
@@ -396,6 +396,27 @@ You usually do not need to set this: when the client's first request 404s and th
396
396
 
397
397
  `baseUrl` may also be supplied alongside `server` to override the booted server's own URL (for a proxy, or a different host than it binds).
398
398
 
399
+ ### Scoping config, parameterised tests, ports and restarts
400
+
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
+ - `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
+ - `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
+ - `test.each` / `test.describe.each` pass the row values to the callback (fixtures first for `test.each`):
405
+
406
+ ```typescript
407
+ test.each([
408
+ ['read', 1],
409
+ ['write', 2],
410
+ ])('scope %s', async ({ mcp }, scope, level) => {
411
+ expect(mcp.isConnected()).toBe(true);
412
+ });
413
+ ```
414
+
415
+ - `server.info.pid` is the pid of the process that listens on the port. `server.restart()` reconnects `mcp` and every client made with `server.createClient()`.
416
+ - `mcp.logs` holds the log notifications the client received; `server.getLogs()` holds the server process output. They are different sources.
417
+ - `mcp.intercept.failMethod(method, message)` makes matching calls reject with `message`; the returned function removes it.
418
+ - `httpMock` patches `fetch` in the test process only (see above) and `interceptor.restore()` puts the original `fetch` back.
419
+
399
420
  ### Gating a block on credentials
400
421
 
401
422
  `test.skip(condition, reason)` is the Playwright signature and skips every test registered after it in the enclosing block:
@@ -494,7 +515,10 @@ matches at the first `node_modules/`, and the run fails with
494
515
  `SyntaxError: Unexpected token 'export'`.
495
516
 
496
517
  In standalone projects driven by `frontmcp test`, prefer `test.esmPackages` in
497
- `frontmcp.config.ts` — the injected config already carries the pattern above:
518
+ `frontmcp.config.ts` — the injected config already carries the pattern above,
519
+ and already transpiles `jose`, `@noble/hashes` and `@noble/ciphers` (CodeCall).
520
+ `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):
498
522
 
499
523
  ```typescript
500
524
  // frontmcp.config.ts
@@ -607,7 +631,7 @@ node scripts/fix-unused-imports.mjs feature/my-branch
607
631
  | `toContainTool` matcher not found | Using `expect` from Jest instead of `@frontmcp/testing` | Import `expect` from `@frontmcp/testing` to get MCP-specific matchers |
608
632
  | `McpTestClient.create()` connection refused | Test server not running or wrong `baseUrl` | Ensure `TestServer.start()` completes before creating client; verify port matches |
609
633
  | Istanbul shows 0% coverage for async methods | TypeScript compilation source-map mismatch | Known issue with `ts-jest` and certain async patterns; check `tsconfig.spec.json` source-map settings |
610
- | Auth E2E test returns 401 unexpectedly | Token not set or expired | Call `mcp.setAuthToken(token)` before the tool call; use `auth.createToken()` with valid claims |
634
+ | Auth E2E test returns 401 unexpectedly | Token not set or expired | Call `await mcp.authenticate(token)` before the tool call; use `auth.createToken()` with valid claims |
611
635
 
612
636
  ## Examples
613
637
 
@@ -94,6 +94,14 @@ describe('Authenticated Server', () => {
94
94
  });
95
95
  ```
96
96
 
97
+ ## Token semantics
98
+
99
+ - Default issuer `https://test.frontmcp.local`, default audience `frontmcp-test` (override with `new TestTokenFactory({ issuer, audience })`).
100
+ - `expiresIn` is counted from now and rounded up to whole seconds, so `expiresIn: 1` is valid for at least one second. `createAnonymousToken(expiresIn?)` takes a lifetime too.
101
+ - `await mcp.authenticate(token)` opens a new session for the token and **rejects** if the server refuses it (expired, bad signature); the client then keeps its previous identity. On an unconnected client it only stores the token. 401/403 are never retried.
102
+ - Tools read token scopes through `this.auth` (e.g. `this.auth.scopes`).
103
+ - Access tokens from `MockOAuthServer` carry the granted `scope` claim and `exp = accessTokenTtlSeconds`, also after a refresh.
104
+
97
105
  ## Mock OAuth / OIDC server
98
106
 
99
107
  `MockOAuthServer` serves JWKS, OAuth metadata, authorization, token, and userinfo endpoints. Construct it with a `TestTokenFactory`, call `.start()` to bind a port, and configure your MCP server to point at the returned `info.issuer` / `info.jwksUrl`.
@@ -74,7 +74,7 @@
74
74
  },
75
75
  {
76
76
  "name": "ui-widgets",
77
- "description": "@Tool({ ui }) \u2014 template formats, trusted markup (html / escapeStringResults), servingMode, host-detect resourceMode, CSP, widgetAccessible, MCP Apps spec."
77
+ "description": "@Tool({ ui }) \u2014 template formats, trusted markup (html / escapeStringResults), servingMode, host-detect resourceMode, CSP, ignored options, MCP Apps spec."
78
78
  },
79
79
  {
80
80
  "name": "annotations",
@@ -362,10 +362,10 @@
362
362
  "name": "24-tool-with-ui-csp-and-bridge",
363
363
  "level": "advanced",
364
364
  "description": "Interactive tool widget that fetches from an allow-listed CSP origin and invokes another tool via `window.FrontMcpBridge.callTool` \u2014 the full pattern for live-data widgets that need cross-tool composition.",
365
- "tags": ["ui", "csp", "widgetAccessible", "FrontMcpBridge", "interactive-widget"],
365
+ "tags": ["ui", "csp", "callTool", "FrontMcpBridge", "interactive-widget"],
366
366
  "features": [
367
367
  "Restricting the widget's outbound `fetch` via `ui.csp.connectDomains` (emitted on the resource per #455)",
368
- "Opting the widget into cross-tool calls with `widgetAccessible: true` and using `window.FrontMcpBridge.callTool(name, args)` instead of host-specific APIs",
368
+ "Calling other tools from the widget with `window.FrontMcpBridge.callTool(name, args)` instead of host-specific APIs",
369
369
  "Building the markup with `ctx.helpers.html` and embedding initial data into the inline `<script>` via `trustedHtml(jsonEmbed(...))` (`jsonEmbed` escapes `<`, `>` and `&`)",
370
370
  "Surfacing in-flight status via `invocationStatus.invoking` / `invoked` so the host UI shows feedback"
371
371
  ]
@@ -944,7 +944,7 @@
944
944
  "Using `keyPrefix` to namespace guard keys in a shared Redis instance",
945
945
  "Combining `partitionBy: 'ip'` for global limits with `partitionBy: 'session'` per tool",
946
946
  "In-memory counters are per-process and would allow N times the intended rate with N instances",
947
- "Startup fails closed with `GuardStorageUnavailableError` when Redis is down, unless `fallback: 'memory'`"
947
+ "Startup fails closed, and a limited call is refused, with `GuardStorageUnavailableError` when Redis is down, unless `fallback: 'memory'`"
948
948
  ]
949
949
  },
950
950
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontmcp/skills",
3
- "version": "1.8.6",
3
+ "version": "1.8.7",
4
4
  "description": "Curated skills catalog for FrontMCP projects",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "homepage": "https://docs.agentfront.dev",