@frontmcp/skills 0.0.1 → 1.0.0-beta.9

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 (84) hide show
  1. package/catalog/TEMPLATE.md +58 -13
  2. package/catalog/frontmcp-config/SKILL.md +140 -0
  3. package/catalog/frontmcp-config/references/configure-auth.md +238 -0
  4. package/catalog/frontmcp-config/references/configure-elicitation.md +178 -0
  5. package/catalog/frontmcp-config/references/configure-http.md +205 -0
  6. package/catalog/frontmcp-config/references/configure-session.md +205 -0
  7. package/catalog/frontmcp-config/references/configure-throttle.md +229 -0
  8. package/catalog/frontmcp-config/references/configure-transport.md +195 -0
  9. package/catalog/frontmcp-config/references/setup-redis.md +4 -0
  10. package/catalog/frontmcp-config/references/setup-sqlite.md +4 -0
  11. package/catalog/frontmcp-deployment/SKILL.md +124 -0
  12. package/catalog/frontmcp-deployment/references/build-for-browser.md +138 -0
  13. package/catalog/frontmcp-deployment/references/build-for-cli.md +138 -0
  14. package/catalog/{deployment/build-for-sdk/SKILL.md → frontmcp-deployment/references/build-for-sdk.md} +65 -24
  15. package/catalog/frontmcp-deployment/references/deploy-to-cloudflare.md +213 -0
  16. package/catalog/{deployment/deploy-to-lambda/SKILL.md → frontmcp-deployment/references/deploy-to-lambda.md} +73 -60
  17. package/catalog/{deployment/deploy-to-node/references/Dockerfile.example → frontmcp-deployment/references/deploy-to-node-dockerfile.md} +11 -2
  18. package/catalog/{deployment/deploy-to-node/SKILL.md → frontmcp-deployment/references/deploy-to-node.md} +65 -37
  19. package/catalog/frontmcp-deployment/references/deploy-to-vercel-config.md +60 -0
  20. package/catalog/frontmcp-deployment/references/deploy-to-vercel.md +224 -0
  21. package/catalog/frontmcp-development/SKILL.md +118 -0
  22. package/catalog/frontmcp-development/references/create-adapter.md +165 -0
  23. package/catalog/{development/create-agent/references/llm-config.md → frontmcp-development/references/create-agent-llm-config.md} +5 -5
  24. package/catalog/{development/create-agent/SKILL.md → frontmcp-development/references/create-agent.md} +82 -44
  25. package/catalog/{development/create-job/SKILL.md → frontmcp-development/references/create-job.md} +61 -19
  26. package/catalog/{plugins/create-plugin-hooks/SKILL.md → frontmcp-development/references/create-plugin-hooks.md} +63 -11
  27. package/catalog/{plugins/create-plugin/SKILL.md → frontmcp-development/references/create-plugin.md} +65 -60
  28. package/catalog/{development/create-prompt/SKILL.md → frontmcp-development/references/create-prompt.md} +62 -26
  29. package/catalog/{development/create-provider/SKILL.md → frontmcp-development/references/create-provider.md} +62 -27
  30. package/catalog/{development/create-resource/SKILL.md → frontmcp-development/references/create-resource.md} +62 -30
  31. package/catalog/{development/create-skill-with-tools/SKILL.md → frontmcp-development/references/create-skill-with-tools.md} +69 -24
  32. package/catalog/{development/create-skill/SKILL.md → frontmcp-development/references/create-skill.md} +71 -20
  33. package/catalog/{development/create-tool/SKILL.md → frontmcp-development/references/create-tool.md} +62 -26
  34. package/catalog/{development/create-workflow/SKILL.md → frontmcp-development/references/create-workflow.md} +60 -18
  35. package/catalog/{development/decorators-guide/SKILL.md → frontmcp-development/references/decorators-guide.md} +123 -34
  36. package/catalog/frontmcp-development/references/official-adapters.md +194 -0
  37. package/catalog/{plugins/official-plugins/SKILL.md → frontmcp-development/references/official-plugins.md} +68 -22
  38. package/catalog/frontmcp-guides/SKILL.md +417 -0
  39. package/catalog/frontmcp-guides/references/example-knowledge-base.md +636 -0
  40. package/catalog/frontmcp-guides/references/example-task-manager.md +512 -0
  41. package/catalog/frontmcp-guides/references/example-weather-api.md +292 -0
  42. package/catalog/frontmcp-setup/SKILL.md +127 -0
  43. package/catalog/frontmcp-setup/references/frontmcp-skills-usage.md +265 -0
  44. package/catalog/{setup/multi-app-composition/SKILL.md → frontmcp-setup/references/multi-app-composition.md} +65 -23
  45. package/catalog/{setup/nx-workflow/SKILL.md → frontmcp-setup/references/nx-workflow.md} +78 -21
  46. package/catalog/frontmcp-setup/references/project-structure-nx.md +246 -0
  47. package/catalog/frontmcp-setup/references/project-structure-standalone.md +212 -0
  48. package/catalog/{setup/setup-project/SKILL.md → frontmcp-setup/references/setup-project.md} +62 -62
  49. package/catalog/{setup/setup-redis/SKILL.md → frontmcp-setup/references/setup-redis.md} +59 -86
  50. package/catalog/{setup/setup-sqlite/SKILL.md → frontmcp-setup/references/setup-sqlite.md} +64 -76
  51. package/catalog/frontmcp-testing/SKILL.md +121 -0
  52. package/catalog/{testing/setup-testing/SKILL.md → frontmcp-testing/references/setup-testing.md} +78 -67
  53. package/catalog/{testing/setup-testing → frontmcp-testing}/references/test-tool-unit.md +1 -0
  54. package/catalog/skills-manifest.json +34 -383
  55. package/package.json +1 -1
  56. package/src/manifest.d.ts +3 -3
  57. package/src/manifest.js +1 -3
  58. package/src/manifest.js.map +1 -1
  59. package/catalog/adapters/create-adapter/SKILL.md +0 -127
  60. package/catalog/adapters/official-adapters/SKILL.md +0 -136
  61. package/catalog/auth/configure-auth/SKILL.md +0 -250
  62. package/catalog/auth/configure-session/SKILL.md +0 -201
  63. package/catalog/config/configure-elicitation/SKILL.md +0 -136
  64. package/catalog/config/configure-http/SKILL.md +0 -167
  65. package/catalog/config/configure-throttle/SKILL.md +0 -189
  66. package/catalog/config/configure-transport/SKILL.md +0 -151
  67. package/catalog/deployment/build-for-browser/SKILL.md +0 -95
  68. package/catalog/deployment/build-for-cli/SKILL.md +0 -100
  69. package/catalog/deployment/deploy-to-cloudflare/SKILL.md +0 -192
  70. package/catalog/deployment/deploy-to-vercel/SKILL.md +0 -196
  71. package/catalog/deployment/deploy-to-vercel/references/vercel.json.example +0 -60
  72. package/catalog/setup/frontmcp-skills-usage/SKILL.md +0 -200
  73. package/catalog/setup/project-structure-nx/SKILL.md +0 -186
  74. package/catalog/setup/project-structure-standalone/SKILL.md +0 -153
  75. /package/catalog/{auth/configure-auth/references/auth-modes.md → frontmcp-config/references/configure-auth-modes.md} +0 -0
  76. /package/catalog/{config/configure-throttle/references/guard-config.md → frontmcp-config/references/configure-throttle-guard-config.md} +0 -0
  77. /package/catalog/{config/configure-transport/references/protocol-presets.md → frontmcp-config/references/configure-transport-protocol-presets.md} +0 -0
  78. /package/catalog/{development/create-tool/references/tool-annotations.md → frontmcp-development/references/create-tool-annotations.md} +0 -0
  79. /package/catalog/{development/create-tool/references/output-schema-types.md → frontmcp-development/references/create-tool-output-schema-types.md} +0 -0
  80. /package/catalog/{testing/setup-testing → frontmcp-testing}/references/test-auth.md +0 -0
  81. /package/catalog/{testing/setup-testing → frontmcp-testing}/references/test-browser-build.md +0 -0
  82. /package/catalog/{testing/setup-testing → frontmcp-testing}/references/test-cli-binary.md +0 -0
  83. /package/catalog/{testing/setup-testing → frontmcp-testing}/references/test-direct-client.md +0 -0
  84. /package/catalog/{testing/setup-testing → frontmcp-testing}/references/test-e2e-handler.md +0 -0
