@frontmcp/skills 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/README.md +38 -29
  2. package/catalog/TEMPLATE.md +26 -0
  3. package/catalog/create-tool/SKILL.md +318 -0
  4. package/catalog/create-tool/examples/01-basic-class-tool.md +112 -0
  5. package/catalog/create-tool/examples/02-basic-function-tool.md +80 -0
  6. package/catalog/create-tool/examples/03-tool-with-zod-shape-output.md +78 -0
  7. package/catalog/create-tool/examples/04-tool-with-zod-schema-output.md +97 -0
  8. package/catalog/create-tool/examples/05-tool-with-primitive-output.md +93 -0
  9. package/catalog/create-tool/examples/06-tool-with-media-output.md +109 -0
  10. package/catalog/create-tool/examples/08-tool-with-provider-injection.md +110 -0
  11. package/catalog/create-tool/examples/09-tool-with-multiple-providers.md +107 -0
  12. package/catalog/create-tool/examples/11-tool-with-fetch.md +94 -0
  13. package/catalog/create-tool/examples/12-tool-with-fetch-and-retries.md +115 -0
  14. package/catalog/create-tool/examples/13-tool-with-single-auth-provider.md +85 -0
  15. package/catalog/create-tool/examples/14-tool-with-multiple-auth-providers.md +105 -0
  16. package/catalog/create-tool/examples/15-tool-with-credential-vault.md +115 -0
  17. package/catalog/create-tool/examples/16-tool-with-rate-limit.md +71 -0
  18. package/catalog/create-tool/examples/17-tool-with-concurrency-and-timeout.md +101 -0
  19. package/catalog/create-tool/examples/18-tool-with-progress-and-notify.md +96 -0
  20. package/catalog/create-tool/examples/19-tool-with-elicitation.md +102 -0
  21. package/catalog/create-tool/examples/20-tool-with-annotations.md +125 -0
  22. package/catalog/create-tool/examples/21-tool-with-availability-constraints.md +107 -0
  23. package/catalog/create-tool/examples/22-tool-with-ui-html-template.md +93 -0
  24. package/catalog/create-tool/examples/23-tool-with-ui-filesource-tsx.md +112 -0
  25. package/catalog/create-tool/examples/24-tool-with-ui-csp-and-bridge.md +127 -0
  26. package/catalog/create-tool/examples/25-tool-handing-off-to-job.md +143 -0
  27. package/catalog/create-tool/examples/26-tool-with-resource-link-output.md +94 -0
  28. package/catalog/create-tool/examples/27-tool-with-examples-metadata.md +90 -0
  29. package/catalog/create-tool/references/annotations.md +96 -0
  30. package/catalog/create-tool/references/auth-providers.md +167 -0
  31. package/catalog/create-tool/references/availability.md +106 -0
  32. package/catalog/create-tool/references/decorator-options.md +95 -0
  33. package/catalog/create-tool/references/derived-types.md +102 -0
  34. package/catalog/create-tool/references/elicitation.md +128 -0
  35. package/catalog/create-tool/references/error-handling.md +128 -0
  36. package/catalog/create-tool/references/execution-context.md +158 -0
  37. package/catalog/create-tool/references/file-layout.md +96 -0
  38. package/catalog/create-tool/references/function-style-builder.md +118 -0
  39. package/catalog/create-tool/references/input-schema.md +141 -0
  40. package/catalog/create-tool/references/output-schema.md +175 -0
  41. package/catalog/create-tool/references/quick-start.md +124 -0
  42. package/catalog/create-tool/references/registration.md +132 -0
  43. package/catalog/create-tool/references/remote-and-esm.md +68 -0
  44. package/catalog/create-tool/references/testing.md +59 -0
  45. package/catalog/create-tool/references/throttling.md +109 -0
  46. package/catalog/create-tool/references/ui-widgets.md +198 -0
  47. package/catalog/create-tool/rules/always-define-output-schema.md +77 -0
  48. package/catalog/create-tool/rules/derive-execute-types.md +57 -0
  49. package/catalog/create-tool/rules/input-schema-is-raw-shape.md +76 -0
  50. package/catalog/create-tool/rules/no-toolcontext-generics.md +50 -0
  51. package/catalog/create-tool/rules/no-try-catch-around-execute.md +79 -0
  52. package/catalog/create-tool/rules/register-in-app.md +76 -0
  53. package/catalog/create-tool/rules/snake-case-tool-names.md +45 -0
  54. package/catalog/create-tool/rules/use-this-fail-for-business-errors.md +75 -0
  55. package/catalog/create-tool/rules/widget-paths-anchor-with-import-meta-url.md +76 -0
  56. package/catalog/create-tool/rules/widget-resource-mode-host-detect.md +61 -0
  57. package/catalog/frontmcp-auth-ui/SKILL.md +146 -0
  58. package/catalog/frontmcp-auth-ui/examples/custom-auth-ui/login-slot.md +97 -0
  59. package/catalog/frontmcp-auth-ui/examples/custom-auth-ui/multi-step-auth-extra.md +133 -0
  60. package/catalog/frontmcp-auth-ui/references/custom-auth-ui.md +162 -0
  61. package/catalog/frontmcp-authorities/SKILL.md +55 -18
  62. package/catalog/frontmcp-authorities/references/authority-profiles.md +25 -1
  63. package/catalog/frontmcp-authorities/references/custom-evaluators.md +1 -1
  64. package/catalog/frontmcp-authorities/references/rbac-abac-rebac.md +9 -0
  65. package/catalog/frontmcp-channels/SKILL.md +7 -1
  66. package/catalog/frontmcp-config/SKILL.md +9 -2
  67. package/catalog/frontmcp-config/examples/configure-auth/local-credential-vault.md +94 -0
  68. package/catalog/frontmcp-config/examples/configure-auth/local-secure-store.md +138 -0
  69. package/catalog/frontmcp-config/examples/configure-auth/remote-oauth-with-vault.md +45 -23
  70. package/catalog/frontmcp-config/examples/configure-auth-modes/local-behind-tunnel.md +73 -0
  71. package/catalog/frontmcp-config/examples/configure-auth-modes/local-consent-enforcement.md +87 -0
  72. package/catalog/frontmcp-config/examples/configure-auth-modes/local-dcr-control.md +67 -0
  73. package/catalog/frontmcp-config/examples/configure-auth-modes/local-minimal.md +62 -0
  74. package/catalog/frontmcp-config/examples/configure-auth-modes/local-multi-provider-orchestration.md +93 -0
  75. package/catalog/frontmcp-config/examples/configure-auth-modes/local-self-signed-tokens.md +18 -20
  76. package/catalog/frontmcp-config/examples/configure-auth-modes/local-single-operator.md +66 -0
  77. package/catalog/frontmcp-config/examples/configure-auth-modes/remote-enterprise-oauth.md +37 -23
  78. package/catalog/frontmcp-config/examples/configure-http/custom-http-routes.md +98 -0
  79. package/catalog/frontmcp-config/examples/configure-skills-http/audit-log-redis.md +17 -9
  80. package/catalog/frontmcp-config/references/configure-auth-modes.md +86 -23
  81. package/catalog/frontmcp-config/references/configure-auth.md +296 -50
  82. package/catalog/frontmcp-config/references/configure-http.md +149 -15
  83. package/catalog/frontmcp-deployment/SKILL.md +15 -13
  84. package/catalog/frontmcp-deployment/references/deploy-manifest-yaml.md +308 -0
  85. package/catalog/frontmcp-deployment/references/deploy-to-cloudflare-skills-only.md +174 -0
  86. package/catalog/frontmcp-deployment/references/mcp-client-integration.md +38 -2
  87. package/catalog/frontmcp-development/SKILL.md +30 -44
  88. package/catalog/frontmcp-development/references/decorators-guide.md +15 -15
  89. package/catalog/frontmcp-extensibility/SKILL.md +1 -1
  90. package/catalog/frontmcp-extensibility/examples/skill-audit-log/verify-chain.md +8 -6
  91. package/catalog/frontmcp-extensibility/references/skill-audit-log.md +7 -2
  92. package/catalog/frontmcp-guides/SKILL.md +1 -1
  93. package/catalog/frontmcp-observability/SKILL.md +1 -1
  94. package/catalog/frontmcp-production-readiness/SKILL.md +1 -1
  95. package/catalog/frontmcp-production-readiness/examples/common-checklist/security-hardening.md +3 -2
  96. package/catalog/frontmcp-setup/SKILL.md +1 -1
  97. package/catalog/frontmcp-setup/examples/multi-app-composition/per-app-auth-and-isolation.md +7 -4
  98. package/catalog/frontmcp-setup/references/multi-app-composition.md +6 -5
  99. package/catalog/frontmcp-testing/SKILL.md +9 -1
  100. package/catalog/frontmcp-testing/references/test-auth.md +24 -0
  101. package/catalog/skills-manifest.json +653 -149
  102. package/package.json +1 -1
  103. package/src/manifest.d.ts +72 -1
  104. package/src/manifest.js +4 -1
  105. package/src/manifest.js.map +1 -1
  106. package/catalog/frontmcp-development/examples/create-tool/basic-class-tool.md +0 -80
  107. package/catalog/frontmcp-development/examples/create-tool/tool-with-di-and-errors.md +0 -132
  108. package/catalog/frontmcp-development/examples/create-tool/tool-with-rate-limiting-and-progress.md +0 -110
  109. package/catalog/frontmcp-development/examples/create-tool-annotations/destructive-delete-tool.md +0 -92
  110. package/catalog/frontmcp-development/examples/create-tool-annotations/readonly-query-tool.md +0 -59
  111. package/catalog/frontmcp-development/examples/create-tool-output-schema-types/primitive-and-media-outputs.md +0 -101
  112. package/catalog/frontmcp-development/examples/create-tool-output-schema-types/zod-raw-shape-output.md +0 -62
  113. package/catalog/frontmcp-development/examples/create-tool-output-schema-types/zod-schema-advanced-output.md +0 -101
  114. package/catalog/frontmcp-development/references/create-tool-annotations.md +0 -48
  115. package/catalog/frontmcp-development/references/create-tool-output-schema-types.md +0 -71
  116. package/catalog/frontmcp-development/references/create-tool.md +0 -806
