@frontmcp/skills 0.0.1 → 1.0.0-beta.10

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