@@ -0,0 +1,205 @@
1
+ # Configuring HTTP Options
2
+
3
+ Configure the HTTP server — port, CORS policy, unix sockets, and entry path prefix.
4
+
5
+ ## When to Use This Skill
6
+
7
+ ### Must Use
8
+
9
+ - Changing the default HTTP port or binding to a specific network interface
10
+ - Enabling or restricting CORS for a frontend application that calls the MCP server
11
+ - Binding to a unix socket for local daemon or process-manager integrations
12
+
13
+ ### Recommended
14
+
15
+ - Mounting the MCP server under a URL prefix behind a reverse proxy
16
+ - Setting a dynamic port from an environment variable for container deployments
17
+ - Fine-tuning CORS preflight caching for performance-sensitive frontends
18
+
19
+ ### Skip When
20
+
21
+ - Using stdio transport only with no HTTP listener -- no HTTP options apply
22
+ - Only need rate limiting or IP filtering without changing HTTP binding -- use `configure-throttle`
23
+ - Need to configure TLS/HTTPS termination -- handle at the reverse proxy or load balancer level, not in FrontMCP
24
+
25
+ > **Decision:** Use this skill when you need to customize how the HTTP listener binds (port, socket, prefix) or how it handles CORS; skip if the default port 3001 with permissive CORS is sufficient.
26
+
27
+ ## HttpOptionsInput
28
+
29
+ ```typescript
30
+ @FrontMcp({
31
+ info: { name: 'my-server', version: '1.0.0' },
32
+ apps: [MyApp],
33
+ http: {
34
+ port: 3001, // default: 3001
35
+ entryPath: '', // default: '' (root)
36
+ socketPath: undefined, // unix socket path (overrides port)
37
+ cors: {
38
+ // default: permissive (all origins)
39
+ origin: ['https://myapp.com'],
40
+ credentials: true,
41
+ maxAge: 86400,
42
+ },
43
+ },
44
+ })
45
+ class Server {}
46
+ ```
47
+
48
+ ## Port Configuration
49
+
50
+ ```typescript
51
+ // Default: port 3001
52
+ http: {
53
+ port: 3001;
54
+ }
55
+
56
+ // Use environment variable
57
+ http: {
58
+ port: Number(process.env.PORT) || 3001;
59
+ }
60
+
61
+ // Random port (useful for testing)
62
+ http: {
63
+ port: 0;
64
+ }
65
+ ```
66
+
67
+ ## CORS Configuration
68
+
69
+ ### Permissive (Default)
70
+
71
+ When `cors` is not specified, the server allows all origins without credentials:
72
+
73
+ ```typescript
74
+ // All origins allowed (default behavior)
75
+ http: {
76
+ }
77
+ ```
78
+
79
+ ### Restrict to Specific Origins
80
+
81
+ ```typescript
82
+ http: {
83
+ cors: {
84
+ origin: ['https://myapp.com', 'https://staging.myapp.com'],
85
+ credentials: true,
86
+ maxAge: 86400, // Cache preflight for 24 hours
87
+ },
88
+ }
89
+ ```
90
+
91
+ ### Disable CORS Entirely
92
+
93
+ ```typescript
94
+ http: {
95
+ cors: false, // No CORS headers at all
96
+ }
97
+ ```
98
+
99
+ ### Dynamic Origin
100
+
101
+ ```typescript
102
+ http: {
103
+ cors: {
104
+ origin: (origin: string) => {
105
+ // Allow any *.myapp.com subdomain
106
+ return origin.endsWith('.myapp.com');
107
+ },
108
+ credentials: true,
109
+ },
110
+ }
111
+ ```
112
+
113
+ ### CORS Fields
114
+
115
+ | Field | Type | Default | Description |
116
+ | ------------- | ------------------------------------------- | ------------ | ---------------------------------- |
117
+ | `origin` | `boolean \| string \| string[] \| function` | `true` (all) | Allowed origins |
118
+ | `credentials` | `boolean` | `false` | Allow cookies/auth headers |
119
+ | `maxAge` | `number` | — | Preflight cache duration (seconds) |
120
+
121
+ ## Entry Path Prefix
122
+
123
+ Mount the MCP server under a URL prefix:
124
+
125
+ ```typescript
126
+ http: {
127
+ entryPath: '/api/mcp',
128
+ }
129
+ // Server endpoints become: /api/mcp/sse, /api/mcp/, etc.
130
+ ```
131
+
132
+ Useful when running behind a reverse proxy or alongside other services.
133
+
134
+ ## Unix Socket Mode
135
+
136
+ Bind to a unix socket instead of a TCP port for local-only access:
137
+
138
+ ```typescript
139
+ http: {
140
+ socketPath: '/tmp/my-mcp-server.sock',
141
+ }
142
+ ```
143
+
144
+ - Mutually exclusive with `port` — if `socketPath` is set, `port` is ignored
145
+ - Use for local daemons, CLI tools, and process manager integrations
146
+ - Combine with `sqlite` for fully local deployments
147
+
148
+ ## Verification
149
+
150
+ ```bash
151
+ # Start with custom port
152
+ PORT=8080 frontmcp dev
153
+
154
+ # Test CORS
155
+ curl -v -H "Origin: https://myapp.com" http://localhost:8080/
156
+
157
+ # Test unix socket
158
+ curl --unix-socket /tmp/my-mcp-server.sock http://localhost/
159
+ ```
160
+
161
+ ## Common Patterns
162
+
163
+ | Pattern | Correct | Incorrect | Why |
164
+ | --------------------- | ------------------------------------------------------------ | -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
165
+ | Port from environment | `port: Number(process.env.PORT) \|\| 3001` | `port: process.env.PORT` | The `port` field expects a number; passing a string causes a silent bind failure |
166
+ | CORS with credentials | `cors: { origin: ['https://myapp.com'], credentials: true }` | `cors: { origin: true, credentials: true }` | Browsers reject `Access-Control-Allow-Origin: *` when credentials are enabled; you must list explicit origins |
167
+ | Unix socket mode | `socketPath: '/tmp/my-mcp.sock'` with no `port` field | Setting both `socketPath` and `port` | When `socketPath` is set, `port` is silently ignored which can cause confusion during debugging |
168
+ | Entry path prefix | `entryPath: '/api/mcp'` (no trailing slash) | `entryPath: '/api/mcp/'` with trailing slash | Trailing slashes cause double-slash issues in route matching (e.g., `/api/mcp//sse`) |
169
+ | Disabling CORS | `cors: false` | Omitting the `cors` field entirely | Omitting `cors` applies permissive defaults (all origins allowed); set `false` explicitly to send no CORS headers |
170
+
171
+ ## Verification Checklist
172
+
173
+ ### Configuration
174
+
175
+ - [ ] `http` block is present in the `@FrontMcp` decorator metadata
176
+ - [ ] Port value is a number (not a string) and falls within a valid range (0-65535)
177
+ - [ ] If `socketPath` is set, `port` is removed or commented out to avoid confusion
178
+ - [ ] `entryPath` does not have a trailing slash
179
+
180
+ ### CORS
181
+
182
+ - [ ] If `credentials: true`, `origin` lists explicit allowed origins (not `true` or `*`)
183
+ - [ ] `maxAge` is set to a reasonable value for production (e.g., `86400` for 24 hours)
184
+ - [ ] Dynamic origin function handles `undefined` origin (non-browser requests)
185
+
186
+ ### Runtime
187
+
188
+ - [ ] Server starts and binds to the expected port or socket path
189
+ - [ ] `curl -v -H "Origin: <your-origin>" <url>` returns correct `Access-Control-Allow-Origin`
190
+ - [ ] Preflight `OPTIONS` requests return `204` with expected CORS headers
191
+
192
+ ## Troubleshooting
193
+
194
+ | Problem | Cause | Solution |
195
+ | ------------------------------------------------ | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------- |
196
+ | `EADDRINUSE` on startup | Another process is already using the configured port | Change the port, stop the other process, or use `port: 0` for a random available port |
197
+ | CORS errors in the browser console | Origin not included in the `cors.origin` list or `credentials: true` with wildcard origin | Add the frontend origin to the `origin` array and ensure credentials and origin settings are compatible |
198
+ | Unix socket file not created | Missing write permissions on the target directory or stale socket file from a previous run | Check directory permissions and remove the stale `.sock` file before restarting |
199
+ | Routes return 404 after setting `entryPath` | Client is still requesting the root path without the prefix | Update client base URL to include the entry path (e.g., `http://localhost:3001/api/mcp`) |
200
+ | Server binds but external clients cannot connect | Server bound to `localhost` or `127.0.0.1` inside a container | Set `host: '0.0.0.0'` or use Docker port mapping to expose the container port |
201
+
202
+ ## Reference
203
+
204
+ - [HTTP Server Docs](https://docs.agentfront.dev/frontmcp/deployment/local-dev-server)
205
+ - Related skills: `configure-throttle`, `configure-transport`, `setup-redis`, `setup-project`
@@ -0,0 +1,205 @@
1
+ # Configure Session Management
2
+
3
+ This skill covers setting up session storage in FrontMCP. Sessions track authenticated user state, token storage, and request context across MCP interactions.
4
+
5
+ ## When to Use This Skill
6
+
7
+ ### Must Use
8
+
9
+ - Deploying to production where sessions must survive process restarts (Redis or Vercel KV required)
10
+ - Running multiple server instances behind a load balancer that need shared session state
11
+ - Using Streamable HTTP transport where sessions must persist across reconnects
12
+
13
+ ### Recommended
14
+
15
+ - Configuring session TTL to match your workload pattern (interactive, agent, CI/CD)
16
+ - Namespacing session keys with a unique `keyPrefix` when sharing a Redis instance across multiple servers
17
+ - Setting up Vercel KV for serverless deployments on the Vercel platform
18
+
19
+ ### Skip When
20
+
21
+ - Running a single-instance local development server -- the default in-memory store is sufficient
22
+ - Using stdio transport only where session persistence is not needed
23
+ - Need to provision Redis itself rather than configure sessions -- use `setup-redis` first, then return here
24
+
25
+ > **Decision:** Use this skill to choose and configure a session storage provider (memory, Redis, or Vercel KV) and tune TTL and key prefix settings; use `setup-redis` if Redis is not yet provisioned.
26
+
27
+ ## Storage Providers
28
+
29
+ | Provider | Use Case | Persistence | Package Required |
30
+ | ----------- | ------------------- | ----------- | ---------------- |
31
+ | `memory` | Development/testing | None | None (default) |
32
+ | `redis` | Node.js production | Yes | `ioredis` |
33
+ | `vercel-kv` | Vercel deployments | Yes | `@vercel/kv` |
34
+
35
+ Never use the memory store in production. Sessions are lost on process restart, which breaks authentication for all connected clients.
36
+
37
+ ## Redis (Production)
38
+
39
+ Configure Redis session storage via the `@FrontMcp` decorator:
40
+
41
+ ```typescript
42
+ import { FrontMcp, App } from '@frontmcp/sdk';
43
+
44
+ @App({ name: 'MyApp' })
45
+ class MyApp {}
46
+
47
+ @FrontMcp({
48
+ info: { name: 'my-server', version: '1.0.0' },
49
+ apps: [MyApp],
50
+ redis: {
51
+ provider: 'redis',
52
+ host: process.env['REDIS_HOST'] ?? 'localhost',
53
+ port: Number(process.env['REDIS_PORT'] ?? 6379),
54
+ password: process.env['REDIS_PASSWORD'],
55
+ },
56
+ })
57
+ class MyServer {}
58
+ ```
59
+
60
+ The SDK internally calls `createSessionStore()` to create a `RedisSessionStore`. The factory lazy-loads `ioredis` so it is not bundled when you use a different provider.
61
+
62
+ ## Vercel KV
63
+
64
+ For Vercel deployments, use the `vercel-kv` provider. Credentials are read from environment variables set automatically by the Vercel platform:
65
+
66
+ ```typescript
67
+ @FrontMcp({
68
+ info: { name: 'my-server', version: '1.0.0' },
69
+ apps: [MyApp],
70
+ redis: { provider: 'vercel-kv' },
71
+ })
72
+ class MyServer {}
73
+ ```
74
+
75
+ Required environment variables (auto-injected when a KV store is linked to your Vercel project):
76
+
77
+ | Variable | Description |
78
+ | ------------------- | ------------------------------ |
79
+ | `KV_REST_API_URL` | Vercel KV REST endpoint |
80
+ | `KV_REST_API_TOKEN` | Vercel KV authentication token |
81
+
82
+ ## Memory (Development Default)
83
+
84
+ When no Redis or KV configuration is provided, the SDK falls back to an in-memory store. This is suitable only for development:
85
+
86
+ ```typescript
87
+ @FrontMcp({
88
+ info: { name: 'my-server', version: '1.0.0' },
89
+ apps: [MyApp],
90
+ // No redis config -- defaults to memory
91
+ })
92
+ class MyServer {}
93
+ ```
94
+
95
+ ## Key Prefix
96
+
97
+ All persistent stores support a `keyPrefix` option that namespaces session keys. This is important when multiple FrontMCP servers share the same Redis instance:
98
+
99
+ ```typescript
100
+ @FrontMcp({
101
+ info: { name: 'billing-server', version: '1.0.0' },
102
+ apps: [MyApp],
103
+ redis: {
104
+ provider: 'redis',
105
+ host: 'shared-redis.internal',
106
+ port: 6379,
107
+ keyPrefix: 'billing-mcp:session:',
108
+ },
109
+ })
110
+ class BillingServer {}
111
+ ```
112
+
113
+ Use a unique prefix per server to prevent session key collisions.
114
+
115
+ ## TTL Configuration
116
+
117
+ The `defaultTtlMs` option controls how long sessions live before expiring:
118
+
119
+ | Scenario | Recommended TTL |
120
+ | ---------------------------- | ----------------------- |
121
+ | Interactive user sessions | `3_600_000` (1 hour) |
122
+ | Long-running agent workflows | `86_400_000` (24 hours) |
123
+ | Short-lived CI/CD operations | `600_000` (10 minutes) |
124
+
125
+ ```typescript
126
+ @FrontMcp({
127
+ info: { name: 'my-server', version: '1.0.0' },
128
+ apps: [MyApp],
129
+ redis: {
130
+ provider: 'redis',
131
+ host: 'localhost',
132
+ port: 6379,
133
+ defaultTtlMs: 86_400_000, // 24 hours for agent workflows
134
+ },
135
+ })
136
+ class MyServer {}
137
+ ```
138
+
139
+ ## Pub/Sub for Resource Subscriptions
140
+
141
+ If your server uses resource subscriptions (clients subscribe to resource change notifications), you need a pub/sub channel. Vercel KV does not support pub/sub, so you must use Redis for the pub/sub channel even when using Vercel KV for sessions:
142
+
143
+ ```typescript
144
+ import { createSessionStore, createPubsubStore } from '@frontmcp/sdk/auth/session';
145
+
146
+ // Sessions in Vercel KV
147
+ const sessionStore = await createSessionStore({
148
+ provider: 'vercel-kv',
149
+ url: process.env['KV_REST_API_URL'],
150
+ token: process.env['KV_REST_API_TOKEN'],
151
+ });
152
+
153
+ // Pub/sub requires Redis
154
+ const pubsubStore = createPubsubStore({
155
+ provider: 'redis',
156
+ host: process.env['REDIS_HOST'] ?? 'localhost',
157
+ port: 6379,
158
+ });
159
+ ```
160
+
161
+ ## Common Patterns
162
+
163
+ | Pattern | Correct | Incorrect | Why |
164
+ | ---------------------- | -------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
165
+ | Store construction | Use `createSessionStore()` factory function | `new RedisSessionStore(client)` direct construction | The factory handles lazy-loading, key prefix normalization, and provider detection automatically |
166
+ | Vercel KV creation | `const store = await createSessionStore({ provider: 'vercel-kv' })` | `const store = createSessionStore({ provider: 'vercel-kv' })` without `await` | The factory is async for Vercel KV; forgetting `await` uses the store before its connection is ready |
167
+ | Key prefix per server | `keyPrefix: 'billing-mcp:session:'` unique per server | Same `keyPrefix` across multiple servers sharing one Redis instance | Shared prefixes cause session key collisions; one server may read or overwrite another's sessions |
168
+ | Production storage | `redis: { provider: 'redis', host: '...' }` or `redis: { provider: 'vercel-kv' }` | Omitting redis config in production (falls back to memory) | Memory sessions vanish on restart; all connected clients must re-authenticate and in-flight workflows are lost |
169
+ | Pub/sub with Vercel KV | Separate `pubsub` config pointing to real Redis alongside `redis: { provider: 'vercel-kv' }` | Expecting Vercel KV to handle pub/sub | Vercel KV does not support pub/sub operations; a real Redis instance is required for resource subscriptions |
170
+
171
+ ## Verification Checklist
172
+
173
+ ### Configuration
174
+
175
+ - [ ] `redis` block is present in the `@FrontMcp` decorator with a valid `provider` field (`'redis'` or `'vercel-kv'`)
176
+ - [ ] `keyPrefix` is unique per server when sharing a Redis instance
177
+ - [ ] `defaultTtlMs` matches the workload pattern (1 hour for interactive, 24 hours for agents, 10 minutes for CI/CD)
178
+
179
+ ### Vercel KV
180
+
181
+ - [ ] `provider: 'vercel-kv'` is set in the `redis` config
182
+ - [ ] `KV_REST_API_URL` and `KV_REST_API_TOKEN` environment variables are present (auto-injected on Vercel)
183
+ - [ ] A separate `pubsub` config pointing to real Redis is provided if resource subscriptions are used
184
+
185
+ ### Runtime
186
+
187
+ - [ ] Server starts without Redis connection errors in the logs
188
+ - [ ] `redis-cli keys "mcp:session:*"` shows session keys after an MCP request (for Redis provider)
189
+ - [ ] Sessions persist across server restarts (for Redis/Vercel KV providers)
190
+ - [ ] Sessions expire after the configured TTL
191
+
192
+ ## Troubleshooting
193
+
194
+ | Problem | Cause | Solution |
195
+ | -------------------------------------- | -------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
196
+ | Sessions lost after server restart | Using the default in-memory store in production | Configure `redis: { provider: 'redis' }` or `redis: { provider: 'vercel-kv' }` for persistence |
197
+ | `ECONNREFUSED` on startup | Redis is not running or host/port is incorrect | Start the Redis container (`docker compose up -d redis`) or verify connection details |
198
+ | Vercel KV `401 Unauthorized` | Missing or invalid KV tokens | Check `KV_REST_API_URL` and `KV_REST_API_TOKEN` in the Vercel dashboard and redeploy |
199
+ | Session key collisions between servers | Multiple servers share the same Redis instance and `keyPrefix` | Set a unique `keyPrefix` per server (e.g., `billing-mcp:session:`, `api-mcp:session:`) |
200
+ | Pub/sub not working with Vercel KV | Vercel KV does not support pub/sub operations | Add a separate `pubsub` config pointing to a real Redis instance |
201
+
202
+ ## Reference
203
+
204
+ - [Session Storage Docs](https://docs.agentfront.dev/frontmcp/deployment/redis-setup)
205
+ - Related skills: `setup-redis`, `configure-auth`, `configure-transport`, `configure-elicitation`
@@ -0,0 +1,229 @@
1
+ # Configuring Throttle, Rate Limits, and IP Filtering
2
+
3
+ Protect your FrontMCP server with rate limiting, concurrency control, execution timeouts, and IP filtering — at both server and per-tool levels.
4
+
5
+ ## When to Use This Skill
6
+
7
+ ### Must Use
8
+
9
+ - Deploying a server to production where abuse protection and rate limiting are required
10
+ - Exposing expensive or destructive tools that need concurrency caps and execution timeouts
11
+ - Restricting access by IP address with allow/deny lists for compliance or security
12
+
13
+ ### Recommended
14
+
15
+ - Enforcing per-session or per-IP request quotas to ensure fair resource distribution
16
+ - Adding global concurrency limits to prevent server overload under burst traffic
17
+ - Configuring distributed rate limiting across multiple server instances with Redis
18
+
19
+ ### Skip When
20
+
21
+ - Running a local development server with stdio transport only -- throttle adds unnecessary overhead
22
+ - Only need CORS or port configuration without rate limiting -- use `configure-http`
23
+ - Need authentication or session management rather than rate limiting -- use `configure-session` or `configure-auth`
24
+
25
+ > **Decision:** Use this skill when your server needs protection against abuse, rate limiting, concurrency control, IP filtering, or execution timeouts at either the server or per-tool level.
26
+
27
+ ## Server-Level Throttle (GuardConfig)
28
+
29
+ ```typescript
30
+ @FrontMcp({
31
+ info: { name: 'my-server', version: '1.0.0' },
32
+ apps: [MyApp],
33
+ throttle: {
34
+ enabled: true,
35
+
36
+ // Global rate limit (all requests combined)
37
+ global: {
38
+ maxRequests: 1000,
39
+ windowMs: 60000, // 1 minute window
40
+ partitionBy: 'global', // shared across all clients
41
+ },
42
+
43
+ // Global concurrency limit
44
+ globalConcurrency: {
45
+ maxConcurrent: 50,
46
+ partitionBy: 'global',
47
+ },
48
+
49
+ // Default limits for individual tools (applied unless tool overrides)
50
+ defaultRateLimit: {
51
+ maxRequests: 100,
52
+ windowMs: 60000,
53
+ },
54
+ defaultConcurrency: {
55
+ maxConcurrent: 10,
56
+ },
57
+ defaultTimeout: {
58
+ executeMs: 30000, // 30 second timeout
59
+ },
60
+
61
+ // IP filtering
62
+ ipFilter: {
63
+ allowList: ['10.0.0.0/8', '172.16.0.0/12'], // CIDR ranges
64
+ denyList: ['192.168.1.100'],
65
+ defaultAction: 'allow', // 'allow' | 'deny'
66
+ trustProxy: true, // trust X-Forwarded-For
67
+ trustedProxyDepth: 1, // proxy depth to trust
68
+ },
69
+ },
70
+ })
71
+ class Server {}
72
+ ```
73
+
74
+ ## Per-Tool Rate Limiting
75
+
76
+ Override server defaults on individual tools:
77
+
78
+ ```typescript
79
+ @Tool({
80
+ name: 'expensive_query',
81
+ description: 'Run an expensive database query',
82
+ inputSchema: {
83
+ query: z.string(),
84
+ },
85
+ outputSchema: { rows: z.array(z.record(z.unknown())) },
86
+
87
+ // Per-tool limits
88
+ rateLimit: {
89
+ maxRequests: 10,
90
+ windowMs: 60000,
91
+ partitionBy: 'session', // per-session rate limit
92
+ },
93
+ concurrency: {
94
+ maxConcurrent: 3,
95
+ queueTimeoutMs: 5000, // wait up to 5s for a slot
96
+ partitionBy: 'session',
97
+ },
98
+ timeout: {
99
+ executeMs: 60000, // 60 second timeout for this tool
100
+ },
101
+ })
102
+ class ExpensiveQueryTool extends ToolContext {
103
+ async execute(input: { query: string }) {
104
+ const db = this.get(DB_TOKEN);
105
+ return { rows: await db.query(input.query) };
106
+ }
107
+ }
108
+ ```
109
+
110
+ ## Configuration Types
111
+
112
+ ### RateLimitConfig
113
+
114
+ | Field | Type | Default | Description |
115
+ | ------------- | ------------------------------- | ---------- | ------------------------- |
116
+ | `maxRequests` | `number` | — | Max requests per window |
117
+ | `windowMs` | `number` | `60000` | Window duration in ms |
118
+ | `partitionBy` | `'global' \| 'ip' \| 'session'` | `'global'` | How to partition counters |
119
+
120
+ ### ConcurrencyConfig
121
+
122
+ | Field | Type | Default | Description |
123
+ | ---------------- | ------------------------------- | ---------- | -------------------------------------------------- |
124
+ | `maxConcurrent` | `number` | — | Max simultaneous executions |
125
+ | `queueTimeoutMs` | `number` | `0` | How long to wait for a slot (0 = fail immediately) |
126
+ | `partitionBy` | `'global' \| 'ip' \| 'session'` | `'global'` | How to partition counters |
127
+
128
+ ### TimeoutConfig
129
+
130
+ | Field | Type | Default | Description |
131
+ | ----------- | -------- | ------- | ------------------------ |
132
+ | `executeMs` | `number` | — | Max execution time in ms |
133
+
134
+ ### IpFilterConfig
135
+
136
+ | Field | Type | Default | Description |
137
+ | ------------------- | ------------------- | --------- | ----------------------------------- |
138
+ | `allowList` | `string[]` | — | Allowed IPs or CIDR ranges |
139
+ | `denyList` | `string[]` | — | Blocked IPs or CIDR ranges |
140
+ | `defaultAction` | `'allow' \| 'deny'` | `'allow'` | Action when IP matches neither list |
141
+ | `trustProxy` | `boolean` | `false` | Trust X-Forwarded-For header |
142
+ | `trustedProxyDepth` | `number` | `1` | How many proxy hops to trust |
143
+
144
+ ## Partition Strategies
145
+
146
+ - **`'global'`** — Single shared counter for all clients. Use for global capacity limits.
147
+ - **`'ip'`** — Separate counter per client IP. Use for per-client rate limiting.
148
+ - **`'session'`** — Separate counter per MCP session. Use for per-session fairness.
149
+
150
+ ## Distributed Rate Limiting
151
+
152
+ For multi-instance deployments, configure Redis storage in the guard:
153
+
154
+ ```typescript
155
+ throttle: {
156
+ enabled: true,
157
+ storage: {
158
+ type: 'redis',
159
+ redis: { config: { host: 'redis.internal', port: 6379 } },
160
+ },
161
+ global: { maxRequests: 1000, windowMs: 60000 },
162
+ }
163
+ ```
164
+
165
+ ## Verification
166
+
167
+ ```bash
168
+ # Start server
169
+ frontmcp dev
170
+
171
+ # Test rate limiting (send 101 requests rapidly)
172
+ for i in $(seq 1 101); do
173
+ curl -s -o /dev/null -w "%{http_code}\n" -X POST http://localhost:3001/ \
174
+ -H 'Content-Type: application/json' \
175
+ -d '{"jsonrpc":"2.0","method":"tools/list","id":1}'
176
+ done
177
+ # Should see 429 responses after limit is exceeded
178
+ ```
179
+
180
+ ## Common Patterns
181
+
182
+ | Pattern | Correct | Incorrect | Why |
183
+ | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
184
+ | Per-tool override | Set `rateLimit` on the `@Tool` decorator to override server defaults | Duplicating the full server-level `throttle` config inside each tool | Per-tool config merges with server defaults; only specify the fields you want to override |
185
+ | Partition strategy | Use `partitionBy: 'session'` for per-user fairness on shared tools | Using `partitionBy: 'global'` for all limits | Global partitioning means one abusive client can exhaust the quota for everyone |
186
+ | Distributed rate limiting | Configure `storage: { type: 'redis', redis: { config: { host, port } } }` in the throttle block for multi-instance deployments | Relying on in-memory counters with multiple server instances | In-memory counters are per-process; each instance tracks limits independently, allowing N times the intended rate |
187
+ | IP filter ordering | Set `defaultAction: 'deny'` with an explicit `allowList` for strict environments | Setting `defaultAction: 'allow'` with only a `denyList` | A deny-by-default posture is safer; new unknown IPs are blocked until explicitly allowed |
188
+ | Concurrency queue timeout | Set `queueTimeoutMs` on concurrency config to queue excess requests briefly | Setting `queueTimeoutMs: 0` on expensive tools | Zero timeout immediately rejects excess requests instead of briefly queuing them, causing unnecessary failures during short bursts |
189
+
190
+ ## Verification Checklist
191
+
192
+ ### Configuration
193
+
194
+ - [ ] `throttle.enabled` is set to `true` in the `@FrontMcp` decorator
195
+ - [ ] `global.maxRequests` and `global.windowMs` are set to reasonable production values
196
+ - [ ] `defaultTimeout.executeMs` is configured to prevent runaway tool executions
197
+ - [ ] IP filter `defaultAction` matches your security posture (`allow` for open, `deny` for restricted)
198
+
199
+ ### Per-Tool
200
+
201
+ - [ ] Expensive or destructive tools have explicit `rateLimit` and `concurrency` overrides
202
+ - [ ] `partitionBy` is set to `'session'` or `'ip'` for tools that need per-client fairness
203
+ - [ ] `queueTimeoutMs` is set on concurrency-limited tools to handle brief bursts
204
+
205
+ ### Distributed
206
+
207
+ - [ ] Redis storage is configured in the throttle block for multi-instance deployments
208
+ - [ ] Redis connection is verified before deploying (see `setup-redis`)
209
+
210
+ ### Runtime
211
+
212
+ - [ ] Sending requests beyond the rate limit returns HTTP 429
213
+ - [ ] Blocked IPs receive HTTP 403
214
+ - [ ] Tool executions that exceed `executeMs` are terminated and return a timeout error
215
+
216
+ ## Troubleshooting
217
+
218
+ | Problem | Cause | Solution |
219
+ | ----------------------------------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------- |
220
+ | Rate limits not enforced across instances | In-memory storage used with multiple server replicas | Configure `storage: { type: 'redis' }` in the throttle block to share counters |
221
+ | All requests rejected with 403 | `ipFilter.defaultAction` set to `'deny'` without any `allowList` entries | Add the allowed IP ranges to `allowList` or change `defaultAction` to `'allow'` |
222
+ | Tools timing out unexpectedly | `defaultTimeout.executeMs` too low for the tool's normal execution time | Increase the global default or set a per-tool `timeout.executeMs` override |
223
+ | `X-Forwarded-For` header ignored | `ipFilter.trustProxy` not enabled or `trustedProxyDepth` too low | Set `trustProxy: true` and adjust `trustedProxyDepth` to match your proxy chain |
224
+ | Rate limit resets not aligned with expectations | `windowMs` misunderstood as a sliding window when it is a fixed window | The window is fixed; all counters reset at the end of each `windowMs` interval |
225
+
226
+ ## Reference
227
+
228
+ - [Guard Configuration Docs](https://docs.agentfront.dev/frontmcp/servers/guard)
229
+ - Related skills: `configure-http`, `configure-transport`, `setup-redis`, `configure-auth`