@frontmcp/skills 1.1.2 → 1.2.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/catalog/TEMPLATE.md +16 -11
- package/catalog/frontmcp-authorities/SKILL.md +116 -11
- package/catalog/frontmcp-authorities/references/authority-profiles.md +39 -36
- package/catalog/frontmcp-authorities/references/claims-mapping.md +7 -0
- package/catalog/frontmcp-authorities/references/custom-evaluators.md +63 -14
- package/catalog/frontmcp-channels/SKILL.md +36 -0
- package/catalog/frontmcp-channels/examples/channel-sources/file-watcher.md +8 -2
- package/catalog/frontmcp-channels/examples/channel-sources/replay-buffer.md +111 -30
- package/catalog/frontmcp-channels/examples/channel-two-way/whatsapp-bridge.md +45 -3
- package/catalog/frontmcp-channels/references/channel-sources.md +11 -3
- package/catalog/frontmcp-channels/references/channel-two-way.md +60 -89
- package/catalog/frontmcp-config/SKILL.md +111 -8
- package/catalog/frontmcp-config/examples/configure-auth-modes/local-self-signed-tokens.md +4 -4
- package/catalog/frontmcp-config/examples/configure-auth-modes/remote-enterprise-oauth.md +7 -1
- package/catalog/frontmcp-config/examples/configure-deployment-targets/distributed-ha-config.md +1 -1
- package/catalog/frontmcp-config/examples/configure-deployment-targets/json-schema-ide-support.md +1 -1
- package/catalog/frontmcp-config/examples/configure-deployment-targets/multi-target-with-security.md +12 -9
- package/catalog/frontmcp-config/examples/configure-http/cors-restricted-origins.md +2 -2
- package/catalog/frontmcp-config/examples/configure-http/entry-path-reverse-proxy.md +1 -1
- package/catalog/frontmcp-config/examples/configure-security-headers/csp-report-only.md +1 -1
- package/catalog/frontmcp-config/examples/configure-security-headers/full-production-headers.md +1 -1
- package/catalog/frontmcp-config/examples/configure-skills-http/audit-log-basic.md +76 -0
- package/catalog/frontmcp-config/examples/configure-skills-http/audit-log-redis.md +116 -0
- package/catalog/frontmcp-config/examples/configure-skills-http/inject-instructions.md +59 -0
- package/catalog/frontmcp-config/references/configure-auth-modes.md +5 -5
- package/catalog/frontmcp-config/references/configure-deployment-targets.md +27 -24
- package/catalog/frontmcp-config/references/configure-http.md +14 -10
- package/catalog/frontmcp-config/references/configure-security-headers.md +2 -2
- package/catalog/frontmcp-config/references/configure-session.md +25 -25
- package/catalog/frontmcp-config/references/configure-skills-http.md +157 -0
- package/catalog/frontmcp-config/references/configure-throttle.md +1 -1
- package/catalog/frontmcp-config/references/configure-transport.md +2 -2
- package/catalog/frontmcp-deployment/SKILL.md +112 -9
- package/catalog/frontmcp-deployment/examples/build-for-browser/browser-build-with-custom-entry.md +23 -11
- package/catalog/frontmcp-deployment/examples/build-for-browser/browser-crypto-and-storage.md +44 -17
- package/catalog/frontmcp-deployment/examples/build-for-browser/react-provider-setup.md +53 -21
- package/catalog/frontmcp-deployment/examples/build-for-cli/cli-binary-build.md +1 -1
- package/catalog/frontmcp-deployment/examples/build-for-cli/unix-socket-daemon.md +1 -1
- package/catalog/frontmcp-deployment/examples/build-for-mcpb/mcpb-bundle-build.md +1 -1
- package/catalog/frontmcp-deployment/examples/build-for-sdk/connect-openai.md +1 -1
- package/catalog/frontmcp-deployment/examples/build-for-sdk/multi-platform-connect.md +1 -1
- package/catalog/frontmcp-deployment/examples/deploy-to-cloudflare/basic-worker-deploy.md +7 -8
- package/catalog/frontmcp-deployment/examples/deploy-to-cloudflare/worker-custom-domain.md +8 -6
- package/catalog/frontmcp-deployment/examples/deploy-to-cloudflare/worker-with-kv-storage.md +5 -4
- package/catalog/frontmcp-deployment/examples/deploy-to-lambda/cdk-deployment.md +8 -5
- package/catalog/frontmcp-deployment/examples/deploy-to-lambda/lambda-handler-with-cors.md +20 -18
- package/catalog/frontmcp-deployment/examples/deploy-to-lambda/sam-template-basic.md +8 -5
- package/catalog/frontmcp-deployment/examples/deploy-to-node/docker-compose-with-redis.md +3 -3
- package/catalog/frontmcp-deployment/examples/deploy-to-node/pm2-with-nginx.md +1 -1
- package/catalog/frontmcp-deployment/examples/deploy-to-node/resource-limits.md +2 -2
- package/catalog/frontmcp-deployment/examples/deploy-to-node-dockerfile/basic-multistage-dockerfile.md +2 -2
- package/catalog/frontmcp-deployment/examples/deploy-to-node-dockerfile/secure-nonroot-dockerfile.md +1 -1
- package/catalog/frontmcp-deployment/examples/deploy-to-vercel/vercel-mcp-endpoint-test.md +23 -21
- package/catalog/frontmcp-deployment/examples/deploy-to-vercel/vercel-with-kv.md +25 -22
- package/catalog/frontmcp-deployment/examples/deploy-to-vercel/vercel-with-skills-cache.md +23 -30
- package/catalog/frontmcp-deployment/examples/deploy-to-vercel-config/minimal-vercel-config.md +52 -28
- package/catalog/frontmcp-deployment/examples/deploy-to-vercel-config/vercel-config-with-security-headers.md +32 -55
- package/catalog/frontmcp-deployment/examples/mcp-client-integration/http-remote.md +9 -0
- package/catalog/frontmcp-deployment/references/build-for-browser.md +40 -17
- package/catalog/frontmcp-deployment/references/build-for-cli.md +8 -8
- package/catalog/frontmcp-deployment/references/deploy-to-cloudflare.md +43 -24
- package/catalog/frontmcp-deployment/references/deploy-to-lambda.md +36 -25
- package/catalog/frontmcp-deployment/references/deploy-to-node-dockerfile.md +56 -14
- package/catalog/frontmcp-deployment/references/deploy-to-node.md +9 -6
- package/catalog/frontmcp-deployment/references/deploy-to-vercel-config.md +57 -58
- package/catalog/frontmcp-deployment/references/deploy-to-vercel.md +49 -59
- package/catalog/frontmcp-deployment/references/mcp-client-integration.md +2 -0
- package/catalog/frontmcp-development/SKILL.md +186 -11
- package/catalog/frontmcp-development/examples/create-agent/custom-multi-pass-agent.md +1 -1
- package/catalog/frontmcp-development/examples/create-agent/nested-agents-with-swarm.md +30 -27
- package/catalog/frontmcp-development/examples/create-job/job-with-permissions.md +13 -8
- package/catalog/frontmcp-development/examples/create-provider/basic-database-provider.md +33 -23
- package/catalog/frontmcp-development/examples/create-provider/config-and-api-providers.md +19 -10
- package/catalog/frontmcp-development/examples/create-tool/tool-with-rate-limiting-and-progress.md +3 -3
- package/catalog/frontmcp-development/examples/create-workflow/webhook-triggered-workflow.md +6 -4
- package/catalog/frontmcp-development/examples/decorators-guide/agent-skill-job-workflow.md +1 -1
- package/catalog/frontmcp-development/examples/decorators-guide/basic-server-with-app-and-tools.md +13 -8
- package/catalog/frontmcp-development/examples/decorators-guide/multi-app-with-plugins-and-providers.md +50 -23
- package/catalog/frontmcp-development/references/create-agent.md +47 -30
- package/catalog/frontmcp-development/references/create-job.md +69 -54
- package/catalog/frontmcp-development/references/create-plugin-hooks.md +45 -28
- package/catalog/frontmcp-development/references/create-plugin.md +10 -8
- package/catalog/frontmcp-development/references/create-prompt.md +3 -3
- package/catalog/frontmcp-development/references/create-provider.md +91 -51
- package/catalog/frontmcp-development/references/create-resource.md +3 -3
- package/catalog/frontmcp-development/references/create-skill.md +2 -2
- package/catalog/frontmcp-development/references/create-tool.md +7 -7
- package/catalog/frontmcp-development/references/create-workflow.md +8 -10
- package/catalog/frontmcp-development/references/decorators-guide.md +92 -56
- package/catalog/frontmcp-development/references/official-plugins.md +4 -3
- package/catalog/frontmcp-development/references/openapi-adapter.md +1 -1
- package/catalog/frontmcp-extensibility/SKILL.md +70 -10
- package/catalog/frontmcp-extensibility/examples/skill-audit-log/custom-store.md +197 -0
- package/catalog/frontmcp-extensibility/examples/skill-audit-log/verify-chain.md +68 -0
- package/catalog/frontmcp-extensibility/examples/vectoriadb/product-catalog-search.md +3 -5
- package/catalog/frontmcp-extensibility/examples/vectoriadb/semantic-search-with-persistence.md +4 -11
- package/catalog/frontmcp-extensibility/examples/vectoriadb/tfidf-keyword-search.md +41 -30
- package/catalog/frontmcp-extensibility/references/skill-audit-log.md +233 -0
- package/catalog/frontmcp-extensibility/references/vectoriadb.md +73 -63
- package/catalog/frontmcp-guides/SKILL.md +84 -27
- package/catalog/frontmcp-guides/examples/example-knowledge-base/agent-and-plugin.md +72 -62
- package/catalog/frontmcp-guides/examples/example-knowledge-base/vector-search-and-resources.md +32 -43
- package/catalog/frontmcp-guides/examples/example-task-manager/auth-and-crud-tools.md +24 -17
- package/catalog/frontmcp-guides/examples/example-task-manager/authenticated-e2e-tests.md +23 -21
- package/catalog/frontmcp-guides/examples/example-task-manager/redis-provider-with-di.md +47 -39
- package/catalog/frontmcp-guides/examples/example-weather-api/server-and-app-setup.md +16 -6
- package/catalog/frontmcp-guides/examples/example-weather-api/unit-and-e2e-tests.md +9 -8
- package/catalog/frontmcp-guides/references/example-knowledge-base.md +192 -265
- package/catalog/frontmcp-guides/references/example-task-manager.md +60 -54
- package/catalog/frontmcp-guides/references/example-weather-api.md +22 -24
- package/catalog/frontmcp-observability/SKILL.md +66 -2
- package/catalog/frontmcp-observability/examples/telemetry-api/skill-counters.md +100 -0
- package/catalog/frontmcp-observability/examples/tracing-setup/production-tracing.md +7 -2
- package/catalog/frontmcp-observability/examples/vendor-integrations/coralogix-setup.md +6 -2
- package/catalog/frontmcp-observability/references/telemetry-api.md +72 -8
- package/catalog/frontmcp-observability/references/testing-observability.md +33 -49
- package/catalog/frontmcp-observability/references/tracing-setup.md +12 -5
- package/catalog/frontmcp-observability/references/vendor-integrations.md +46 -1
- package/catalog/frontmcp-production-readiness/SKILL.md +134 -3
- package/catalog/frontmcp-production-readiness/examples/common-checklist/caching-and-performance.md +57 -36
- package/catalog/frontmcp-production-readiness/examples/common-checklist/observability-setup.md +1 -1
- package/catalog/frontmcp-production-readiness/examples/common-checklist/security-hardening.md +102 -6
- package/catalog/frontmcp-production-readiness/examples/production-cli-daemon/daemon-socket-config.md +2 -1
- package/catalog/frontmcp-production-readiness/examples/production-cli-daemon/graceful-shutdown-cleanup.md +66 -58
- package/catalog/frontmcp-production-readiness/examples/production-cli-daemon/security-and-permissions.md +5 -3
- package/catalog/frontmcp-production-readiness/examples/production-cloudflare/durable-objects-state.md +2 -1
- package/catalog/frontmcp-production-readiness/examples/production-cloudflare/wrangler-config.md +55 -76
- package/catalog/frontmcp-production-readiness/examples/production-lambda/cold-start-connection-reuse.md +43 -40
- package/catalog/frontmcp-production-readiness/examples/production-lambda/sam-template.md +63 -94
- package/catalog/frontmcp-production-readiness/examples/production-lambda/scaling-and-monitoring.md +28 -18
- package/catalog/frontmcp-production-readiness/examples/production-node-sdk/multi-instance-cleanup.md +29 -14
- package/catalog/frontmcp-production-readiness/examples/production-node-server/graceful-shutdown.md +58 -42
- package/catalog/frontmcp-production-readiness/examples/production-node-server/redis-session-scaling.md +5 -2
- package/catalog/frontmcp-production-readiness/examples/production-vercel/cold-start-optimization.md +41 -24
- package/catalog/frontmcp-production-readiness/examples/production-vercel/vercel-edge-config.md +56 -65
- package/catalog/frontmcp-production-readiness/references/common-checklist.md +17 -5
- package/catalog/frontmcp-production-readiness/references/production-cli-daemon.md +5 -5
- package/catalog/frontmcp-production-readiness/references/production-cloudflare.md +5 -5
- package/catalog/frontmcp-production-readiness/references/production-lambda.md +5 -5
- package/catalog/frontmcp-production-readiness/references/production-node-sdk.md +5 -5
- package/catalog/frontmcp-production-readiness/references/production-node-server.md +1 -1
- package/catalog/frontmcp-production-readiness/references/production-vercel.md +5 -5
- package/catalog/frontmcp-setup/SKILL.md +88 -0
- package/catalog/frontmcp-setup/examples/project-structure-nx/nx-workspace-with-apps.md +10 -4
- package/catalog/frontmcp-setup/examples/project-structure-standalone/dev-workflow-commands.md +21 -8
- package/catalog/frontmcp-setup/examples/readme-guide/node-server-readme.md +3 -3
- package/catalog/frontmcp-setup/references/multi-app-composition.md +4 -3
- package/catalog/frontmcp-setup/references/project-structure-nx.md +15 -6
- package/catalog/frontmcp-setup/references/project-structure-standalone.md +18 -15
- package/catalog/frontmcp-setup/references/readme-guide.md +1 -1
- package/catalog/frontmcp-setup/references/setup-project.md +19 -5
- package/catalog/frontmcp-setup/references/setup-redis.md +27 -39
- package/catalog/frontmcp-setup/references/setup-sqlite.md +25 -18
- package/catalog/frontmcp-testing/SKILL.md +102 -15
- package/catalog/frontmcp-testing/examples/setup-testing/unit-test-tool-resource-prompt.md +3 -3
- package/catalog/frontmcp-testing/examples/test-auth/oauth-flow-test.md +50 -39
- package/catalog/frontmcp-testing/examples/test-auth/role-based-access-test.md +52 -29
- package/catalog/frontmcp-testing/examples/test-auth/token-factory-test.md +37 -20
- package/catalog/frontmcp-testing/examples/test-direct-client/basic-create-test.md +25 -15
- package/catalog/frontmcp-testing/examples/test-direct-client/openai-claude-format-test.md +27 -21
- package/catalog/frontmcp-testing/examples/test-e2e-handler/basic-e2e-test.md +29 -20
- package/catalog/frontmcp-testing/examples/test-e2e-handler/manual-client-with-transport.md +5 -3
- package/catalog/frontmcp-testing/examples/test-e2e-handler/tool-call-and-error-e2e.md +35 -26
- package/catalog/frontmcp-testing/examples/test-tool-unit/basic-tool-test.md +8 -3
- package/catalog/frontmcp-testing/examples/test-tool-unit/schema-validation-test.md +4 -1
- package/catalog/frontmcp-testing/examples/test-tool-unit/tool-error-handling-test.md +6 -3
- package/catalog/frontmcp-testing/references/setup-testing.md +35 -39
- package/catalog/frontmcp-testing/references/test-auth.md +86 -43
- package/catalog/frontmcp-testing/references/test-browser-build.md +1 -1
- package/catalog/frontmcp-testing/references/test-direct-client.md +29 -19
- package/catalog/frontmcp-testing/references/test-e2e-handler.md +31 -19
- package/catalog/frontmcp-testing/references/test-tool-unit.md +6 -2
- package/catalog/skills-manifest.json +428 -339
- package/package.json +1 -1
- package/src/manifest.d.ts +13 -0
- package/src/manifest.js.map +1 -1
package/catalog/TEMPLATE.md
CHANGED
|
@@ -145,17 +145,22 @@ Add a `## Examples` section at the bottom of each reference file (before `## Ref
|
|
|
145
145
|
|
|
146
146
|
## Resource Access
|
|
147
147
|
|
|
148
|
-
Skills are accessible via the `
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
|
153
|
-
|
|
|
154
|
-
| `
|
|
155
|
-
| `
|
|
156
|
-
| `
|
|
157
|
-
| `
|
|
158
|
-
| `
|
|
148
|
+
Skills are accessible via the `skill://` URI scheme as MCP resources per
|
|
149
|
+
[SEP-2640 (Skills Extension)](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640),
|
|
150
|
+
with auto-complete on `skillPath`:
|
|
151
|
+
|
|
152
|
+
| URI | Returns |
|
|
153
|
+
| ------------------------------------------ | ------------------------------------------------------------------------ |
|
|
154
|
+
| `skill://index.json` | SEP-2640 discovery document (agentskills.io schema) |
|
|
155
|
+
| `skill://{skillPath}/SKILL.md` | Raw SKILL.md (YAML frontmatter + markdown body, identical to filesystem) |
|
|
156
|
+
| `skill://{skillPath}/references/{file}.md` | Reference markdown content |
|
|
157
|
+
| `skill://{skillPath}/examples/{file}.md` | Example markdown content |
|
|
158
|
+
| `skill://{skillPath}/scripts/{file}` | Script asset (any media type) |
|
|
159
|
+
| `skill://{skillPath}/assets/{file}` | Bundled asset (any media type) |
|
|
160
|
+
|
|
161
|
+
`{skillPath}` may be a single segment (`git-workflow`) or nested
|
|
162
|
+
(`acme/billing/refunds`). Its final segment must equal the skill's
|
|
163
|
+
frontmatter `name`.
|
|
159
164
|
|
|
160
165
|
## Reference
|
|
161
166
|
|
|
@@ -106,7 +106,12 @@ authorities: {
|
|
|
106
106
|
}
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
-
If no `claimsMapping` is provided, the engine falls back to
|
|
109
|
+
If no `claimsMapping` is provided, the engine falls back to (in order):
|
|
110
|
+
|
|
111
|
+
- **roles**: `authInfo.user.roles` → `authInfo.extra.authorization.scopes` → `[]`
|
|
112
|
+
- **permissions**: `authInfo.user.permissions` → `[]`
|
|
113
|
+
|
|
114
|
+
The OAuth-scopes-as-roles fallback means a token with no `roles` claim but with `scope: "admin read"` will be treated as having `roles: ['admin', 'read']`. Configure explicit `claimsMapping.roles` to opt out. For non-standard token shapes, use `claimsResolver` instead (see Common Patterns below).
|
|
110
115
|
|
|
111
116
|
### Step 3: Register Named Profiles
|
|
112
117
|
|
|
@@ -198,6 +203,87 @@ export default class PublishContentTool extends ToolContext { ... }
|
|
|
198
203
|
export default class SensitiveActionTool extends ToolContext { ... }
|
|
199
204
|
```
|
|
200
205
|
|
|
206
|
+
**Async guards (one-shot DB/Redis/API checks):**
|
|
207
|
+
|
|
208
|
+
For dynamic, async authorization that does not warrant a reusable custom evaluator, use the
|
|
209
|
+
`guards` field. Each guard receives the same `AuthoritiesEvaluationContext` and returns
|
|
210
|
+
`true` on grant, or `false`/a denial string on deny. Guards run in sequence and combine with
|
|
211
|
+
other policy fields via `operator` (default AND).
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
import type { AuthorityGuardFn } from '@frontmcp/auth';
|
|
215
|
+
|
|
216
|
+
const requireActiveSubscription: AuthorityGuardFn = async (ctx) => {
|
|
217
|
+
const active = await db.isSubscriptionActive(ctx.user.sub);
|
|
218
|
+
return active ? true : 'subscription is not active';
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
@Tool({
|
|
222
|
+
name: 'premium_feature',
|
|
223
|
+
authorities: {
|
|
224
|
+
roles: { any: ['user'] },
|
|
225
|
+
guards: [requireActiveSubscription],
|
|
226
|
+
},
|
|
227
|
+
})
|
|
228
|
+
export default class PremiumFeatureTool extends ToolContext { ... }
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Use `guards` for one-off async checks; promote to a registered custom evaluator when the
|
|
232
|
+
same logic is reused across many entries (see `references/custom-evaluators.md`).
|
|
233
|
+
|
|
234
|
+
### Optional: Map Denials to OAuth Scope Challenges (`scopeMapping`)
|
|
235
|
+
|
|
236
|
+
If your transport layer issues OAuth scope challenges (RFC 6750 `insufficient_scope`),
|
|
237
|
+
declare a `scopeMapping` so authority denials are converted into the right `WWW-Authenticate`
|
|
238
|
+
challenge with the required scopes. Mapping is explicit only — no automatic
|
|
239
|
+
permission-to-scope inference.
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
authorities: {
|
|
243
|
+
scopeMapping: {
|
|
244
|
+
roles: { admin: ['admin:all'] },
|
|
245
|
+
permissions: { 'repo:write': ['repo'] },
|
|
246
|
+
profiles: { admin: ['admin:all'] },
|
|
247
|
+
},
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
When a request is denied because the user lacks role `admin`, the response surfaces
|
|
252
|
+
the `admin:all` scope as the required challenge.
|
|
253
|
+
|
|
254
|
+
### Optional: Extract Custom Auth Context Fields (`pipes`)
|
|
255
|
+
|
|
256
|
+
`pipes` are functions that run during auth context construction and merge their output into
|
|
257
|
+
`FrontMcpAuthContext`. Use them to extract custom typed fields from JWT claims so they are
|
|
258
|
+
available to your tools as strongly typed accessors. Declare the resulting fields by
|
|
259
|
+
augmenting `ExtendFrontMcpAuthContext`:
|
|
260
|
+
|
|
261
|
+
A pipe receives the **raw JWT claims** (a `Readonly<Record<string, unknown>>`) and
|
|
262
|
+
returns a `Partial<ExtendFrontMcpAuthContext>` (sync or async). It does **not**
|
|
263
|
+
receive the `AuthInfo` envelope — there is no `.user` accessor on the input.
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
// Pipe signature (defined inline; do not import — AuthContextPipe is not
|
|
267
|
+
// re-exported from `@frontmcp/auth`):
|
|
268
|
+
// (claims: Readonly<Record<string, unknown>>) =>
|
|
269
|
+
// Partial<ExtendFrontMcpAuthContext> | Promise<Partial<ExtendFrontMcpAuthContext>>
|
|
270
|
+
|
|
271
|
+
const tenantPipe = (claims: Readonly<Record<string, unknown>>) => ({
|
|
272
|
+
tenantId: claims['tenantId'] as string | undefined,
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
declare global {
|
|
276
|
+
interface ExtendFrontMcpAuthContext {
|
|
277
|
+
tenantId?: string;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// In @FrontMcp({ authorities: { ... } })
|
|
282
|
+
authorities: {
|
|
283
|
+
pipes: [tenantPipe],
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
201
287
|
## Scenario Routing Table
|
|
202
288
|
|
|
203
289
|
| Scenario | Approach | Reference |
|
|
@@ -253,16 +339,35 @@ export default class SensitiveActionTool extends ToolContext { ... }
|
|
|
253
339
|
|
|
254
340
|
## Troubleshooting
|
|
255
341
|
|
|
256
|
-
| Problem
|
|
257
|
-
|
|
|
258
|
-
| `profile 'admin' is not registered`
|
|
259
|
-
| All users denied despite correct roles
|
|
260
|
-
| `authorities` field not recognized on decorator
|
|
261
|
-
| ABAC condition always fails
|
|
262
|
-
| ReBAC always denies
|
|
263
|
-
| Custom evaluator not found
|
|
264
|
-
| List endpoints show all entries
|
|
265
|
-
| `AuthorityDeniedError` has no detail
|
|
342
|
+
| Problem | Cause | Solution |
|
|
343
|
+
| ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
344
|
+
| `profile 'admin' is not registered` | Profile used in decorator but not in `authorities.profiles` config | Add the profile to the `profiles` field in `@FrontMcp({ authorities })` |
|
|
345
|
+
| All users denied despite correct roles | `claimsMapping.roles` path does not match the actual JWT claim path | Decode a real JWT and verify the dot-path resolves to the roles array |
|
|
346
|
+
| `authorities` field not recognized on decorator | `@frontmcp/auth` not imported (metadata augmentation not active) | Add `import '@frontmcp/auth'` (or `import type ... from '@frontmcp/auth'`) anywhere in your project to activate the metadata augmentation. There is no `@frontmcp/auth/authorities` subpath. |
|
|
347
|
+
| ABAC condition always fails | `{ fromInput: 'tenantId' }` but tool input field is named `tenant_id` | The `fromInput` key must exactly match the tool's input schema field name |
|
|
348
|
+
| ReBAC always denies | No `relationshipResolver` provided | Implement `RelationshipResolver` and pass it to plugin options |
|
|
349
|
+
| Custom evaluator not found | Key in `custom.*` policy does not match registered evaluator name | Ensure the evaluator is registered with the same key used in the policy |
|
|
350
|
+
| List endpoints show all entries | No `authorities` config in `@FrontMcp()` or hook priority conflict | Verify `authorities: { ... }` is set on `@FrontMcp()` decorator |
|
|
351
|
+
| `AuthorityDeniedError` has no detail | `deniedBy` field shows generic message | Check the `evaluatedPolicies` array on the error for which policy type failed |
|
|
352
|
+
| TS error: `evaluators`/`relationshipResolver`/`claimsResolver` not on `AuthoritiesConfig` | The runtime Zod schema accepts these keys but the exported `AuthoritiesConfig` interface in `authorities.profiles.ts` only declares `claimsMapping`, `profiles`, `scopeMapping`, `pipes`. | Pass the config inline (TS infers from the decorator's broader type) or cast the typed config as the interface catches up. |
|
|
353
|
+
|
|
354
|
+
## Examples
|
|
355
|
+
|
|
356
|
+
This skill currently exposes only references; see [`references/`](./references/) for guidance.
|
|
357
|
+
|
|
358
|
+
## Accessing This Skill
|
|
359
|
+
|
|
360
|
+
Skills are distributed as plain SKILL.md files plus a sibling `references/`
|
|
361
|
+
and `examples/` tree, so consumers can pick whichever access mode fits:
|
|
362
|
+
|
|
363
|
+
| Mode | How it works |
|
|
364
|
+
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
365
|
+
| **Filesystem** | Read `libs/skills/catalog/frontmcp-authorities/` directly from a clone of the catalog repo, or from a published `@frontmcp/skills` install. SKILL.md is the entry point. |
|
|
366
|
+
| **`frontmcp` CLI** | `frontmcp skills list`, `frontmcp skills read frontmcp-authorities`, `frontmcp skills read frontmcp-authorities:references/<file>.md`, `frontmcp skills install frontmcp-authorities` — no server required. |
|
|
367
|
+
| **MCP `skill://`** | When a developer mounts this skill into their own FrontMCP server (`@FrontMcp({ skills: [...] })`), the SDK exposes it via SEP-2640 resources: `skill://frontmcp-authorities/SKILL.md`, `skill://frontmcp-authorities/references/{file}.md`, etc. The server’s `skill://index.json` returns the SEP-2640 discovery document for everything mounted on it. |
|
|
368
|
+
|
|
369
|
+
The catalog itself is **not** an MCP server. The `skill://` URIs only resolve
|
|
370
|
+
when a server has been configured to host this skill.
|
|
266
371
|
|
|
267
372
|
## Reference
|
|
268
373
|
|
|
@@ -17,40 +17,40 @@ import { FrontMcp } from '@frontmcp/sdk';
|
|
|
17
17
|
@FrontMcp({
|
|
18
18
|
name: 'my-server',
|
|
19
19
|
authorities: {
|
|
20
|
-
claimsMapping: {
|
|
20
|
+
claimsMapping: {
|
|
21
|
+
roles: 'realm_access.roles',
|
|
22
|
+
permissions: 'resource_access.my-client.roles',
|
|
23
|
+
},
|
|
21
24
|
profiles: {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
// RBAC: user must have at least one of these roles
|
|
26
|
+
admin: {
|
|
27
|
+
roles: { any: ['admin', 'superadmin'] },
|
|
28
|
+
},
|
|
26
29
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
},
|
|
30
|
+
// ABAC: user must be authenticated (sub exists)
|
|
31
|
+
authenticated: {
|
|
32
|
+
attributes: {
|
|
33
|
+
conditions: [{ path: 'user.sub', op: 'exists', value: true }],
|
|
32
34
|
},
|
|
35
|
+
},
|
|
33
36
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
{ path: 'claims.org_id', op: 'eq', value: { fromInput: 'tenantId' } },
|
|
39
|
-
],
|
|
40
|
-
},
|
|
37
|
+
// ABAC: tenant from JWT must match the tenantId input argument
|
|
38
|
+
matchTenant: {
|
|
39
|
+
attributes: {
|
|
40
|
+
conditions: [{ path: 'claims.org_id', op: 'eq', value: { fromInput: 'tenantId' } }],
|
|
41
41
|
},
|
|
42
|
+
},
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
// RBAC: permission-based
|
|
45
|
+
canPublish: {
|
|
46
|
+
permissions: { all: ['content:publish'] },
|
|
47
|
+
},
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
},
|
|
49
|
+
// Combined: role AND permission
|
|
50
|
+
editorWithPublish: {
|
|
51
|
+
roles: { any: ['editor'] },
|
|
52
|
+
permissions: { all: ['content:publish'] },
|
|
53
|
+
// operator defaults to 'AND', so both must pass
|
|
54
54
|
},
|
|
55
55
|
},
|
|
56
56
|
},
|
|
@@ -78,8 +78,11 @@ export default class DeleteUserTool extends ToolContext {
|
|
|
78
78
|
authorities: 'admin',
|
|
79
79
|
})
|
|
80
80
|
export default class InternalMetricsResource extends ResourceContext {
|
|
81
|
-
async
|
|
82
|
-
// only admin can
|
|
81
|
+
async execute(uri: string, _params: Record<string, string>) {
|
|
82
|
+
// only admin can reach here
|
|
83
|
+
return {
|
|
84
|
+
contents: [{ uri, mimeType: 'application/json', text: JSON.stringify({ ok: true }) }],
|
|
85
|
+
};
|
|
83
86
|
}
|
|
84
87
|
}
|
|
85
88
|
|
|
@@ -246,13 +249,13 @@ This filtering happens automatically. No additional configuration is needed. Ent
|
|
|
246
249
|
|
|
247
250
|
## Profile Design Guidelines
|
|
248
251
|
|
|
249
|
-
| Guideline
|
|
250
|
-
|
|
|
251
|
-
| Name profiles after the role or intent, not the technical check | `admin` not `hasAdminRole`
|
|
252
|
-
| Keep profiles single-purpose
|
|
253
|
-
| Use `allOf`/`anyOf` for complex compositions in inline policies | Do not create profiles that duplicate combinator logic
|
|
254
|
-
| Document your profiles in a central file
|
|
255
|
-
| Test profiles with both authorized and unauthorized users
|
|
252
|
+
| Guideline | Example |
|
|
253
|
+
| --------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
|
|
254
|
+
| Name profiles after the role or intent, not the technical check | `admin` not `hasAdminRole` |
|
|
255
|
+
| Keep profiles single-purpose | `matchTenant` does tenant check only; combine with `['authenticated', 'matchTenant']` |
|
|
256
|
+
| Use `allOf`/`anyOf` for complex compositions in inline policies | Do not create profiles that duplicate combinator logic |
|
|
257
|
+
| Document your profiles in a central file | `types/authorities.d.ts` with augmentation and comments |
|
|
258
|
+
| Test profiles with both authorized and unauthorized users | See `frontmcp-testing` skill for auth testing patterns |
|
|
256
259
|
|
|
257
260
|
## Reference
|
|
258
261
|
|
|
@@ -234,6 +234,13 @@ authorities: {
|
|
|
234
234
|
return {
|
|
235
235
|
roles,
|
|
236
236
|
permissions,
|
|
237
|
+
// Merge order matches the engine's default in
|
|
238
|
+
// `authorities.context.ts` (`{ ...authorizationClaims, ...user }`):
|
|
239
|
+
// spread the raw `authorization.claims` first, then `authInfo.user` so
|
|
240
|
+
// IdP-provided user fields (`sub`, `name`, `email`, `tenantId`, ...)
|
|
241
|
+
// override any same-named server-side authorization claim on conflict.
|
|
242
|
+
// Reverse this order only if you intentionally want authorization
|
|
243
|
+
// claims to win over user identity fields.
|
|
237
244
|
claims: { ...claims, ...user },
|
|
238
245
|
};
|
|
239
246
|
},
|
|
@@ -12,7 +12,7 @@ When the built-in RBAC, ABAC, and ReBAC models do not cover your authorization r
|
|
|
12
12
|
Every custom evaluator must implement the `AuthoritiesEvaluator` interface from `@frontmcp/auth`.
|
|
13
13
|
|
|
14
14
|
```typescript
|
|
15
|
-
import type {
|
|
15
|
+
import type { AuthoritiesEvaluationContext, AuthoritiesEvaluator, AuthoritiesResult } from '@frontmcp/auth';
|
|
16
16
|
|
|
17
17
|
interface AuthoritiesEvaluator {
|
|
18
18
|
/** Evaluator name (must match the key under `custom.*` in policies) */
|
|
@@ -30,13 +30,28 @@ The return value must be an `AuthoritiesResult`:
|
|
|
30
30
|
interface AuthoritiesResult {
|
|
31
31
|
/** Whether access was granted */
|
|
32
32
|
granted: boolean;
|
|
33
|
-
/** Human-readable reason for denial */
|
|
33
|
+
/** Human-readable reason for denial (kept for backward compatibility) */
|
|
34
34
|
deniedBy?: string;
|
|
35
|
+
/** Structured denial data (machine-parsable) */
|
|
36
|
+
denial?: AuthoritiesDenial;
|
|
35
37
|
/** List of policy types that were evaluated (for audit) */
|
|
36
38
|
evaluatedPolicies: string[];
|
|
37
39
|
/** Optional detailed message */
|
|
38
40
|
message?: string;
|
|
39
41
|
}
|
|
42
|
+
|
|
43
|
+
interface AuthoritiesDenial {
|
|
44
|
+
/** Which policy type caused the denial */
|
|
45
|
+
kind: 'roles' | 'permissions' | 'attributes' | 'relationships' | 'custom' | 'profile' | 'not' | 'allOf' | 'anyOf';
|
|
46
|
+
/** Dot-path into the policy that failed (e.g., "custom.ipAllowList") */
|
|
47
|
+
path: string;
|
|
48
|
+
/** Values that were required but missing */
|
|
49
|
+
missing?: string[];
|
|
50
|
+
/** Expected value */
|
|
51
|
+
expected?: unknown;
|
|
52
|
+
/** Actual value found */
|
|
53
|
+
actual?: unknown;
|
|
54
|
+
}
|
|
40
55
|
```
|
|
41
56
|
|
|
42
57
|
## Registering Custom Evaluators
|
|
@@ -45,8 +60,9 @@ Custom evaluators are registered in the `evaluators` field of the `authorities`
|
|
|
45
60
|
|
|
46
61
|
```typescript
|
|
47
62
|
import { FrontMcp } from '@frontmcp/sdk';
|
|
48
|
-
|
|
63
|
+
|
|
49
64
|
import { featureFlagEvaluator } from './evaluators/feature-flag';
|
|
65
|
+
import { ipAllowListEvaluator } from './evaluators/ip-allow-list';
|
|
50
66
|
import { timeWindowEvaluator } from './evaluators/time-window';
|
|
51
67
|
|
|
52
68
|
@FrontMcp({
|
|
@@ -123,13 +139,41 @@ const featureFlagGuard: AuthoritiesEvaluator = {
|
|
|
123
139
|
};
|
|
124
140
|
```
|
|
125
141
|
|
|
126
|
-
### When to Use Custom Evaluators vs Hooks
|
|
142
|
+
### When to Use Custom Evaluators vs Guards vs Hooks
|
|
143
|
+
|
|
144
|
+
| Approach | When to Use |
|
|
145
|
+
| ---------------------------------------- | ------------------------------------------------------------------------------------------------- |
|
|
146
|
+
| **Custom evaluator** | Reusable async check across many tools — register once, reference via `custom.<name>` in policies |
|
|
147
|
+
| **`guards: AuthorityGuardFn[]`** | One-off async check on a single entry — declare inline, no registration required |
|
|
148
|
+
| **`Will('checkEntryAuthorities')` hook** | Cross-cutting pre/post logic across the whole flow stage (audit, logging, replacement) |
|
|
149
|
+
| **Static `authorities` policy** | Roles, permissions, attributes — no I/O needed |
|
|
150
|
+
|
|
151
|
+
#### Inline `guards` example
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
import type { AuthorityGuardFn } from '@frontmcp/auth';
|
|
155
|
+
|
|
156
|
+
const tenantInAllowlist: AuthorityGuardFn = async (ctx) => {
|
|
157
|
+
const tenantId = ctx.input['tenantId'] as string | undefined;
|
|
158
|
+
if (!tenantId) return 'tenantId missing from input';
|
|
159
|
+
const ok = await redis.sismember('allowed-tenants', tenantId);
|
|
160
|
+
return ok ? true : `tenant '${tenantId}' is not allowlisted`;
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
@Tool({
|
|
164
|
+
name: 'tenant_action',
|
|
165
|
+
authorities: {
|
|
166
|
+
roles: { any: ['user'] },
|
|
167
|
+
guards: [tenantInAllowlist],
|
|
168
|
+
},
|
|
169
|
+
})
|
|
170
|
+
export default class TenantActionTool extends ToolContext { ... }
|
|
171
|
+
```
|
|
127
172
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
| **Static `authorities` policy** | Roles, permissions, attributes — no I/O needed |
|
|
173
|
+
Guards combine with sibling policy fields via `operator` (default AND). They are the
|
|
174
|
+
simplest async authorization mechanism — no registry, no `name` field, no `custom.*`
|
|
175
|
+
indirection — and are preferred over hooks for "this single entry needs an extra
|
|
176
|
+
async check" scenarios.
|
|
133
177
|
|
|
134
178
|
## Using Custom Evaluators in Policies
|
|
135
179
|
|
|
@@ -156,7 +200,7 @@ Restricts access to requests from specific CIDR ranges.
|
|
|
156
200
|
|
|
157
201
|
```typescript
|
|
158
202
|
// evaluators/ip-allow-list.ts
|
|
159
|
-
import type {
|
|
203
|
+
import type { AuthoritiesEvaluationContext, AuthoritiesEvaluator, AuthoritiesResult } from '@frontmcp/auth';
|
|
160
204
|
|
|
161
205
|
interface IpAllowListPolicy {
|
|
162
206
|
cidr: string[];
|
|
@@ -174,12 +218,17 @@ export const ipAllowListEvaluator: AuthoritiesEvaluator = {
|
|
|
174
218
|
name: 'ipAllowList',
|
|
175
219
|
async evaluate(policy: unknown, ctx: AuthoritiesEvaluationContext): Promise<AuthoritiesResult> {
|
|
176
220
|
const { cidr } = policy as IpAllowListPolicy;
|
|
221
|
+
// The SDK does not populate ctx.env by default — the engine's `env` defaults to `{}`.
|
|
222
|
+
// To use this evaluator, populate `env.remoteIp` yourself: register an
|
|
223
|
+
// `Around('checkEntryAuthorities')` hook (or transport adapter middleware) that reads
|
|
224
|
+
// the request's remote IP (e.g., from `X-Forwarded-For`) and merges it into the
|
|
225
|
+
// evaluation context env before the engine runs.
|
|
177
226
|
const remoteIp = ctx.env['remoteIp'] as string | undefined;
|
|
178
227
|
|
|
179
228
|
if (!remoteIp) {
|
|
180
229
|
return {
|
|
181
230
|
granted: false,
|
|
182
|
-
deniedBy: 'custom.ipAllowList: remoteIp not
|
|
231
|
+
deniedBy: 'custom.ipAllowList: remoteIp not populated in env (configure a hook/middleware to set it)',
|
|
183
232
|
evaluatedPolicies: ['custom.ipAllowList'],
|
|
184
233
|
};
|
|
185
234
|
}
|
|
@@ -215,7 +264,7 @@ Gates access behind a feature flag service.
|
|
|
215
264
|
|
|
216
265
|
```typescript
|
|
217
266
|
// evaluators/feature-flag.ts
|
|
218
|
-
import type {
|
|
267
|
+
import type { AuthoritiesEvaluationContext, AuthoritiesEvaluator, AuthoritiesResult } from '@frontmcp/auth';
|
|
219
268
|
|
|
220
269
|
interface FeatureFlagPolicy {
|
|
221
270
|
flag: string;
|
|
@@ -264,7 +313,7 @@ Restricts access to specific time windows (e.g., business hours only).
|
|
|
264
313
|
|
|
265
314
|
```typescript
|
|
266
315
|
// evaluators/time-window.ts
|
|
267
|
-
import type {
|
|
316
|
+
import type { AuthoritiesEvaluationContext, AuthoritiesEvaluator, AuthoritiesResult } from '@frontmcp/auth';
|
|
268
317
|
|
|
269
318
|
interface TimeWindowPolicy {
|
|
270
319
|
/** Allowed days (0=Sunday, 6=Saturday) */
|
|
@@ -334,7 +383,7 @@ Denies access when a per-user rate limit is exceeded.
|
|
|
334
383
|
|
|
335
384
|
```typescript
|
|
336
385
|
// evaluators/rate-limit.ts
|
|
337
|
-
import type {
|
|
386
|
+
import type { AuthoritiesEvaluationContext, AuthoritiesEvaluator, AuthoritiesResult } from '@frontmcp/auth';
|
|
338
387
|
|
|
339
388
|
interface RateLimitPolicy {
|
|
340
389
|
/** Maximum calls allowed */
|
|
@@ -115,6 +115,42 @@ Build push-based notification channels that stream real-time events into Claude
|
|
|
115
115
|
| Duplicate notifications | Multiple sessions subscribed | This is correct behavior -- each session gets its own copy |
|
|
116
116
|
| Events lost on reconnect | Channels are in-memory | Channel state resets on server restart; use persistent sources |
|
|
117
117
|
|
|
118
|
+
## Examples
|
|
119
|
+
|
|
120
|
+
Each reference has matching examples under [`examples/<reference>/`](./examples/):
|
|
121
|
+
|
|
122
|
+
### `channel-sources`
|
|
123
|
+
|
|
124
|
+
| Example | Level | Description |
|
|
125
|
+
| ---------------------------------------------------------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
126
|
+
| [`webhook-github`](./examples/channel-sources/webhook-github.md) | Basic | Forward GitHub webhook events (PRs, pushes, CI) into Claude Code |
|
|
127
|
+
| [`app-errors`](./examples/channel-sources/app-errors.md) | Basic | Forward application errors to Claude Code via the in-process event bus |
|
|
128
|
+
| [`agent-notify`](./examples/channel-sources/agent-notify.md) | Intermediate | Notify Claude Code when AI agents complete their tasks |
|
|
129
|
+
| [`job-completion`](./examples/channel-sources/job-completion.md) | Intermediate | Notify Claude Code when background jobs and workflows complete |
|
|
130
|
+
| [`service-connector`](./examples/channel-sources/service-connector.md) | Advanced | Build a persistent service connector that lets Claude send and receive messages through WhatsApp, Telegram, or any messaging API |
|
|
131
|
+
| [`file-watcher`](./examples/channel-sources/file-watcher.md) | Intermediate | Watch files for changes and notify Claude Code in real-time |
|
|
132
|
+
| [`replay-buffer`](./examples/channel-sources/replay-buffer.md) | Advanced | Buffer channel events so Claude Code receives them when it connects, even if events occurred while offline |
|
|
133
|
+
|
|
134
|
+
### `channel-two-way`
|
|
135
|
+
|
|
136
|
+
| Example | Level | Description |
|
|
137
|
+
| ------------------------------------------------------------------ | -------- | -------------------------------------------------------------------------------------- |
|
|
138
|
+
| [`whatsapp-bridge`](./examples/channel-two-way/whatsapp-bridge.md) | Advanced | Full WhatsApp Business API bridge allowing users to chat with Claude Code via WhatsApp |
|
|
139
|
+
|
|
140
|
+
## Accessing This Skill
|
|
141
|
+
|
|
142
|
+
Skills are distributed as plain SKILL.md files plus a sibling `references/`
|
|
143
|
+
and `examples/` tree, so consumers can pick whichever access mode fits:
|
|
144
|
+
|
|
145
|
+
| Mode | How it works |
|
|
146
|
+
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
147
|
+
| **Filesystem** | Read `libs/skills/catalog/frontmcp-channels/` directly from a clone of the catalog repo, or from a published `@frontmcp/skills` install. SKILL.md is the entry point. |
|
|
148
|
+
| **`frontmcp` CLI** | `frontmcp skills list`, `frontmcp skills read frontmcp-channels`, `frontmcp skills read frontmcp-channels:references/<file>.md`, `frontmcp skills install frontmcp-channels` — no server required. |
|
|
149
|
+
| **MCP `skill://`** | When a developer mounts this skill into their own FrontMCP server (`@FrontMcp({ skills: [...] })`), the SDK exposes it via SEP-2640 resources: `skill://frontmcp-channels/SKILL.md`, `skill://frontmcp-channels/references/{file}.md`, etc. The server’s `skill://index.json` returns the SEP-2640 discovery document for everything mounted on it. |
|
|
150
|
+
|
|
151
|
+
The catalog itself is **not** an MCP server. The `skill://` URIs only resolve
|
|
152
|
+
when a server has been configured to host this skill.
|
|
153
|
+
|
|
118
154
|
## Reference
|
|
119
155
|
|
|
120
156
|
- [Claude Code Channels Reference](https://code.claude.com/docs/en/channels-reference)
|
|
@@ -19,9 +19,15 @@ Watch files for changes and notify Claude Code in real-time
|
|
|
19
19
|
|
|
20
20
|
```typescript
|
|
21
21
|
// src/apps/monitoring/channels/log-watcher.channel.ts
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
|
|
23
|
+
// NOTE: We normally read/write files via `@frontmcp/utils`, but it does not
|
|
24
|
+
// expose a file-watcher helper because watching is platform-specific (inotify
|
|
25
|
+
// on Linux, FSEvents on macOS, ReadDirectoryChangesW on Windows) and not
|
|
26
|
+
// available in browser/edge runtimes. `node:fs` is the right call here; this
|
|
27
|
+
// channel is Node-only by design.
|
|
24
28
|
import { watch } from 'node:fs';
|
|
29
|
+
|
|
30
|
+
import { Channel, ChannelContext, type ChannelNotification } from '@frontmcp/sdk';
|
|
25
31
|
import { readFile } from '@frontmcp/utils';
|
|
26
32
|
|
|
27
33
|
@Channel({
|