@frontmcp/skills 0.0.1 → 1.0.0-beta.11

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 (104) hide show
  1. package/README.md +2 -2
  2. package/catalog/TEMPLATE.md +58 -13
  3. package/catalog/frontmcp-config/SKILL.md +156 -0
  4. package/catalog/{auth/configure-auth/references/auth-modes.md → frontmcp-config/references/configure-auth-modes.md} +5 -0
  5. package/catalog/frontmcp-config/references/configure-auth.md +243 -0
  6. package/catalog/frontmcp-config/references/configure-elicitation.md +183 -0
  7. package/catalog/frontmcp-config/references/configure-http.md +210 -0
  8. package/catalog/frontmcp-config/references/configure-session.md +210 -0
  9. package/catalog/{config/configure-throttle/references/guard-config.md → frontmcp-config/references/configure-throttle-guard-config.md} +5 -0
  10. package/catalog/frontmcp-config/references/configure-throttle.md +234 -0
  11. package/catalog/{config/configure-transport/references/protocol-presets.md → frontmcp-config/references/configure-transport-protocol-presets.md} +5 -0
  12. package/catalog/frontmcp-config/references/configure-transport.md +200 -0
  13. package/catalog/frontmcp-config/references/setup-redis.md +9 -0
  14. package/catalog/frontmcp-config/references/setup-sqlite.md +9 -0
  15. package/catalog/frontmcp-deployment/SKILL.md +152 -0
  16. package/catalog/frontmcp-deployment/references/build-for-browser.md +143 -0
  17. package/catalog/frontmcp-deployment/references/build-for-cli.md +191 -0
  18. package/catalog/{deployment/build-for-sdk/SKILL.md → frontmcp-deployment/references/build-for-sdk.md} +66 -20
  19. package/catalog/frontmcp-deployment/references/deploy-to-cloudflare.md +218 -0
  20. package/catalog/{deployment/deploy-to-lambda/SKILL.md → frontmcp-deployment/references/deploy-to-lambda.md} +77 -59
  21. package/catalog/{deployment/deploy-to-node/references/Dockerfile.example → frontmcp-deployment/references/deploy-to-node-dockerfile.md} +18 -4
  22. package/catalog/{deployment/deploy-to-node/SKILL.md → frontmcp-deployment/references/deploy-to-node.md} +69 -36
  23. package/catalog/frontmcp-deployment/references/deploy-to-vercel-config.md +65 -0
  24. package/catalog/frontmcp-deployment/references/deploy-to-vercel.md +229 -0
  25. package/catalog/frontmcp-development/SKILL.md +126 -0
  26. package/catalog/frontmcp-development/references/create-adapter.md +170 -0
  27. package/catalog/{development/create-agent/references/llm-config.md → frontmcp-development/references/create-agent-llm-config.md} +10 -5
  28. package/catalog/{development/create-agent/SKILL.md → frontmcp-development/references/create-agent.md} +83 -40
  29. package/catalog/{development/create-job/SKILL.md → frontmcp-development/references/create-job.md} +62 -15
  30. package/catalog/{plugins/create-plugin-hooks/SKILL.md → frontmcp-development/references/create-plugin-hooks.md} +100 -7
  31. package/catalog/frontmcp-development/references/create-plugin.md +506 -0
  32. package/catalog/{development/create-prompt/SKILL.md → frontmcp-development/references/create-prompt.md} +65 -22
  33. package/catalog/{development/create-provider/SKILL.md → frontmcp-development/references/create-provider.md} +63 -23
  34. package/catalog/{development/create-resource/SKILL.md → frontmcp-development/references/create-resource.md} +148 -26
  35. package/catalog/{development/create-skill-with-tools/SKILL.md → frontmcp-development/references/create-skill-with-tools.md} +174 -20
  36. package/catalog/{development/create-skill/SKILL.md → frontmcp-development/references/create-skill.md} +114 -28
  37. package/catalog/{development/create-tool/references/tool-annotations.md → frontmcp-development/references/create-tool-annotations.md} +5 -0
  38. package/catalog/{development/create-tool/references/output-schema-types.md → frontmcp-development/references/create-tool-output-schema-types.md} +5 -0
  39. package/catalog/{development/create-tool/SKILL.md → frontmcp-development/references/create-tool.md} +172 -23
  40. package/catalog/{development/create-workflow/SKILL.md → frontmcp-development/references/create-workflow.md} +61 -14
  41. package/catalog/frontmcp-development/references/decorators-guide.md +754 -0
  42. package/catalog/frontmcp-development/references/official-adapters.md +199 -0
  43. package/catalog/{plugins/official-plugins/SKILL.md → frontmcp-development/references/official-plugins.md} +97 -27
  44. package/catalog/frontmcp-extensibility/SKILL.md +103 -0
  45. package/catalog/frontmcp-extensibility/references/vectoriadb.md +289 -0
  46. package/catalog/frontmcp-guides/SKILL.md +420 -0
  47. package/catalog/frontmcp-guides/references/example-knowledge-base.md +641 -0
  48. package/catalog/frontmcp-guides/references/example-task-manager.md +517 -0
  49. package/catalog/frontmcp-guides/references/example-weather-api.md +297 -0
  50. package/catalog/frontmcp-production-readiness/SKILL.md +98 -0
  51. package/catalog/frontmcp-production-readiness/references/common-checklist.md +156 -0
  52. package/catalog/frontmcp-production-readiness/references/production-browser.md +46 -0
  53. package/catalog/frontmcp-production-readiness/references/production-cli-binary.md +62 -0
  54. package/catalog/frontmcp-production-readiness/references/production-cli-daemon.md +61 -0
  55. package/catalog/frontmcp-production-readiness/references/production-cloudflare.md +52 -0
  56. package/catalog/frontmcp-production-readiness/references/production-lambda.md +53 -0
  57. package/catalog/frontmcp-production-readiness/references/production-node-sdk.md +66 -0
  58. package/catalog/frontmcp-production-readiness/references/production-node-server.md +61 -0
  59. package/catalog/frontmcp-production-readiness/references/production-vercel.md +52 -0
  60. package/catalog/frontmcp-setup/SKILL.md +132 -0
  61. package/catalog/frontmcp-setup/references/frontmcp-skills-usage.md +280 -0
  62. package/catalog/{setup/multi-app-composition/SKILL.md → frontmcp-setup/references/multi-app-composition.md} +66 -19
  63. package/catalog/{setup/nx-workflow/SKILL.md → frontmcp-setup/references/nx-workflow.md} +79 -17
  64. package/catalog/frontmcp-setup/references/project-structure-nx.md +251 -0
  65. package/catalog/frontmcp-setup/references/project-structure-standalone.md +217 -0
  66. package/catalog/frontmcp-setup/references/readme-guide.md +226 -0
  67. package/catalog/{setup/setup-project/SKILL.md → frontmcp-setup/references/setup-project.md} +63 -58
  68. package/catalog/{setup/setup-redis/SKILL.md → frontmcp-setup/references/setup-redis.md} +60 -82
  69. package/catalog/{setup/setup-sqlite/SKILL.md → frontmcp-setup/references/setup-sqlite.md} +65 -72
  70. package/catalog/frontmcp-testing/SKILL.md +135 -0
  71. package/catalog/{testing/setup-testing/SKILL.md → frontmcp-testing/references/setup-testing.md} +79 -63
  72. package/catalog/{testing/setup-testing → frontmcp-testing}/references/test-auth.md +5 -0
  73. package/catalog/{testing/setup-testing → frontmcp-testing}/references/test-browser-build.md +5 -0
  74. package/catalog/{testing/setup-testing → frontmcp-testing}/references/test-cli-binary.md +5 -0
  75. package/catalog/{testing/setup-testing → frontmcp-testing}/references/test-direct-client.md +5 -0
  76. package/catalog/{testing/setup-testing → frontmcp-testing}/references/test-e2e-handler.md +5 -0
  77. package/catalog/{testing/setup-testing → frontmcp-testing}/references/test-tool-unit.md +6 -0
  78. package/catalog/skills-manifest.json +337 -382
  79. package/package.json +2 -2
  80. package/src/index.d.ts +1 -1
  81. package/src/index.js.map +1 -1
  82. package/src/loader.js +0 -1
  83. package/src/loader.js.map +1 -1
  84. package/src/manifest.d.ts +15 -3
  85. package/src/manifest.js +3 -3
  86. package/src/manifest.js.map +1 -1
  87. package/catalog/adapters/create-adapter/SKILL.md +0 -127
  88. package/catalog/adapters/official-adapters/SKILL.md +0 -136
  89. package/catalog/auth/configure-auth/SKILL.md +0 -250
  90. package/catalog/auth/configure-session/SKILL.md +0 -201
  91. package/catalog/config/configure-elicitation/SKILL.md +0 -136
  92. package/catalog/config/configure-http/SKILL.md +0 -167
  93. package/catalog/config/configure-throttle/SKILL.md +0 -189
  94. package/catalog/config/configure-transport/SKILL.md +0 -151
  95. package/catalog/deployment/build-for-browser/SKILL.md +0 -95
  96. package/catalog/deployment/build-for-cli/SKILL.md +0 -100
  97. package/catalog/deployment/deploy-to-cloudflare/SKILL.md +0 -192
  98. package/catalog/deployment/deploy-to-vercel/SKILL.md +0 -196
  99. package/catalog/deployment/deploy-to-vercel/references/vercel.json.example +0 -60
  100. package/catalog/development/decorators-guide/SKILL.md +0 -598
  101. package/catalog/plugins/create-plugin/SKILL.md +0 -336
  102. package/catalog/setup/frontmcp-skills-usage/SKILL.md +0 -200
  103. package/catalog/setup/project-structure-nx/SKILL.md +0 -186
  104. package/catalog/setup/project-structure-standalone/SKILL.md +0 -153