@@ -2,17 +2,26 @@
2
2
  name: remote-oauth-with-vault
3
3
  reference: configure-auth
4
4
  level: intermediate
5
- description: 'Configure a FrontMCP server with remote OAuth 2.1 authentication and use the credential vault to call downstream APIs on behalf of the authenticated user.'
6
- tags: [config, oauth, auth, remote, vault]
5
+ description: 'Configure a FrontMCP server with local OAuth orchestration of an upstream provider and read the downstream provider token in a tool via this.orchestration.getToken.'
6
+ tags: [config, oauth, auth, orchestration, providers]
7
7
  features:
8
- - "Configuring `mode: 'remote'` for full OAuth 2.1 authorization flow"
9
- - 'Loading `clientId` from environment variables instead of hardcoding'
10
- - "Using `this.authProviders.headers('github')` to get pre-formatted auth headers for downstream API calls"
8
+ - 'Declaring an upstream provider in top-level `auth.providers` so FrontMCP orchestrates its OAuth flow'
9
+ - 'Loading `clientId`/`clientSecret` from environment variables instead of hardcoding'
10
+ - "Reading the downstream provider token in a tool via `this.orchestration.getToken('github')`"
11
11
  ---
12
12
 
13
- # Remote OAuth Mode with Credential Vault
13
+ # Local OAuth Orchestration with a Downstream Provider Token
14
14
 
