@frontmcp/skills 1.2.1 → 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 (131) 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 +14 -7
  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-deployment-targets.md +84 -1
  83. package/catalog/frontmcp-config/references/configure-http.md +203 -14
  84. package/catalog/frontmcp-config/references/configure-session.md +14 -7
  85. package/catalog/frontmcp-deployment/SKILL.md +17 -15
  86. package/catalog/frontmcp-deployment/references/build-for-mcpb.md +1 -1
  87. package/catalog/frontmcp-deployment/references/deploy-manifest-yaml.md +308 -0
  88. package/catalog/frontmcp-deployment/references/deploy-to-cloudflare-skills-only.md +174 -0
  89. package/catalog/frontmcp-deployment/references/mcp-client-integration.md +145 -2
  90. package/catalog/frontmcp-development/SKILL.md +36 -50
  91. package/catalog/frontmcp-development/examples/create-provider/basic-database-provider.md +14 -0
  92. package/catalog/frontmcp-development/examples/create-provider/config-and-api-providers.md +85 -9
  93. package/catalog/frontmcp-development/references/create-job.md +45 -11
  94. package/catalog/frontmcp-development/references/create-provider.md +80 -8
  95. package/catalog/frontmcp-development/references/create-skill-with-tools.md +31 -0
  96. package/catalog/frontmcp-development/references/create-skill.md +45 -0
  97. package/catalog/frontmcp-development/references/decorators-guide.md +15 -15
  98. package/catalog/frontmcp-extensibility/SKILL.md +1 -1
  99. package/catalog/frontmcp-extensibility/examples/skill-audit-log/verify-chain.md +8 -6
  100. package/catalog/frontmcp-extensibility/references/skill-audit-log.md +7 -2
  101. package/catalog/frontmcp-guides/SKILL.md +8 -8
  102. package/catalog/frontmcp-observability/SKILL.md +16 -8
  103. package/catalog/frontmcp-observability/examples/metrics-endpoint/enable-metrics-endpoint.md +77 -0
  104. package/catalog/frontmcp-observability/references/metrics-endpoint.md +161 -0
  105. package/catalog/frontmcp-production-readiness/SKILL.md +1 -1
  106. package/catalog/frontmcp-production-readiness/examples/common-checklist/security-hardening.md +3 -2
  107. package/catalog/frontmcp-setup/SKILL.md +12 -12
  108. package/catalog/frontmcp-setup/examples/frontmcp-skills-usage/install-and-search-skills.md +19 -1
  109. package/catalog/frontmcp-setup/examples/multi-app-composition/per-app-auth-and-isolation.md +7 -4
  110. package/catalog/frontmcp-setup/references/frontmcp-skills-usage.md +260 -19
  111. package/catalog/frontmcp-setup/references/multi-app-composition.md +6 -5
  112. package/catalog/frontmcp-setup/references/setup-project.md +29 -0
  113. package/catalog/frontmcp-setup/references/setup-sqlite.md +68 -9
  114. package/catalog/frontmcp-testing/SKILL.md +26 -18
  115. package/catalog/frontmcp-testing/references/test-auth.md +24 -0
  116. package/catalog/skills-manifest.json +676 -146
  117. package/package.json +1 -1
  118. package/src/manifest.d.ts +72 -1
  119. package/src/manifest.js +4 -1
  120. package/src/manifest.js.map +1 -1
  121. package/catalog/frontmcp-development/examples/create-tool/basic-class-tool.md +0 -61
  122. package/catalog/frontmcp-development/examples/create-tool/tool-with-di-and-errors.md +0 -84
  123. package/catalog/frontmcp-development/examples/create-tool/tool-with-rate-limiting-and-progress.md +0 -92
  124. package/catalog/frontmcp-development/examples/create-tool-annotations/destructive-delete-tool.md +0 -92
  125. package/catalog/frontmcp-development/examples/create-tool-annotations/readonly-query-tool.md +0 -59
  126. package/catalog/frontmcp-development/examples/create-tool-output-schema-types/primitive-and-media-outputs.md +0 -101
  127. package/catalog/frontmcp-development/examples/create-tool-output-schema-types/zod-raw-shape-output.md +0 -62
  128. package/catalog/frontmcp-development/examples/create-tool-output-schema-types/zod-schema-advanced-output.md +0 -101
  129. package/catalog/frontmcp-development/references/create-tool-annotations.md +0 -48
  130. package/catalog/frontmcp-development/references/create-tool-output-schema-types.md +0 -71
  131. package/catalog/frontmcp-development/references/create-tool.md +0 -728