@@ -0,0 +1,517 @@
1
+ ---
2
+ name: example-task-manager
3
+ description: Authenticated task management server with CRUD tools, Redis storage, OAuth, and Vercel deployment
4
+ ---
5
+
6
+ # Example: Task Manager (Intermediate)
7
+
8
+ > Skills used: setup-project, create-tool, create-provider, configure-auth, configure-session, setup-redis, setup-testing, deploy-to-vercel
9
+
10
+ An authenticated task management MCP server with CRUD tools, a Redis-backed provider for storage, OAuth authentication, and Vercel deployment. Demonstrates DI with tokens, session management, per-user data isolation, and authenticated E2E testing.
11
+
12
+ ---
13
+
14
+ ## Project Setup
15
+
16
+ ```jsonc
17
+ // package.json
18
+ {
19
+ "name": "task-manager",
20
+ "version": "1.0.0",
21
+ "private": true,
22
+ "scripts": {
23
+ "build": "frontmcp build --target vercel",
24
+ "start": "frontmcp start",
25
+ "test": "jest --coverage",
26
+ },
27
+ "dependencies": {
28
+ "@frontmcp/sdk": "^1.0.0",
29
+ "ioredis": "^5.4.0",
30
+ "zod": "^3.23.0",
31
+ },
32
+ "devDependencies": {
33
+ "@frontmcp/testing": "^1.0.0",
34
+ "jest": "^29.0.0",
35
+ "ts-jest": "^29.0.0",
36
+ "typescript": "^5.4.0",
37
+ },
38
+ }
39
+ ```
40
+
41
+ ---
42
+
43
+ ## Server Entry Point
44
+
45
+ ```typescript
46
+ // src/main.ts
47
+ import { FrontMcp } from '@frontmcp/sdk';
48
+ import { TasksApp } from './tasks.app';
49
+
50
+ @FrontMcp({
51
+ info: { name: 'task-manager', version: '1.0.0' },
52
+ apps: [TasksApp],
53
+ auth: { mode: 'remote', provider: 'https://auth.example.com', clientId: 'my-client-id' },
54
+ redis: { provider: 'redis', host: process.env.REDIS_URL ?? 'localhost' },
55
+ })
56
+ export default class TaskManagerServer {}
57
+ ```
58
+
59
+ ---
60
+
61
+ ## App Registration
62
+
63
+ ```typescript
64
+ // src/tasks.app.ts
65
+ import { App } from '@frontmcp/sdk';
66
+ import { RedisTaskStoreProvider } from './providers/task-store.provider';
67
+ import { CreateTaskTool } from './tools/create-task.tool';
68
+ import { ListTasksTool } from './tools/list-tasks.tool';
69
+ import { UpdateTaskTool } from './tools/update-task.tool';
70
+ import { DeleteTaskTool } from './tools/delete-task.tool';
71
+
72
+ @App({
73
+ name: 'Tasks',
74
+ description: 'Task management with CRUD operations',
75
+ providers: [RedisTaskStoreProvider],
76
+ tools: [CreateTaskTool, ListTasksTool, UpdateTaskTool, DeleteTaskTool],
77
+ })
78
+ export class TasksApp {}
79
+ ```
80
+
81
+ ---
82
+
83
+ ## Shared Types
84
+
85
+ ```typescript
86
+ // src/types/task.ts
87
+ export interface Task {
88
+ id: string;
89
+ title: string;
90
+ priority: 'low' | 'medium' | 'high';
91
+ status: 'pending' | 'in_progress' | 'done';
92
+ userId: string;
93
+ createdAt: string;
94
+ }
95
+ ```
96
+
97
+ ---
98
+
99
+ ## Provider: Redis Task Store
100
+
101
+ ```typescript
102
+ // src/providers/task-store.provider.ts
103
+ import { Provider } from '@frontmcp/sdk';
104
+ import type { Token } from '@frontmcp/di';
105
+ import type { Task } from '../types/task';
106
+
107
+ export interface TaskStore {
108
+ create(task: Omit<Task, 'id' | 'createdAt'>): Promise<Task>;
109
+ list(userId: string): Promise<Task[]>;
110
+ update(id: string, userId: string, data: Partial<Pick<Task, 'title' | 'priority' | 'status'>>): Promise<Task>;
111
+ delete(id: string, userId: string): Promise<void>;
112
+ }
113
+
114
+ export const TASK_STORE: Token<TaskStore> = Symbol('TaskStore');
115
+
116
+ @Provider({ token: TASK_STORE })
117
+ export class RedisTaskStoreProvider implements TaskStore {
118
+ private redis!: import('ioredis').default;
119
+
120
+ async onInit(): Promise<void> {
121
+ const Redis = (await import('ioredis')).default;
122
+ this.redis = new Redis(process.env.REDIS_URL ?? 'redis://localhost:6379');
123
+ }
124
+
125
+ async create(input: Omit<Task, 'id' | 'createdAt'>): Promise<Task> {
126
+ const { randomUUID } = await import('@frontmcp/utils');
127
+ const task: Task = {
128
+ ...input,
129
+ id: randomUUID(),
130
+ createdAt: new Date().toISOString(),
131
+ };
132
+ await this.redis.hset(`tasks:${task.userId}`, task.id, JSON.stringify(task));
133
+ return task;
134
+ }
135
+
136
+ async list(userId: string): Promise<Task[]> {
137
+ const entries = await this.redis.hgetall(`tasks:${userId}`);
138
+ return Object.values(entries).map((v) => JSON.parse(v) as Task);
139
+ }
140
+
141
+ async update(id: string, userId: string, data: Partial<Pick<Task, 'title' | 'priority' | 'status'>>): Promise<Task> {
142
+ const raw = await this.redis.hget(`tasks:${userId}`, id);
143
+ if (!raw) {
144
+ throw new Error(`Task not found: ${id}`);
145
+ }
146
+ const task: Task = { ...(JSON.parse(raw) as Task), ...data };
147
+ await this.redis.hset(`tasks:${userId}`, id, JSON.stringify(task));
148
+ return task;
149
+ }
150
+
151
+ async delete(id: string, userId: string): Promise<void> {
152
+ const removed = await this.redis.hdel(`tasks:${userId}`, id);
153
+ if (removed === 0) {
154
+ throw new Error(`Task not found: ${id}`);
155
+ }
156
+ }
157
+
158
+ async onDestroy(): Promise<void> {
159
+ await this.redis.quit();
160
+ }
161
+ }
162
+ ```
163
+
164
+ ---
165
+
166
+ ## Tool: Create Task
167
+
168
+ ```typescript
169
+ // src/tools/create-task.tool.ts
170
+ import { Tool, ToolContext } from '@frontmcp/sdk';
171
+ import { z } from 'zod';
172
+ import { TASK_STORE } from '../providers/task-store.provider';
173
+
174
+ @Tool({
175
+ name: 'create_task',
176
+ description: 'Create a new task for the authenticated user',
177
+ inputSchema: {
178
+ title: z.string().min(1).max(200).describe('Task title'),
179
+ priority: z.enum(['low', 'medium', 'high']).default('medium').describe('Task priority'),
180
+ },
181
+ outputSchema: {
182
+ id: z.string(),
183
+ title: z.string(),
184
+ priority: z.string(),
185
+ status: z.string(),
186
+ createdAt: z.string(),
187
+ },
188
+ })
189
+ export class CreateTaskTool extends ToolContext {
190
+ async execute(input: { title: string; priority: 'low' | 'medium' | 'high' }) {
191
+ const store = this.get(TASK_STORE);
192
+ const userId = this.context.session?.userId;
193
+
194
+ if (!userId) {
195
+ this.fail(new Error('Authentication required'));
196
+ }
197
+
198
+ const task = await store.create({
199
+ title: input.title,
200
+ priority: input.priority,
201
+ status: 'pending',
202
+ userId,
203
+ });
204
+
205
+ return {
206
+ id: task.id,
207
+ title: task.title,
208
+ priority: task.priority,
209
+ status: task.status,
210
+ createdAt: task.createdAt,
211
+ };
212
+ }
213
+ }
214
+ ```
215
+
216
+ ---
217
+
218
+ ## Tool: List Tasks
219
+
220
+ ```typescript
221
+ // src/tools/list-tasks.tool.ts
222
+ import { Tool, ToolContext } from '@frontmcp/sdk';
223
+ import { z } from 'zod';
224
+ import { TASK_STORE } from '../providers/task-store.provider';
225
+
226
+ @Tool({
227
+ name: 'list_tasks',
228
+ description: 'List all tasks for the authenticated user',
229
+ inputSchema: {
230
+ status: z.enum(['pending', 'in_progress', 'done']).optional().describe('Filter by status'),
231
+ },
232
+ outputSchema: {
233
+ tasks: z.array(
234
+ z.object({
235
+ id: z.string(),
236
+ title: z.string(),
237
+ priority: z.string(),
238
+ status: z.string(),
239
+ createdAt: z.string(),
240
+ }),
241
+ ),
242
+ total: z.number(),
243
+ },
244
+ })
245
+ export class ListTasksTool extends ToolContext {
246
+ async execute(input: { status?: 'pending' | 'in_progress' | 'done' }) {
247
+ const store = this.get(TASK_STORE);
248
+ const userId = this.context.session?.userId;
249
+
250
+ if (!userId) {
251
+ this.fail(new Error('Authentication required'));
252
+ }
253
+
254
+ let tasks = await store.list(userId);
255
+
256
+ if (input.status) {
257
+ tasks = tasks.filter((t) => t.status === input.status);
258
+ }
259
+
260
+ return {
261
+ tasks: tasks.map((t) => ({
262
+ id: t.id,
263
+ title: t.title,
264
+ priority: t.priority,
265
+ status: t.status,
266
+ createdAt: t.createdAt,
267
+ })),
268
+ total: tasks.length,
269
+ };
270
+ }
271
+ }
272
+ ```
273
+
274
+ ---
275
+
276
+ ## Tool: Update Task
277
+
278
+ ```typescript
279
+ // src/tools/update-task.tool.ts
280
+ import { Tool, ToolContext } from '@frontmcp/sdk';
281
+ import { z } from 'zod';
282
+ import { TASK_STORE } from '../providers/task-store.provider';
283
+
284
+ @Tool({
285
+ name: 'update_task',
286
+ description: 'Update the status or priority of an existing task',
287
+ inputSchema: {
288
+ id: z.string().min(1).describe('Task ID to update'),
289
+ status: z.enum(['pending', 'in_progress', 'done']).optional().describe('New status'),
290
+ priority: z.enum(['low', 'medium', 'high']).optional().describe('New priority'),
291
+ },
292
+ outputSchema: {
293
+ id: z.string(),
294
+ title: z.string(),
295
+ priority: z.string(),
296
+ status: z.string(),
297
+ },
298
+ })
299
+ export class UpdateTaskTool extends ToolContext {
300
+ async execute(input: {
301
+ id: string;
302
+ status?: 'pending' | 'in_progress' | 'done';
303
+ priority?: 'low' | 'medium' | 'high';
304
+ }) {
305
+ const store = this.get(TASK_STORE);
306
+ const userId = this.context.session?.userId;
307
+
308
+ if (!userId) {
309
+ this.fail(new Error('Authentication required'));
310
+ }
311
+
312
+ try {
313
+ const updated = await store.update(input.id, userId, {
314
+ ...(input.status && { status: input.status }),
315
+ ...(input.priority && { priority: input.priority }),
316
+ });
317
+
318
+ return {
319
+ id: updated.id,
320
+ title: updated.title,
321
+ priority: updated.priority,
322
+ status: updated.status,
323
+ };
324
+ } catch (err) {
325
+ this.fail(new Error(`Failed to update task: ${String(err)}`));
326
+ }
327
+ }
328
+ }
329
+ ```
330
+
331
+ ---
332
+
333
+ ## Tool: Delete Task
334
+
335
+ ```typescript
336
+ // src/tools/delete-task.tool.ts
337
+ import { Tool, ToolContext } from '@frontmcp/sdk';
338
+ import { z } from 'zod';
339
+ import { TASK_STORE } from '../providers/task-store.provider';
340
+
341
+ @Tool({
342
+ name: 'delete_task',
343
+ description: 'Delete a task by ID',
344
+ inputSchema: {
345
+ id: z.string().min(1).describe('Task ID to delete'),
346
+ },
347
+ outputSchema: {
348
+ deleted: z.boolean(),
349
+ id: z.string(),
350
+ },
351
+ })
352
+ export class DeleteTaskTool extends ToolContext {
353
+ async execute(input: { id: string }) {
354
+ const store = this.get(TASK_STORE);
355
+ const userId = this.context.session?.userId;
356
+
357
+ if (!userId) {
358
+ this.fail(new Error('Authentication required'));
359
+ }
360
+
361
+ try {
362
+ await store.delete(input.id, userId);
363
+ return { deleted: true, id: input.id };
364
+ } catch (err) {
365
+ this.fail(new Error(`Failed to delete task: ${String(err)}`));
366
+ }
367
+ }
368
+ }
369
+ ```
370
+
371
+ ---
372
+
373
+ ## Vercel Deployment Config
374
+
375
+ ```jsonc
376
+ // vercel.json
377
+ {
378
+ "version": 2,
379
+ "builds": [{ "src": "api/**/*.ts", "use": "@vercel/node" }],
380
+ "routes": [{ "src": "/mcp/(.*)", "dest": "/api/mcp" }],
381
+ "env": {
382
+ "REDIS_URL": "@redis-url",
383
+ },
384
+ }
385
+ ```
386
+
387
+ ---
388
+
389
+ ## Unit Test: CreateTaskTool
390
+
391
+ ```typescript
392
+ // test/create-task.tool.spec.ts
393
+ import { ToolContext } from '@frontmcp/sdk';
394
+ import { CreateTaskTool } from '../src/tools/create-task.tool';
395
+ import { TASK_STORE, type TaskStore } from '../src/providers/task-store.provider';
396
+ import type { Task } from '../src/types/task';
397
+
398
+ describe('CreateTaskTool', () => {
399
+ let tool: CreateTaskTool;
400
+ let mockStore: jest.Mocked<TaskStore>;
401
+
402
+ beforeEach(() => {
403
+ tool = new CreateTaskTool();
404
+ mockStore = {
405
+ create: jest.fn(),
406
+ list: jest.fn(),
407
+ update: jest.fn(),
408
+ delete: jest.fn(),
409
+ };
410
+ });
411
+
412
+ function applyContext(userId: string | undefined): void {
413
+ const ctx = {
414
+ get: jest.fn((token: symbol) => {
415
+ if (token === TASK_STORE) return mockStore;
416
+ throw new Error(`Unknown token: ${String(token)}`);
417
+ }),
418
+ tryGet: jest.fn(),
419
+ fail: jest.fn((err: Error) => {
420
+ throw err;
421
+ }),
422
+ mark: jest.fn(),
423
+ notify: jest.fn(),
424
+ respondProgress: jest.fn(),
425
+ context: { session: userId ? { userId } : undefined },
426
+ } as unknown as ToolContext;
427
+ Object.assign(tool, ctx);
428
+ }
429
+
430
+ it('should create a task for an authenticated user', async () => {
431
+ const mockTask: Task = {
432
+ id: 'task-001',
433
+ title: 'Write tests',
434
+ priority: 'high',
435
+ status: 'pending',
436
+ userId: 'user-123',
437
+ createdAt: '2026-03-27T10:00:00.000Z',
438
+ };
439
+ mockStore.create.mockResolvedValue(mockTask);
440
+ applyContext('user-123');
441
+
442
+ const result = await tool.execute({ title: 'Write tests', priority: 'high' });
443
+
444
+ expect(result).toEqual({
445
+ id: 'task-001',
446
+ title: 'Write tests',
447
+ priority: 'high',
448
+ status: 'pending',
449
+ createdAt: '2026-03-27T10:00:00.000Z',
450
+ });
451
+ expect(mockStore.create).toHaveBeenCalledWith({
452
+ title: 'Write tests',
453
+ priority: 'high',
454
+ status: 'pending',
455
+ userId: 'user-123',
456
+ });
457
+ });
458
+
459
+ it('should fail when user is not authenticated', async () => {
460
+ applyContext(undefined);
461
+
462
+ await expect(tool.execute({ title: 'Write tests', priority: 'medium' })).rejects.toThrow('Authentication required');
463
+ });
464
+ });
465
+ ```
466
+
467
+ ---
468
+
469
+ ## E2E Test: Task Manager
470
+
471
+ ```typescript
472
+ // test/tasks.e2e.spec.ts
473
+ import { McpTestClient, TestServer, TestTokenFactory } from '@frontmcp/testing';
474
+ import Server from '../src/main';
475
+
476
+ describe('Task Manager E2E', () => {
477
+ let client: McpTestClient;
478
+ let server: TestServer;
479
+
480
+ beforeAll(async () => {
481
+ server = await TestServer.start({ command: 'npx tsx src/main.ts' });
482
+ const tokenFactory = new TestTokenFactory();
483
+ const token = await tokenFactory.createTestToken({ sub: 'user-e2e', scopes: ['tasks'] });
484
+ client = await McpTestClient.create({ baseUrl: server.info.baseUrl }).withToken(token).buildAndConnect();
485
+ });
486
+
487
+ afterAll(async () => {
488
+ await client.disconnect();
489
+ await server.stop();
490
+ });
491
+
492
+ it('should list all CRUD tools', async () => {
493
+ const { tools } = await client.listTools();
494
+ const names = tools.map((t) => t.name);
495
+
496
+ expect(names).toContain('create_task');
497
+ expect(names).toContain('list_tasks');
498
+ expect(names).toContain('update_task');
499
+ expect(names).toContain('delete_task');
500
+ });
501
+
502
+ it('should create and list a task', async () => {
503
+ const createResult = await client.callTool('create_task', {
504
+ title: 'E2E test task',
505
+ priority: 'high',
506
+ });
507
+ expect(createResult).toBeSuccessful();
508
+
509
+ const listResult = await client.callTool('list_tasks', {});
510
+ expect(listResult).toBeSuccessful();
511
+
512
+ const parsed = JSON.parse(listResult.content[0].text);
513
+ expect(parsed.tasks.length).toBeGreaterThan(0);
514
+ expect(parsed.tasks.some((t: { title: string }) => t.title === 'E2E test task')).toBe(true);
515
+ });
516
+ });
517
+ ```