@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.
- package/catalog/TEMPLATE.md +58 -13
- package/catalog/frontmcp-config/SKILL.md +140 -0
- package/catalog/frontmcp-config/references/configure-auth.md +238 -0
- package/catalog/frontmcp-config/references/configure-elicitation.md +178 -0
- package/catalog/frontmcp-config/references/configure-http.md +205 -0
- package/catalog/frontmcp-config/references/configure-session.md +205 -0
- package/catalog/frontmcp-config/references/configure-throttle.md +229 -0
- package/catalog/frontmcp-config/references/configure-transport.md +195 -0
- package/catalog/frontmcp-config/references/setup-redis.md +4 -0
- package/catalog/frontmcp-config/references/setup-sqlite.md +4 -0
- package/catalog/frontmcp-deployment/SKILL.md +124 -0
- package/catalog/frontmcp-deployment/references/build-for-browser.md +138 -0
- package/catalog/frontmcp-deployment/references/build-for-cli.md +138 -0
- package/catalog/{deployment/build-for-sdk/SKILL.md → frontmcp-deployment/references/build-for-sdk.md} +65 -24
- package/catalog/frontmcp-deployment/references/deploy-to-cloudflare.md +213 -0
- package/catalog/{deployment/deploy-to-lambda/SKILL.md → frontmcp-deployment/references/deploy-to-lambda.md} +73 -60
- package/catalog/{deployment/deploy-to-node/references/Dockerfile.example → frontmcp-deployment/references/deploy-to-node-dockerfile.md} +11 -2
- package/catalog/{deployment/deploy-to-node/SKILL.md → frontmcp-deployment/references/deploy-to-node.md} +65 -37
- package/catalog/frontmcp-deployment/references/deploy-to-vercel-config.md +60 -0
- package/catalog/frontmcp-deployment/references/deploy-to-vercel.md +224 -0
- package/catalog/frontmcp-development/SKILL.md +118 -0
- package/catalog/frontmcp-development/references/create-adapter.md +165 -0
- package/catalog/{development/create-agent/references/llm-config.md → frontmcp-development/references/create-agent-llm-config.md} +5 -5
- package/catalog/{development/create-agent/SKILL.md → frontmcp-development/references/create-agent.md} +82 -44
- package/catalog/{development/create-job/SKILL.md → frontmcp-development/references/create-job.md} +61 -19
- package/catalog/{plugins/create-plugin-hooks/SKILL.md → frontmcp-development/references/create-plugin-hooks.md} +63 -11
- package/catalog/{plugins/create-plugin/SKILL.md → frontmcp-development/references/create-plugin.md} +65 -60
- package/catalog/{development/create-prompt/SKILL.md → frontmcp-development/references/create-prompt.md} +62 -26
- package/catalog/{development/create-provider/SKILL.md → frontmcp-development/references/create-provider.md} +62 -27
- package/catalog/{development/create-resource/SKILL.md → frontmcp-development/references/create-resource.md} +62 -30
- package/catalog/{development/create-skill-with-tools/SKILL.md → frontmcp-development/references/create-skill-with-tools.md} +69 -24
- package/catalog/{development/create-skill/SKILL.md → frontmcp-development/references/create-skill.md} +71 -20
- package/catalog/{development/create-tool/SKILL.md → frontmcp-development/references/create-tool.md} +62 -26
- package/catalog/{development/create-workflow/SKILL.md → frontmcp-development/references/create-workflow.md} +60 -18
- package/catalog/{development/decorators-guide/SKILL.md → frontmcp-development/references/decorators-guide.md} +123 -34
- package/catalog/frontmcp-development/references/official-adapters.md +194 -0
- package/catalog/{plugins/official-plugins/SKILL.md → frontmcp-development/references/official-plugins.md} +68 -22
- package/catalog/frontmcp-guides/SKILL.md +417 -0
- package/catalog/frontmcp-guides/references/example-knowledge-base.md +636 -0
- package/catalog/frontmcp-guides/references/example-task-manager.md +512 -0
- package/catalog/frontmcp-guides/references/example-weather-api.md +292 -0
- package/catalog/frontmcp-setup/SKILL.md +127 -0
- package/catalog/frontmcp-setup/references/frontmcp-skills-usage.md +265 -0
- package/catalog/{setup/multi-app-composition/SKILL.md → frontmcp-setup/references/multi-app-composition.md} +65 -23
- package/catalog/{setup/nx-workflow/SKILL.md → frontmcp-setup/references/nx-workflow.md} +78 -21
- package/catalog/frontmcp-setup/references/project-structure-nx.md +246 -0
- package/catalog/frontmcp-setup/references/project-structure-standalone.md +212 -0
- package/catalog/{setup/setup-project/SKILL.md → frontmcp-setup/references/setup-project.md} +62 -62
- package/catalog/{setup/setup-redis/SKILL.md → frontmcp-setup/references/setup-redis.md} +59 -86
- package/catalog/{setup/setup-sqlite/SKILL.md → frontmcp-setup/references/setup-sqlite.md} +64 -76
- package/catalog/frontmcp-testing/SKILL.md +121 -0
- package/catalog/{testing/setup-testing/SKILL.md → frontmcp-testing/references/setup-testing.md} +78 -67
- package/catalog/{testing/setup-testing → frontmcp-testing}/references/test-tool-unit.md +1 -0
- package/catalog/skills-manifest.json +34 -383
- package/package.json +1 -1
- package/src/manifest.d.ts +3 -3
- package/src/manifest.js +1 -3
- package/src/manifest.js.map +1 -1
- package/catalog/adapters/create-adapter/SKILL.md +0 -127
- package/catalog/adapters/official-adapters/SKILL.md +0 -136
- package/catalog/auth/configure-auth/SKILL.md +0 -250
- package/catalog/auth/configure-session/SKILL.md +0 -201
- package/catalog/config/configure-elicitation/SKILL.md +0 -136
- package/catalog/config/configure-http/SKILL.md +0 -167
- package/catalog/config/configure-throttle/SKILL.md +0 -189
- package/catalog/config/configure-transport/SKILL.md +0 -151
- package/catalog/deployment/build-for-browser/SKILL.md +0 -95
- package/catalog/deployment/build-for-cli/SKILL.md +0 -100
- package/catalog/deployment/deploy-to-cloudflare/SKILL.md +0 -192
- package/catalog/deployment/deploy-to-vercel/SKILL.md +0 -196
- package/catalog/deployment/deploy-to-vercel/references/vercel.json.example +0 -60
- package/catalog/setup/frontmcp-skills-usage/SKILL.md +0 -200
- package/catalog/setup/project-structure-nx/SKILL.md +0 -186
- package/catalog/setup/project-structure-standalone/SKILL.md +0 -153
- /package/catalog/{auth/configure-auth/references/auth-modes.md → frontmcp-config/references/configure-auth-modes.md} +0 -0
- /package/catalog/{config/configure-throttle/references/guard-config.md → frontmcp-config/references/configure-throttle-guard-config.md} +0 -0
- /package/catalog/{config/configure-transport/references/protocol-presets.md → frontmcp-config/references/configure-transport-protocol-presets.md} +0 -0
- /package/catalog/{development/create-tool/references/tool-annotations.md → frontmcp-development/references/create-tool-annotations.md} +0 -0
- /package/catalog/{development/create-tool/references/output-schema-types.md → frontmcp-development/references/create-tool-output-schema-types.md} +0 -0
- /package/catalog/{testing/setup-testing → frontmcp-testing}/references/test-auth.md +0 -0
- /package/catalog/{testing/setup-testing → frontmcp-testing}/references/test-browser-build.md +0 -0
- /package/catalog/{testing/setup-testing → frontmcp-testing}/references/test-cli-binary.md +0 -0
- /package/catalog/{testing/setup-testing → frontmcp-testing}/references/test-direct-client.md +0 -0
- /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`
|