@@ -0,0 +1,138 @@
1
+ ---
2
+ name: local-secure-store
3
+ reference: configure-auth
4
+ level: intermediate
5
+ description: 'Configure the general session secure-secret store (this.secureStore) with a pluggable backing (memory / sqlite / redis / custom OS-keychain) and read/write user-typed secrets from a tool.'
6
+ tags: [config, auth, local, secure-store, secrets, this-secure-store, keychain]
7
+ features:
8
+ - 'Selecting the secure-store backing via `auth.secureStore` (memory / sqlite / redis / custom backend) plus a namespace `scope`'
9
+ - 'Reading/writing arbitrary user-typed secrets from a tool via `this.secureStore.set/get/list/delete` (JSON-serialized, scoped to the session/subject)'
10
+ - 'Backing the store with an OS keychain by supplying a `SecureStoreBackend` — no native dependency is bundled by the framework'
11
+ - 'Understanding scope: `user` (keyed by sub, default), `session` (keyed by sessionId), `global` (server-wide)'
12
+ ---
13
+
14
+ # Local Mode with the Session Secure-Secret Store (`this.secureStore`)
15
+
16
+ Configure the general session secure-secret store (this.secureStore) with a pluggable backing (memory / sqlite / redis / custom OS-keychain) and read/write user-typed secrets from a tool.
17
+
18
+ The store is distinct from `this.credentials` (which is OAuth-credential-centric):
19
+ use `this.secureStore` for arbitrary secrets like an API key a tool prompts for.
20
+ The built-in backings encrypt at rest with AES-256-GCM, and an OS keychain can be
21
+ plugged in without the framework bundling any native dependency.
22
+
23
+ ## Code
24
+
25
+ ```typescript
26
+ // src/server.ts
27
+ import { App, FrontMcp, Tool, ToolContext, z, type SecureStoreConfig } from '@frontmcp/sdk';
28
+
29
+ @Tool({
30
+ name: 'set_api_key',
31
+ description: 'Store a user-typed API key in the session secure store',
32
+ inputSchema: { apiKey: z.string() },
33
+ outputSchema: { saved: z.boolean(), keys: z.array(z.string()) },
34
+ })
35
+ class SetApiKeyTool extends ToolContext {
36
+ async execute(input: { apiKey: string }) {
37
+ // Values are JSON-serialized and encrypted at rest (built-in backings),
38
+ // scoped to the current `sub` (user scope, the default).
39
+ await this.secureStore.set('stg.api-key', input.apiKey);
40
+ const keys = await this.secureStore.list();
41
+ return { saved: true, keys };
42
+ }
43
+ }
44
+
45
+ @Tool({
46
+ name: 'read_api_key',
47
+ description: 'Read the stored API key (presence only — never returns the raw secret)',
48
+ inputSchema: {},
49
+ outputSchema: { present: z.boolean() },
50
+ })
51
+ class ReadApiKeyTool extends ToolContext {
52
+ async execute() {
53
+ const apiKey = await this.secureStore.get<string>('stg.api-key');
54
+ // Never return the raw secret to the model — only presence.
55
+ return { present: apiKey !== undefined };
56
+ }
57
+ }
58
+
59
+ @App({ name: 'Secrets', tools: [SetApiKeyTool, ReadApiKeyTool] })
60
+ class SecretsApp {}
61
+
62
+ // Pick the backing per deployment. Default ('memory') is encrypted in-process.
63
+ const secureStore: SecureStoreConfig = {
64
+ sqlite: { path: './.frontmcp/secrets.sqlite' }, // survives restart
65
+ scope: 'user', // 'user' (default) | 'session' | 'global'
66
+ };
67
+
68
+ @FrontMcp({
69
+ info: { name: 'secure-store-demo', version: '0.1.0' },
70
+ apps: [SecretsApp],
71
+ auth: {
72
+ mode: 'local',
73
+ secureStore,
74
+ login: {
75
+ title: 'Sign in',
76
+ fields: { apiKey: { type: 'password', label: 'API Key', required: true } },
77
+ subject: { fromField: 'apiKey', strategy: 'per-account' },
78
+ },
79
+ authenticate: async (input) => {
80
+ if (!input.fields['apiKey']) return { ok: false, message: 'API key required', retryField: 'apiKey' };
81
+ return { ok: true };
82
+ },
83
+ },
84
+ })
85
+ export default class Server {}
86
+ ```
87
+
88
+ ## What This Demonstrates
89
+
90
+ - Selecting the secure-store backing via `auth.secureStore` (memory / sqlite / redis / custom backend) plus a namespace `scope`
91
+ - Reading/writing arbitrary user-typed secrets from a tool via `this.secureStore.set/get/list/delete` (JSON-serialized, scoped to the session/subject)
92
+ - Backing the store with an OS keychain by supplying a `SecureStoreBackend` — no native dependency is bundled by the framework
93
+ - Understanding scope: `user` (keyed by sub, default), `session` (keyed by sessionId), `global` (server-wide)
94
+
95
+ ## Optional: back the store with an OS keychain (pluggable, not bundled)
96
+
97
+ FrontMCP does **not** ship `keytar`/`wincred`/`libsecret`. Supply an object
98
+ implementing `SecureStoreBackend` and the framework uses it as-is (no framework
99
+ crypto — an OS keychain is encrypted by the OS):
100
+
101
+ ```typescript
102
+ import type { SecureStoreBackend } from '@frontmcp/sdk';
103
+
104
+ // import keytar from 'keytar'; // YOU add the native peer-dep
105
+
106
+ const keychainBackend: SecureStoreBackend = {
107
+ async get(namespace, key) {
108
+ return (await keytar.getPassword(`frontmcp:${namespace}`, key)) ?? null;
109
+ },
110
+ async set(namespace, key, value /*, ttlMs */) {
111
+ await keytar.setPassword(`frontmcp:${namespace}`, key, value); // ttlMs ignored
112
+ },
113
+ async delete(namespace, key) {
114
+ return keytar.deletePassword(`frontmcp:${namespace}`, key);
115
+ },
116
+ async list(/* namespace */) {
117
+ const creds = await keytar.findCredentials('frontmcp');
118
+ return creds.map((c) => c.account);
119
+ },
120
+ };
121
+
122
+ // auth: { mode: 'local', secureStore: { backend: keychainBackend, scope: 'global' } }
123
+ ```
124
+
125
+ ## Notes
126
+
127
+ - **Backings**: `'memory'` (default, encrypted in-process), `{ sqlite: { path } }`,
128
+ `{ redis: { ... } }`, or `{ backend }` (custom). The persistent built-ins reuse
129
+ the same `StorageAdapter`/`VaultEncryption` as `tokenStorage`; when the backing
130
+ matches `tokenStorage` the same connection is shared.
131
+ - **Scope**: `user` keys by `sub` (anonymous requests read empty / skip writes),
132
+ `session` keys by `sessionId`, `global` shares one server-wide namespace. The
133
+ identity is hashed into the namespace — never stored raw.
134
+ - **API**: `get<T>(key)`, `set<T>(key, value, { ttlMs? })`, `delete(key)`,
135
+ `list()`. Object configs also accept `ttlMs` and `encryption.pepper`
136
+ (overrides `VAULT_SECRET ?? JWT_SECRET`).
137
+ - **Never return raw secrets to the model** — expose presence/redacted previews
138
+ only, as `read_api_key` does above.
@@ -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