15
- Configure a FrontMCP server with remote OAuth 2.1 authentication and use the credential vault to call downstream APIs on behalf of the authenticated user.
15
+ Configure a FrontMCP server with local OAuth orchestration of an upstream provider and read the downstream provider token in a tool via this.orchestration.getToken.
16
+
17
+ > **Two distinct subsystems — do not conflate them.** `this.orchestration` reads
18
+ > tokens for providers declared in top-level `auth.providers` (the local-mode
19
+ > multi-provider OAuth orchestrator). `this.authProviders` is a _separate_
20
+ > downstream-OAuth-provider accessor whose providers are registered via
21
+ > `@App`/`@Tool({ authProviders: [...] })`. Neither is the per-session credential
22
+ > vault — that is `this.credentials` (see `local-credential-vault`). This example
23
+ > uses the orchestrator, so the provider MUST be declared in `auth.providers` or
24
+ > `getToken('github')` has nothing to return.
16
25
 
17
26
  ## Code
18
27
 
@@ -32,12 +41,14 @@ import { App, FrontMcp, Tool, ToolContext, z } from '@frontmcp/sdk';
32
41
  })
33
42
  class CreateGithubIssueTool extends ToolContext {
34
43
  async execute(input: { repo: string; title: string; body: string }) {
35
- // Access downstream credentials via the authProviders context extension
36
- const headers = await this.authProviders.headers('github');
44
+ // Read the orchestrated GitHub token (declared in auth.providers below).
45
+ // Throws if GitHub is not linked for this session; use tryGetToken() for a
46
+ // null-on-missing variant. The raw token is never exposed to the LLM.
47
+ const token = await this.orchestration.getToken('github');
37
48
 
38
49
  const response = await fetch(`https://api.github.com/repos/${input.repo}/issues`, {
39
50
  method: 'POST',
40
- headers: { ...headers, 'Content-Type': 'application/json' },
51
+ headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
41
52
  body: JSON.stringify({ title: input.title, body: input.body }),
42
53
  });
43
54
  const issue = await response.json();
@@ -45,31 +56,42 @@ class CreateGithubIssueTool extends ToolContext {
45
56
  }
46
57
  }
47
58
 
48
- @App({
49
- name: 'dev-tools',
50
- auth: {
51
- mode: 'remote',
52
- provider: 'https://auth.example.com',
53
- clientId: process.env['OAUTH_CLIENT_ID'] ?? 'mcp-client-id',
54
- },
55
- tools: [CreateGithubIssueTool],
56
- })
59
+ @App({ name: 'dev-tools', tools: [CreateGithubIssueTool] })
57
60
  class DevToolsApp {}
58
61
 
59
62
  @FrontMcp({
60
63
  info: { name: 'dev-tools-server', version: '1.0.0' },
61
64
  apps: [DevToolsApp],
65
+ // Server-level auth so it applies to every app (no `splitByApp` needed).
66
+ auth: {
67
+ mode: 'local',
68
+ // Declare the upstream provider FrontMCP should orchestrate. FrontMCP
69
+ // federates it at /oauth/authorize, stores its tokens encrypted
70
+ // server-side, and exposes them via this.orchestration.getToken('github').
71
+ providers: [
72
+ {
73
+ id: 'github',
74
+ authorizeUrl: 'https://github.com/login/oauth/authorize',
75
+ tokenUrl: 'https://github.com/login/oauth/access_token',
76
+ clientId: process.env['GITHUB_CLIENT_ID']!,
77
+ clientSecret: process.env['GITHUB_CLIENT_SECRET'],
78
+ scopes: ['repo'],
79
+ },
80
+ ],
81
+ federatedAuth: { minProviders: 1, requiredProviders: ['github'] },
82
+ },
62
83
  })
63
84
  class Server {}
64
85
  ```
65
86
 
66
87
  ## What This Demonstrates
67
88
 
68
- - Configuring `mode: 'remote'` for full OAuth 2.1 authorization flow
69
- - Loading `clientId` from environment variables instead of hardcoding
70
- - Using `this.authProviders.headers('github')` to get pre-formatted auth headers for downstream API calls
89
+ - Declaring an upstream provider in top-level `auth.providers` so FrontMCP orchestrates its OAuth flow
90
+ - Loading `clientId`/`clientSecret` from environment variables instead of hardcoding
91
+ - Reading the downstream provider token in a tool via `this.orchestration.getToken('github')`
71
92
 
72
93
  ## Related
73
94
 
74
- - See `configure-auth` for credential vault API (`get`, `headers`, `has`, `refresh`)
95
+ - See `configure-auth` for the multi-provider orchestration config and the `this.orchestration` API (`getToken`, `tryGetToken`, `isAuthenticated`)
96
+ - See `local-credential-vault` for the separate per-session vault (`this.credentials`)
75
97
  - See `configure-session` for setting up Redis-based session storage in production
@@ -0,0 +1,73 @@
1
+ ---
2
+ name: local-behind-tunnel
3
+ reference: configure-auth-modes
4
+ level: intermediate
5
+ description: 'Expose a local-mode server through a tunnel or TLS proxy by aligning the token issuer with the public URL clients actually reach.'
6
+ tags: [config, auth, local, tunnel, proxy, issuer, auth-modes]
7
+ features:
8
+ - 'Relying on request-host-derived OAuth discovery, which works behind a tunnel or under an http.entryPath without extra config'
9
+ - 'Setting `local.issuer` to a full public HTTPS URL so the token `iss` matches what clients reach through the proxy'
10
+ - 'Knowing `FRONTMCP_PUBLIC_HOST` overrides only the discovery host (scheme/port still come from the HTTP config or local.issuer)'
11
+ ---
12
+
13
+ # Local Mode Behind a Tunnel
14
+
15
+ Expose a local-mode server through a tunnel or TLS proxy by aligning the token issuer with the public URL clients actually reach.
16
+
17
+ ## Code
18
+
19
+ ```typescript
20
+ // src/server.ts
21
+ // OAuth discovery (.well-known/*) is derived from the incoming request host at
22
+ // runtime and advertises /oauth/* at the root, so it works behind a tunnel or
23
+ // reverse proxy with no extra config. `local.issuer` only aligns the boot-time
24
+ // `iss` claim with the public HTTPS URL clients reach.
25
+ //
26
+ // `FRONTMCP_PUBLIC_HOST=mcp.example.com` would set only the discovery HOST
27
+ // (scheme stays http, port stays the HTTP port) — use `local.issuer` when you
28
+ // need a different scheme/port, as below.
29
+ import { App, FrontMcp, Tool, ToolContext, z } from '@frontmcp/sdk';
30
+
31
+ @Tool({
32
+ name: 'ping',
33
+ description: 'Ping the server',
34
+ inputSchema: {},
35
+ outputSchema: { pong: z.boolean() },
36
+ })
37
+ class PingTool extends ToolContext {
38
+ async execute() {
39
+ return { pong: true };
40
+ }
41
+ }
42
+
43
+ @App({
44
+ name: 'tunneled-api',
45
+ auth: {
46
+ mode: 'local',
47
+ local: {
48
+ // Public URL exposed by the tunnel / TLS proxy.
49
+ issuer: 'https://mcp.example.com',
50
+ },
51
+ tokenStorage: { sqlite: { path: './data/auth.sqlite' } },
52
+ },
53
+ tools: [PingTool],
54
+ })
55
+ class TunneledApi {}
56
+
57
+ @FrontMcp({
58
+ info: { name: 'tunneled-server', version: '1.0.0' },
59
+ apps: [TunneledApi],
60
+ })
61
+ class Server {}
62
+ ```
63
+
64
+ ## What This Demonstrates
65
+
66
+ - Relying on request-host-derived OAuth discovery, which works behind a tunnel or under an http.entryPath without extra config
67
+ - Setting `local.issuer` to a full public HTTPS URL so the token `iss` matches what clients reach through the proxy
68
+ - Knowing `FRONTMCP_PUBLIC_HOST` overrides only the discovery host (scheme/port still come from the HTTP config or local.issuer)
69
+
70
+ ## Related
71
+
72
+ - See `configure-auth-modes` for a comparison of all auth modes
73
+ - See `local-self-signed-tokens` for token persistence options
@@ -0,0 +1,87 @@
1
+ ---
2
+ name: local-consent-enforcement
3
+ reference: configure-auth-modes
4
+ level: intermediate
5
+ description: 'Enable consent in local mode to render a tool-selection screen at login and enforce the chosen tools at call time, keeping essential tools always available via excludedTools.'
6
+ tags: [config, auth, local, consent, tool-authorization, auth-modes]
7
+ features:
8
+ - 'Setting `consent.enabled` so login renders a tool-selection screen and the chosen tools are enforced on every tools/call'
9
+ - 'Listing `excludedTools` so essential tools are never offered and are always available'
10
+ - 'Honoring `requireSelection` / `defaultSelectedTools` to require a non-empty selection and pre-check tools'
11
+ - 'Pinning `rememberConsent: false` to re-show the screen on every login (the default `true` reuses a prior per-(user, client) selection and only re-prompts when a NEW tool appears)'
12
+ ---
13
+
14
+ # Local Consent Enforcement
15
+
16
+ Enable consent in local mode to render a tool-selection screen at login and enforce the chosen tools at call time, keeping essential tools always available via excludedTools.
17
+
18
+ ## Code
19
+
20
+ ```typescript
21
+ // src/server.ts
22
+ import { App, FrontMcp, Tool, ToolContext, z } from '@frontmcp/sdk';
23
+
24
+ @Tool({
25
+ name: 'health',
26
+ description: 'Health check',
27
+ inputSchema: {},
28
+ outputSchema: { ok: z.boolean() },
29
+ })
30
+ class HealthTool extends ToolContext {
31
+ async execute() {
32
+ return { ok: true };
33
+ }
34
+ }
35
+
36
+ @Tool({
37
+ name: 'delete_account',
38
+ description: 'Delete a user account',
39
+ inputSchema: { userId: z.string() },
40
+ outputSchema: { deleted: z.boolean() },
41
+ })
42
+ class DeleteAccountTool extends ToolContext {
43
+ async execute(input: { userId: string }) {
44
+ return { deleted: true };
45
+ }
46
+ }
47
+
48
+ @App({
49
+ name: 'admin-api',
50
+ auth: {
51
+ mode: 'local',
52
+ consent: {
53
+ enabled: true,
54
+ requireSelection: true, // reject an empty submit (default)
55
+ // Re-show the screen on every login so the user must explicitly opt into
56
+ // delete_account each time. `rememberConsent` defaults to `true` (reuse a
57
+ // prior per-(user, client) selection); pin it `false` for an opt-in-every-time
58
+ // screen like this one.
59
+ rememberConsent: false,
60
+ // `health` is never offered on the consent screen and is always callable.
61
+ excludedTools: ['health'],
62
+ // Pre-check nothing dangerous: the user must explicitly opt into delete_account.
63
+ defaultSelectedTools: [],
64
+ },
65
+ },
66
+ tools: [HealthTool, DeleteAccountTool],
67
+ })
68
+ class AdminApi {}
69
+
70
+ @FrontMcp({
71
+ info: { name: 'consent-server', version: '1.0.0' },
72
+ apps: [AdminApi],
73
+ })
74
+ class Server {}
75
+ ```
76
+
77
+ ## What This Demonstrates
78
+
79
+ - Setting `consent.enabled` so login renders a tool-selection screen and the chosen tools are enforced on every tools/call
80
+ - Listing `excludedTools` so essential tools are never offered and are always available
81
+ - Honoring `requireSelection` / `defaultSelectedTools` to require a non-empty selection and pre-check tools
82
+ - Pinning `rememberConsent: false` to re-show the screen on every login (the default `true` reuses a prior per-(user, client) selection and only re-prompts when a NEW tool appears)
83
+
84
+ ## Related
85
+
86
+ - See `configure-auth-modes` for a comparison of all auth modes
87
+ - See `local-self-signed-tokens` for persisting tokens across restarts
@@ -0,0 +1,67 @@
1
+ ---
2
+ name: local-dcr-control
3
+ reference: configure-auth-modes
4
+ level: intermediate
5
+ description: 'Lock down the built-in Dynamic Client Registration endpoint with the auth.dcr control surface: disable open registration, allowlist redirect URIs and client ids, and seed a pre-registered trusted client.'
6
+ tags: [config, auth, local, dcr, dynamic-client-registration, security, auth-modes]
7
+ features:
8
+ - 'Setting `dcr.enabled: false` so `POST /oauth/register` returns 404 and `registration_endpoint` is dropped from AS metadata'
9
+ - 'Constraining `dcr.allowedRedirectUris` (exact or `*` glob) so unlisted redirect URIs are rejected at both register and authorize'
10
+ - 'Constraining `dcr.allowedClientIds` so only known client ids may use `/oauth/authorize`'
11
+ - 'Seeding `dcr.clients` so a trusted client is accepted without any DCR round-trip'
12
+ ---
13
+
14
+ # Lock Down Dynamic Client Registration
15
+
16
+ Lock down the built-in Dynamic Client Registration endpoint with the auth.dcr control surface: disable open registration, allowlist redirect URIs and client ids, and seed a pre-registered trusted client.
17
+
18
+ ## Code
19
+
20
+ ```typescript
21
+ // src/server.ts
22
+ // JWT_SECRET still signs the HS256 tokens — set a stable value.
23
+ import { FrontMcp } from '@frontmcp/sdk';
24
+
25
+ @FrontMcp({
26
+ info: { name: 'internal-api', version: '1.0.0' },
27
+ auth: {
28
+ mode: 'local',
29
+ dcr: {
30
+ // Close open self-registration entirely (regardless of NODE_ENV).
31
+ // /oauth/register now responds 404 and AS metadata omits registration_endpoint.
32
+ enabled: false,
33
+ // Only these redirect URIs are ever accepted (exact match or simple `*` glob),
34
+ // enforced at BOTH /oauth/register and /oauth/authorize.
35
+ allowedRedirectUris: ['https://app.example.com/callback', 'http://localhost:*/callback'],
36
+ // Only these client ids may use /oauth/authorize.
37
+ allowedClientIds: ['dashboard'],
38
+ // Seed the trusted client so authorize accepts it WITHOUT a DCR round-trip.
39
+ clients: [
40
+ {
41
+ clientId: 'dashboard',
42
+ redirectUris: ['https://app.example.com/callback'],
43
+ clientName: 'Internal Dashboard',
44
+ },
45
+ ],
46
+ },
47
+ },
48
+ })
49
+ class Server {}
50
+ ```
51
+
52
+ To keep DCR open but authenticated instead of closed, drop `enabled: false` and set
53
+ `initialAccessToken` — then `POST /oauth/register` requires an
54
+ `Authorization: Bearer <token>` header that matches it (constant-time compared), and
55
+ requests without it get `401 invalid_token`.
56
+
57
+ ## What This Demonstrates
58
+
59
+ - Setting `dcr.enabled: false` so `POST /oauth/register` returns 404 and `registration_endpoint` is dropped from AS metadata
60
+ - Constraining `dcr.allowedRedirectUris` (exact or `*` glob) so unlisted redirect URIs are rejected at both register and authorize
61
+ - Constraining `dcr.allowedClientIds` so only known client ids may use `/oauth/authorize`
62
+ - Seeding `dcr.clients` so a trusted client is accepted without any DCR round-trip
63
+
64
+ ## Related
65
+
66
+ - See `configure-auth-modes` for the full local-mode option reference, including the `dcr` block
67
+ - See `local-single-operator` for the smallest single-operator local-mode configuration
@@ -0,0 +1,62 @@
1
+ ---
2
+ name: local-minimal
3
+ reference: configure-auth-modes
4
+ level: basic
5
+ description: 'Stand up the built-in local OAuth 2.1 server with the minimum configuration: HS256 signing via JWT_SECRET and in-memory token storage.'
6
+ tags: [config, auth, local, hs256, auth-modes, modes]
7
+ features:
8
+ - "Using `mode: 'local'` with no other auth options to run the built-in OAuth 2.1 server"
9
+ - 'Relying on `JWT_SECRET` for HS256 token signing (symmetric secret, no key pair)'
10
+ - 'Accepting the default in-memory `tokenStorage` for local development (state is lost on restart)'
11
+ ---
12
+
13
+ # Minimal Local Mode
14
+
15
+ Stand up the built-in local OAuth 2.1 server with the minimum configuration: HS256 signing via JWT_SECRET and in-memory token storage.
16
+
17
+ ## Code
18
+
19
+ ```typescript
20
+ // src/server.ts
21
+ // Set JWT_SECRET in the environment, e.g. `export JWT_SECRET=$(openssl rand -hex 32)`.
22
+ // If unset, FrontMCP uses a random per-process secret and tokens are invalidated on restart.
23
+ import { App, FrontMcp, Tool, ToolContext, z } from '@frontmcp/sdk';
24
+
25
+ @Tool({
26
+ name: 'whoami',
27
+ description: 'Return the authenticated subject',
28
+ inputSchema: {},
29
+ outputSchema: { sub: z.string() },
30
+ })
31
+ class WhoAmITool extends ToolContext {
32
+ async execute() {
33
+ return { sub: String(this.auth.claims['sub'] ?? 'unknown') };
34
+ }
35
+ }
36
+
37
+ @App({
38
+ name: 'internal-api',
39
+ auth: {
40
+ mode: 'local',
41
+ },
42
+ tools: [WhoAmITool],
43
+ })
44
+ class InternalApi {}
45
+
46
+ @FrontMcp({
47
+ info: { name: 'local-minimal', version: '1.0.0' },
48
+ apps: [InternalApi],
49
+ })
50
+ class Server {}
51
+ ```
52
+
53
+ ## What This Demonstrates
54
+
55
+ - Using `mode: 'local'` with no other auth options to run the built-in OAuth 2.1 server
56
+ - Relying on `JWT_SECRET` for HS256 token signing (symmetric secret, no key pair)
57
+ - Accepting the default in-memory `tokenStorage` for local development (state is lost on restart)
58
+
59
+ ## Related
60
+
61
+ - See `configure-auth-modes` for a comparison of all auth modes
62
+ - See `local-self-signed-tokens` for persisting tokens with SQLite or Redis
@@ -0,0 +1,93 @@
1
+ ---
2
+ name: local-multi-provider-orchestration
3
+ reference: configure-auth-modes
4
+ level: advanced
5
+ description: 'Orchestrate multiple upstream OAuth providers (GitHub + Slack) in local mode, gate the JWT until they are linked, and read downstream tokens in tools via this.orchestration.'
6
+ tags: [config, auth, local, orchestration, federated, providers, multi-provider, auth-modes]
7
+ features:
8
+ - 'Declaring an `auth.providers` array so FrontMCP federates GitHub + Slack at /oauth/authorize and stores their tokens encrypted server-side'
9
+ - 'Using `authorizeUrl`/`tokenUrl` aliases and letting the per-provider callback URL be auto-computed as ${issuer}/oauth/provider/${id}/callback'
10
+ - 'Gating JWT issuance with `federatedAuth.minProviders` / `requiredProviders` so no token is minted until the linked-provider threshold is met'
11
+ - 'Reading downstream provider tokens in a tool via `this.orchestration.getToken(id)` / `tryGetToken(id)` without exposing them to the LLM'
12
+ ---
13
+
14
+ # Multi-Provider Orchestration (Local Mode)
15
+
16
+ Orchestrate multiple upstream OAuth providers (GitHub + Slack) in local mode, gate the JWT until they are linked, and read downstream tokens in tools via this.orchestration.
17
+
18
+ ## Code
19
+
20
+ ```typescript
21
+ // src/server.ts
22
+ // JWT_SECRET signs the HS256 tokens — set a stable value.
23
+ import { App, FrontMcp, Tool, ToolContext, z } from '@frontmcp/sdk';
24
+
25
+ @Tool({
26
+ name: 'github_repos',
27
+ description: 'List the operator GitHub repositories using the linked upstream token',
28
+ inputSchema: {},
29
+ outputSchema: { repos: z.array(z.string()) },
30
+ })
31
+ class GitHubReposTool extends ToolContext {
32
+ async execute() {
33
+ // `this.orchestration` is bound for authenticated orchestrated requests.
34
+ const token = await this.orchestration.getToken('github'); // throws if not linked
35
+ const res = await fetch('https://api.github.com/user/repos', {
36
+ headers: { Authorization: `Bearer ${token}` },
37
+ });
38
+ const repos = (await res.json()) as Array<{ full_name: string }>;
39
+ return { repos: repos.map((r) => r.full_name) };
40
+ }
41
+ }
42
+
43
+ @App({
44
+ name: 'orchestrated-api',
45
+ auth: {
46
+ mode: 'local',
47
+ // Declare the upstream providers to orchestrate. `authorizeUrl`/`tokenUrl`
48
+ // are accepted aliases for `authorizationEndpoint`/`tokenEndpoint`.
49
+ providers: [
50
+ {
51
+ id: 'github',
52
+ authorizeUrl: 'https://github.com/login/oauth/authorize',
53
+ tokenUrl: 'https://github.com/login/oauth/access_token',
54
+ clientId: process.env.GITHUB_CLIENT_ID!,
55
+ clientSecret: process.env.GITHUB_CLIENT_SECRET,
56
+ scopes: ['read:user', 'repo'],
57
+ },
58
+ {
59
+ id: 'slack',
60
+ authorizeUrl: 'https://slack.com/oauth/v2/authorize',
61
+ tokenUrl: 'https://slack.com/api/oauth.v2.access',
62
+ clientId: process.env.SLACK_CLIENT_ID!,
63
+ clientSecret: process.env.SLACK_CLIENT_SECRET,
64
+ scopes: ['users:read'],
65
+ },
66
+ ],
67
+ // No JWT is minted until GitHub (required) is linked.
68
+ federatedAuth: { minProviders: 1, requiredProviders: ['github'] },
69
+ tokenStorage: { sqlite: { path: './data/auth.sqlite' } },
70
+ },
71
+ tools: [GitHubReposTool],
72
+ })
73
+ class OrchestratedApi {}
74
+
75
+ @FrontMcp({
76
+ info: { name: 'orchestrated-server', version: '1.0.0' },
77
+ apps: [OrchestratedApi],
78
+ })
79
+ class Server {}
80
+ ```
81
+
82
+ ## What This Demonstrates
83
+
84
+ - Declaring an `auth.providers` array so FrontMCP federates GitHub + Slack at /oauth/authorize and stores their tokens encrypted server-side
85
+ - Using `authorizeUrl`/`tokenUrl` aliases and letting the per-provider callback URL be auto-computed as ${issuer}/oauth/provider/${id}/callback
86
+ - Gating JWT issuance with `federatedAuth.minProviders` / `requiredProviders` so no token is minted until the linked-provider threshold is met
87
+ - Reading downstream provider tokens in a tool via `this.orchestration.getToken(id)` / `tryGetToken(id)` without exposing them to the LLM
88
+
89
+ ## Related
90
+
91
+ - See `configure-auth` → "Multi-provider orchestration" for the full provider schema and the `this.orchestration` API
92
+ - See `configure-auth-modes` for a comparison of all auth modes
93
+ - See `local-single-operator` for the no-provider single-operator local setup
@@ -2,24 +2,26 @@
2
2
  name: local-self-signed-tokens
3
3
  reference: configure-auth-modes
4
4
  level: intermediate
5
- description: 'Configure a server that signs its own JWT tokens with consent and incremental auth enabled.'
6
- tags: [config, auth, redis, local, auth-modes, modes]
5
+ description: 'Configure a local-mode server that signs its own HS256 JWTs and persists auth state across restarts with SQLite or Redis.'
6
+ tags: [config, auth, local, sqlite, redis, auth-modes, modes]
7
7
  features:
8
- - "Using `mode: 'local'` so the server signs its own JWTs"
8
+ - "Using `mode: 'local'` so the server signs its own HS256 JWTs (symmetric `JWT_SECRET`, no key pair)"
9
9
  - 'Setting `local.issuer` and `expectedAudience` to control token claims'
10
- - 'Enabling `consent` for explicit user authorization flow'
11
- - 'Enabling `incrementalAuth` to request additional scopes progressively'
12
- - 'Using Redis for token storage in production'
10
+ - 'Persisting authorization codes and refresh tokens with `tokenStorage: { sqlite: { path } }` so they survive restart'
11
+ - 'Switching the same `tokenStorage` to `{ redis }` for multi-instance deployments'
12
+ - 'Enabling `consent` so login renders a tool-selection screen and the chosen tools are enforced at call time'
13
13
  ---
14
14
 
15
15
  # Local Self-Signed Tokens
16
16
 
17
- Configure a server that signs its own JWT tokens with consent and incremental auth enabled.
17
+ Configure a local-mode server that signs its own HS256 JWTs and persists auth state across restarts with SQLite or Redis.
18
18
 
19
19
  ## Code
20
20
 
21
21
  ```typescript
22
22
  // src/server.ts
23
+ // Set a stable JWT_SECRET (e.g. `openssl rand -hex 32`) so HS256-signed
24
+ // tokens survive restart. If unset, a random per-process secret is used.
23
25
  import { App, FrontMcp, Tool, ToolContext, z } from '@frontmcp/sdk';
24
26
 
25
27
  @Tool({
@@ -39,12 +41,13 @@ class ManageUsersTool extends ToolContext {
39
41
  auth: {
40
42
  mode: 'local',
41
43
  local: {
42
- issuer: 'my-internal-server',
44
+ issuer: 'https://mcp.internal.example.com',
43
45
  },
44
46
  expectedAudience: 'internal-api',
45
- tokenStorage: { redis: { host: process.env['REDIS_HOST'] ?? 'localhost', port: 6379 } },
47
+ // Single-node persistence (survives restart, no Redis required).
48
+ // For multiple instances, swap for: { redis: { host: ..., port: 6379 } }
49
+ tokenStorage: { sqlite: { path: './data/auth.sqlite' } },
46
50
  consent: { enabled: true },
47
- incrementalAuth: { enabled: true },
48
51
  },
49
52
  tools: [ManageUsersTool],
50
53
  })
@@ -53,24 +56,19 @@ class InternalApi {}
53
56
  @FrontMcp({
54
57
  info: { name: 'local-auth-server', version: '1.0.0' },
55
58
  apps: [InternalApi],
56
- redis: {
57
- provider: 'redis',
58
- host: process.env['REDIS_HOST'] ?? 'localhost',
59
- port: 6379,
60
- },
61
59
  })
62
60
  class Server {}
63
61
  ```
64
62
 
65
63
  ## What This Demonstrates
66
64
 
67
- - Using `mode: 'local'` so the server signs its own JWTs
65
+ - Using `mode: 'local'` so the server signs its own HS256 JWTs (symmetric `JWT_SECRET`, no key pair)
68
66
  - Setting `local.issuer` and `expectedAudience` to control token claims
69
- - Enabling `consent` for explicit user authorization flow
70
- - Enabling `incrementalAuth` to request additional scopes progressively
71
- - Using Redis for token storage in production
67
+ - Persisting authorization codes and refresh tokens with `tokenStorage: { sqlite: { path } }` so they survive restart
68
+ - Switching the same `tokenStorage` to `{ redis }` for multi-instance deployments
69
+ - Enabling `consent` so login renders a tool-selection screen and the chosen tools are enforced at call time
72
70
 
73
71
  ## Related
74
72
 
75
73
  - See `configure-auth-modes` for a comparison of all auth modes
76
- - See `configure-session` for session storage configuration
74
+ - See `configure-session` for transport/session storage configuration
@@ -0,0 +1,66 @@
1
+ ---
2
+ name: local-single-operator
3
+ reference: configure-auth-modes
4
+ level: basic
5
+ description: 'Run local mode for a single operator (e.g. Claude Code) by skipping the email prompt and minting a stable anonymous subject.'
6
+ tags: [config, auth, local, single-operator, claude-code, auth-modes]
7
+ features:
8
+ - 'Setting `requireEmail: false` so the login callback mints a code without prompting for an email'
9
+ - 'Setting `anonymousSubject` so the single operator gets a stable `sub` across logins'
10
+ - 'Persisting tokens with SQLite so the operator stays logged in across restarts'
11
+ ---
12
+
13
+ # Single-Operator Local Mode
14
+
15
+ Run local mode for a single operator (e.g. Claude Code) by skipping the email prompt and minting a stable anonymous subject.
16
+
17
+ ## Code
18
+
19
+ ```typescript
20
+ // src/server.ts
21
+ // JWT_SECRET still signs the HS256 tokens — set a stable value.
22
+ import { App, FrontMcp, Tool, ToolContext, z } from '@frontmcp/sdk';
23
+
24
+ @Tool({
25
+ name: 'list_notes',
26
+ description: 'List the operator notes',
27
+ inputSchema: {},
28
+ outputSchema: { notes: z.array(z.string()) },
29
+ })
30
+ class ListNotesTool extends ToolContext {
31
+ async execute() {
32
+ return { notes: [] };
33
+ }
34
+ }
35
+
36
+ @App({
37
+ name: 'operator-api',
38
+ auth: {
39
+ mode: 'local',
40
+ // Single operator: do not prompt for an email at /oauth/callback.
41
+ requireEmail: false,
42
+ // Stable subject minted when no email is supplied (this is the default value).
43
+ anonymousSubject: 'local-operator',
44
+ tokenStorage: { sqlite: { path: './data/auth.sqlite' } },
45
+ },
46
+ tools: [ListNotesTool],
47
+ })
48
+ class OperatorApi {}
49
+
50
+ @FrontMcp({
51
+ info: { name: 'single-operator-server', version: '1.0.0' },
52
+ apps: [OperatorApi],
53
+ })
54
+ class Server {}
55
+ ```
56
+
57
+ ## What This Demonstrates
58
+
59
+ - Setting `requireEmail: false` so the login callback mints a code without prompting for an email
60
+ - Setting `anonymousSubject` so the single operator gets a stable `sub` across logins
61
+ - Persisting tokens with SQLite so the operator stays logged in across restarts
62
+
63
+ ## Related
64
+
65
+ - See `configure-auth-modes` for a comparison of all auth modes
66
+ - See `local-minimal` for the smallest local-mode configuration