@prmichaelsen/task-mcp 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/.env.example +19 -0
  2. package/AGENT.md +1165 -0
  3. package/CHANGELOG.md +72 -0
  4. package/agent/commands/acp.commit.md +511 -0
  5. package/agent/commands/acp.init.md +376 -0
  6. package/agent/commands/acp.package-install.md +347 -0
  7. package/agent/commands/acp.proceed.md +311 -0
  8. package/agent/commands/acp.report.md +392 -0
  9. package/agent/commands/acp.status.md +280 -0
  10. package/agent/commands/acp.sync.md +323 -0
  11. package/agent/commands/acp.update.md +301 -0
  12. package/agent/commands/acp.validate.md +385 -0
  13. package/agent/commands/acp.version-check-for-updates.md +275 -0
  14. package/agent/commands/acp.version-check.md +190 -0
  15. package/agent/commands/acp.version-update.md +288 -0
  16. package/agent/commands/command.template.md +273 -0
  17. package/agent/commands/git.commit.md +511 -0
  18. package/agent/commands/git.init.md +513 -0
  19. package/agent/design/.gitkeep +0 -0
  20. package/agent/design/acp-task-execution-requirements.md +555 -0
  21. package/agent/design/api-dto-design.md +394 -0
  22. package/agent/design/code-extraction-guide.md +827 -0
  23. package/agent/design/design.template.md +136 -0
  24. package/agent/design/requirements.template.md +387 -0
  25. package/agent/design/rest-api-integration.md +489 -0
  26. package/agent/design/sdk-export-requirements.md +549 -0
  27. package/agent/milestones/.gitkeep +0 -0
  28. package/agent/milestones/milestone-1-{title}.template.md +206 -0
  29. package/agent/milestones/milestone-2-task-infrastructure.md +232 -0
  30. package/agent/milestones/milestone-4-autonomous-execution.md +235 -0
  31. package/agent/patterns/.gitkeep +0 -0
  32. package/agent/patterns/bootstrap.md +1271 -0
  33. package/agent/patterns/bootstrap.template.md +1237 -0
  34. package/agent/patterns/pattern.template.md +364 -0
  35. package/agent/progress.template.yaml +158 -0
  36. package/agent/progress.yaml +375 -0
  37. package/agent/scripts/check-for-updates.sh +88 -0
  38. package/agent/scripts/install.sh +157 -0
  39. package/agent/scripts/uninstall.sh +75 -0
  40. package/agent/scripts/update.sh +139 -0
  41. package/agent/scripts/version.sh +35 -0
  42. package/agent/tasks/.gitkeep +0 -0
  43. package/agent/tasks/task-1-{title}.template.md +225 -0
  44. package/agent/tasks/task-86-task-data-model-schemas.md +143 -0
  45. package/agent/tasks/task-87-task-database-service.md +220 -0
  46. package/agent/tasks/task-88-firebase-client-wrapper.md +139 -0
  47. package/agent/tasks/task-88-task-execution-engine.md +277 -0
  48. package/agent/tasks/task-89-mcp-server-implementation.md +197 -0
  49. package/agent/tasks/task-90-build-configuration.md +146 -0
  50. package/agent/tasks/task-91-deployment-configuration.md +128 -0
  51. package/coverage/base.css +224 -0
  52. package/coverage/block-navigation.js +87 -0
  53. package/coverage/favicon.png +0 -0
  54. package/coverage/index.html +191 -0
  55. package/coverage/lcov-report/base.css +224 -0
  56. package/coverage/lcov-report/block-navigation.js +87 -0
  57. package/coverage/lcov-report/favicon.png +0 -0
  58. package/coverage/lcov-report/index.html +191 -0
  59. package/coverage/lcov-report/prettify.css +1 -0
  60. package/coverage/lcov-report/prettify.js +2 -0
  61. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  62. package/coverage/lcov-report/sorter.js +210 -0
  63. package/coverage/lcov-report/src/client.ts.html +1030 -0
  64. package/coverage/lcov-report/src/constant/collections.ts.html +469 -0
  65. package/coverage/lcov-report/src/constant/index.html +116 -0
  66. package/coverage/lcov-report/src/dto/index.html +116 -0
  67. package/coverage/lcov-report/src/dto/transformers.ts.html +568 -0
  68. package/coverage/lcov-report/src/index.html +146 -0
  69. package/coverage/lcov-report/src/schemas/index.html +116 -0
  70. package/coverage/lcov-report/src/schemas/task.ts.html +547 -0
  71. package/coverage/lcov-report/src/server-factory.ts.html +418 -0
  72. package/coverage/lcov-report/src/server.ts.html +289 -0
  73. package/coverage/lcov-report/src/services/index.html +116 -0
  74. package/coverage/lcov-report/src/services/task-database.service.ts.html +1495 -0
  75. package/coverage/lcov-report/src/tools/index.html +236 -0
  76. package/coverage/lcov-report/src/tools/index.ts.html +292 -0
  77. package/coverage/lcov-report/src/tools/task-add-message.ts.html +277 -0
  78. package/coverage/lcov-report/src/tools/task-complete-task-item.ts.html +343 -0
  79. package/coverage/lcov-report/src/tools/task-create-milestone.ts.html +286 -0
  80. package/coverage/lcov-report/src/tools/task-create-task-item.ts.html +358 -0
  81. package/coverage/lcov-report/src/tools/task-get-next-step.ts.html +460 -0
  82. package/coverage/lcov-report/src/tools/task-get-status.ts.html +316 -0
  83. package/coverage/lcov-report/src/tools/task-report-completion.ts.html +343 -0
  84. package/coverage/lcov-report/src/tools/task-update-progress.ts.html +232 -0
  85. package/coverage/lcov.info +974 -0
  86. package/coverage/prettify.css +1 -0
  87. package/coverage/prettify.js +2 -0
  88. package/coverage/sort-arrow-sprite.png +0 -0
  89. package/coverage/sorter.js +210 -0
  90. package/coverage/src/client.ts.html +1030 -0
  91. package/coverage/src/constant/collections.ts.html +469 -0
  92. package/coverage/src/constant/index.html +116 -0
  93. package/coverage/src/dto/index.html +116 -0
  94. package/coverage/src/dto/transformers.ts.html +568 -0
  95. package/coverage/src/index.html +146 -0
  96. package/coverage/src/schemas/index.html +116 -0
  97. package/coverage/src/schemas/task.ts.html +547 -0
  98. package/coverage/src/server-factory.ts.html +418 -0
  99. package/coverage/src/server.ts.html +289 -0
  100. package/coverage/src/services/index.html +116 -0
  101. package/coverage/src/services/task-database.service.ts.html +1495 -0
  102. package/coverage/src/tools/index.html +236 -0
  103. package/coverage/src/tools/index.ts.html +292 -0
  104. package/coverage/src/tools/task-add-message.ts.html +277 -0
  105. package/coverage/src/tools/task-complete-task-item.ts.html +343 -0
  106. package/coverage/src/tools/task-create-milestone.ts.html +286 -0
  107. package/coverage/src/tools/task-create-task-item.ts.html +358 -0
  108. package/coverage/src/tools/task-get-next-step.ts.html +460 -0
  109. package/coverage/src/tools/task-get-status.ts.html +316 -0
  110. package/coverage/src/tools/task-report-completion.ts.html +343 -0
  111. package/coverage/src/tools/task-update-progress.ts.html +232 -0
  112. package/firestore.rules +95 -0
  113. package/jest.config.js +31 -0
  114. package/package.json +67 -0
  115. package/src/client.spec.ts +199 -0
  116. package/src/client.ts +315 -0
  117. package/src/constant/collections.ts +128 -0
  118. package/src/dto/index.ts +47 -0
  119. package/src/dto/task-api.dto.ts +219 -0
  120. package/src/dto/transformers.spec.ts +462 -0
  121. package/src/dto/transformers.ts +161 -0
  122. package/src/schemas/task.ts +154 -0
  123. package/src/server-factory.spec.ts +70 -0
  124. package/src/server-factory.ts +111 -0
  125. package/src/server.ts +68 -0
  126. package/src/services/task-database.service.e2e.ts +116 -0
  127. package/src/services/task-database.service.spec.ts +479 -0
  128. package/src/services/task-database.service.ts +470 -0
  129. package/src/test-schemas.ts +161 -0
  130. package/src/tools/index.ts +69 -0
  131. package/src/tools/task-add-message.ts +64 -0
  132. package/src/tools/task-complete-task-item.ts +86 -0
  133. package/src/tools/task-create-milestone.ts +67 -0
  134. package/src/tools/task-create-task-item.ts +91 -0
  135. package/src/tools/task-get-next-step.spec.ts +136 -0
  136. package/src/tools/task-get-next-step.ts +125 -0
  137. package/src/tools/task-get-status.spec.ts +213 -0
  138. package/src/tools/task-get-status.ts +77 -0
  139. package/src/tools/task-report-completion.ts +86 -0
  140. package/src/tools/task-update-progress.ts +49 -0
  141. package/src/tools/tools.spec.ts +194 -0
  142. package/tsconfig.json +31 -0
@@ -0,0 +1,1237 @@
1
+ # MCP Server Bootstrap Pattern
2
+
3
+ ## Overview
4
+
5
+ This document describes the organizational patterns for bootstrapping...
6
+
7
+ ## Core Principles
8
+
9
+ Core principles...
10
+
11
+ ## Project Structure
12
+
13
+ ```
14
+ project-root/
15
+ ├── src/
16
+ │ ├── index.ts # CLI entry point (bundled)
17
+ │ ├── server.ts # Server class (for standalone)
18
+ │ ├── server-factory.ts # Factory function (for multi-tenant)
19
+ │ ├── client.ts # External API client wrapper
20
+ │ ├── types.ts # Shared type definitions
21
+ │ │
22
+ │ ├── tools/ # Tool definitions
23
+ │ │ ├── index.ts # Tool exports
24
+ │ │ ├── tool-one.ts # Individual tool (definition + handler)
25
+ │ │ ├── tool-two.ts
26
+ │ │ └── ...
27
+ │ │
28
+ │ ├── types/ # Type definitions (optional subdirectory)
29
+ │ │ ├── mcp.ts # MCP-specific types
30
+ │ │ ├── api.ts # External API types
31
+ │ │ └── ...
32
+ │ │
33
+ │ └── utils/ # Utilities
34
+ │ ├── logger.ts # Logging (stdio-safe for MCP)
35
+ │ ├── error-serializer.ts # Error handling
36
+ │ └── ...
37
+
38
+ ├── agent/ # Documentation & planning
39
+ │ ├── patterns/ # Architecture patterns
40
+ │ ├── tasks/ # Task tracking
41
+ │ └── ...
42
+
43
+ ├── package.json # Package configuration
44
+ ├── tsconfig.json # TypeScript configuration
45
+ ├── esbuild.build.js # Build script
46
+ ├── esbuild.watch.js # Watch mode script
47
+ ├── .gitignore
48
+ └── README.md
49
+ ```
50
+
51
+ ## Configuration Files
52
+
53
+ ### package.json Structure
54
+
55
+ For a simple MCP server:
56
+
57
+ ```json
58
+ {
59
+ "name": "remember-mcp",
60
+ "version": "0.1.0",
61
+ "description": "Multi-tenant memory system MCP server with vector search and relationships",
62
+ "main": "dist/server.js",
63
+ "type": "module",
64
+ "repository": {
65
+ "type": "git",
66
+ "url": "git+https://github.com/prmichaelsen/remember-mcp.git"
67
+ },
68
+ "bugs": {
69
+ "url": "https://github.com/prmichaelsen/remember-mcp/issues"
70
+ },
71
+ "homepage": "https://github.com/prmichaelsen/remember-mcp#readme",
72
+ "scripts": {
73
+ "build": "node esbuild.build.js",
74
+ "build:watch": "node esbuild.watch.js",
75
+ "clean": "rm -rf dist",
76
+ "dev": "tsx watch src/server.ts",
77
+ "start": "node dist/server.js",
78
+ "test": "jest",
79
+ "test:watch": "jest --watch",
80
+ "test:e2e": "jest --config jest.e2e.config.js",
81
+ "test:e2e:watch": "jest --config jest.e2e.config.js --watch",
82
+ "test:all": "npm test && npm run test:e2e",
83
+ "lint": "eslint src/**/*.ts",
84
+ "typecheck": "tsc --noEmit",
85
+ "prepublishOnly": "npm run clean && npm run build"
86
+ },
87
+ "keywords": [
88
+ "mcp",
89
+ "memory",
90
+ "vector-search",
91
+ "weaviate",
92
+ "firebase"
93
+ ],
94
+ "author": "Patrick Michaelsen",
95
+ "license": "MIT"
96
+ }
97
+ ```
98
+
99
+ For a library with multiple exports:
100
+
101
+ ```json
102
+ {
103
+ "name": "@scope/package-name",
104
+ "version": "1.0.0",
105
+ "description": "MCP server for [purpose]",
106
+ "type": "module",
107
+ "main": "dist/index.js",
108
+ "types": "dist/index.d.ts",
109
+
110
+ "repository": {
111
+ "type": "git",
112
+ "url": "git+https://github.com/username/repo.git"
113
+ },
114
+ "bugs": {
115
+ "url": "https://github.com/username/repo/issues"
116
+ },
117
+ "homepage": "https://github.com/username/repo#readme",
118
+
119
+ "exports": {
120
+ ".": {
121
+ "types": "./dist/index.d.ts",
122
+ "import": "./dist/index.js"
123
+ },
124
+ "./factory": {
125
+ "types": "./dist/server-factory.d.ts",
126
+ "import": "./dist/server-factory.js"
127
+ },
128
+ "./client": {
129
+ "types": "./dist/client.d.ts",
130
+ "import": "./dist/client.js"
131
+ },
132
+ "./tools": {
133
+ "types": "./dist/tools/index.d.ts",
134
+ "import": "./dist/tools/index.js"
135
+ },
136
+ "./types": {
137
+ "types": "./dist/types.d.ts",
138
+ "import": "./dist/types.js"
139
+ }
140
+ },
141
+
142
+ "files": [
143
+ "dist",
144
+ "README.md",
145
+ "LICENSE"
146
+ ],
147
+
148
+ "scripts": {
149
+ "build": "npm run build:types && npm run build:bundle",
150
+ "build:types": "tsc --emitDeclarationOnly",
151
+ "build:bundle": "node esbuild.build.js",
152
+ "build:watch": "node esbuild.watch.js",
153
+ "start": "node dist/index.js",
154
+ "clean": "rm -rf dist",
155
+ "test": "jest",
156
+ "test:watch": "jest --watch",
157
+ "test:e2e": "jest --config jest.e2e.config.js",
158
+ "test:e2e:watch": "jest --config jest.e2e.config.js --watch",
159
+ "test:all": "npm test && npm run test:e2e",
160
+ "prepublishOnly": "npm run clean && npm run build"
161
+ },
162
+
163
+ "keywords": [
164
+ "mcp",
165
+ "model-context-protocol",
166
+ "[domain-specific-keywords]"
167
+ ],
168
+
169
+ "dependencies": {
170
+ "@modelcontextprotocol/sdk": "^1.0.0"
171
+ },
172
+
173
+ "devDependencies": {
174
+ "@types/jest": "^30.0.0",
175
+ "@types/node": "^20.0.0",
176
+ "esbuild": "^0.25.0",
177
+ "jest": "^30.0.0",
178
+ "ts-jest": "^29.0.0",
179
+ "typescript": "^5.3.0"
180
+ },
181
+
182
+ "engines": {
183
+ "node": ">=18.0.0"
184
+ }
185
+ }
186
+ ```
187
+
188
+ **Key Points:**
189
+ - `"type": "module"` required for ESM
190
+ - `repository`, `bugs`, `homepage` for GitHub integration
191
+ - `main` points to the built entry file
192
+ - `exports` field for libraries with multiple entry points
193
+ - `files` array specifies what to publish to npm
194
+ - `build:watch` script for development
195
+ - `clean` script removes build artifacts
196
+ - `prepublishOnly` ensures clean build before publishing
197
+ - `test` scripts for jest (unit and e2e)
198
+ - `author` field for attribution
199
+ - `engines` specifies minimum Node.js version
200
+
201
+ ### tsconfig.json Structure
202
+
203
+ ```json
204
+ {
205
+ "compilerOptions": {
206
+ "target": "ES2022",
207
+ "module": "Node16",
208
+ "moduleResolution": "Node16",
209
+ "lib": ["ES2022"],
210
+ "types": ["node"],
211
+
212
+ "outDir": "./dist",
213
+ "rootDir": "./src",
214
+
215
+ "strict": true,
216
+ "esModuleInterop": true,
217
+ "skipLibCheck": true,
218
+ "forceConsistentCasingInFileNames": true,
219
+ "resolveJsonModule": true,
220
+
221
+ "declaration": true,
222
+ "declarationMap": true,
223
+ "sourceMap": true,
224
+
225
+ "baseUrl": ".",
226
+ "paths": {
227
+ "@/*": ["src/*"]
228
+ }
229
+ },
230
+
231
+ "include": ["src/**/*"],
232
+ "exclude": ["node_modules", "dist"]
233
+ }
234
+ ```
235
+
236
+ **Key Points:**
237
+ - `Node16` module resolution for proper ESM support
238
+ - `declaration: true` for type definitions
239
+ - `strict: true` for type safety
240
+ - Source maps for debugging
241
+ - `baseUrl` and `paths` for module name mapping (`@/` → `src/`)
242
+
243
+ ### Jest Configuration
244
+
245
+ For projects with colocated tests (`.spec.ts` and `.e2e.ts` files alongside source code):
246
+
247
+ #### jest.config.js - Unit Tests
248
+
249
+ ```javascript
250
+ module.exports = {
251
+ preset: 'ts-jest',
252
+ testEnvironment: 'node',
253
+ roots: ['<rootDir>/src'],
254
+ testMatch: ['**/*.spec.ts'],
255
+ moduleFileExtensions: ['ts', 'js'],
256
+ collectCoverage: true,
257
+ coverageDirectory: 'coverage',
258
+ coverageReporters: ['text', 'lcov', 'html'],
259
+ collectCoverageFrom: [
260
+ 'src/**/*.ts',
261
+ '!src/**/*.d.ts',
262
+ '!src/**/*.spec.ts',
263
+ '!src/**/*.e2e.ts',
264
+ '!src/index.ts', // Barrel export only
265
+ '!src/types/**/*.ts', // Type definitions only
266
+ ],
267
+ moduleNameMapper: {
268
+ '^@/(.*)$': '<rootDir>/src/$1',
269
+ },
270
+ };
271
+ ```
272
+
273
+ #### jest.e2e.config.js - E2E Tests
274
+
275
+ ```javascript
276
+ module.exports = {
277
+ preset: 'ts-jest',
278
+ testEnvironment: 'node',
279
+ testMatch: ['**/*.e2e.ts'],
280
+ testTimeout: 30000, // 30 seconds for real API calls
281
+ roots: ['<rootDir>/src'],
282
+ collectCoverageFrom: [
283
+ 'src/**/*.ts',
284
+ '!src/**/*.spec.ts',
285
+ '!src/**/*.e2e.ts',
286
+ '!src/types/**/*.ts',
287
+ '!src/index.ts',
288
+ ],
289
+ moduleNameMapper: {
290
+ '^@/(.*)$': '<rootDir>/src/$1',
291
+ },
292
+ };
293
+ ```
294
+
295
+ **Key Points:**
296
+ - Separate configs for unit tests (`.spec.ts`) and e2e tests (`.e2e.ts`)
297
+ - E2E tests have longer timeout for real API calls
298
+ - Coverage excludes test files and type definitions
299
+ - `moduleNameMapper` matches TypeScript path aliases
300
+ - Tests are colocated with source files in `src/`
301
+
302
+ **Package.json Scripts:**
303
+ ```json
304
+ {
305
+ "scripts": {
306
+ "test": "jest --config jest.config.js",
307
+ "test:e2e": "jest --config jest.e2e.config.js",
308
+ "test:watch": "jest --config jest.config.js --watch",
309
+ "test:coverage": "jest --config jest.config.js --coverage"
310
+ },
311
+ "devDependencies": {
312
+ "@types/jest": "^29.0.0",
313
+ "jest": "^29.0.0",
314
+ "ts-jest": "^29.0.0"
315
+ }
316
+ }
317
+ ```
318
+
319
+ ### esbuild.build.js Structure
320
+
321
+ For a simple MCP server (single entry point):
322
+
323
+ ```javascript
324
+ import * as esbuild from 'esbuild';
325
+
326
+ await esbuild.build({
327
+ entryPoints: ['src/server.ts'],
328
+ bundle: true,
329
+ platform: 'node',
330
+ target: 'node20',
331
+ format: 'esm',
332
+ outfile: 'dist/server.js',
333
+ sourcemap: true,
334
+ external: [
335
+ 'weaviate-client',
336
+ 'firebase-admin',
337
+ '@modelcontextprotocol/sdk'
338
+ ],
339
+ banner: {
340
+ js: "import { createRequire } from 'module'; const require = createRequire(import.meta.url);"
341
+ },
342
+ alias: {
343
+ '@': './src'
344
+ }
345
+ });
346
+
347
+ console.log('✓ Build complete');
348
+ ```
349
+
350
+ For a library with multiple entry points:
351
+
352
+ ```javascript
353
+ import * as esbuild from 'esbuild';
354
+ import { readdir } from 'fs/promises';
355
+ import { join } from 'path';
356
+
357
+ // Option 1: Find all entry points dynamically
358
+ async function findEntryPoints(dir, base = 'src') {
359
+ const entries = [];
360
+ const files = await readdir(dir, { withFileTypes: true });
361
+
362
+ for (const file of files) {
363
+ const fullPath = join(dir, file.name);
364
+ if (file.isDirectory()) {
365
+ entries.push(...await findEntryPoints(fullPath, base));
366
+ } else if (file.name.endsWith('.ts') && !file.name.endsWith('.d.ts')) {
367
+ entries.push(fullPath);
368
+ }
369
+ }
370
+
371
+ return entries;
372
+ }
373
+
374
+ // Option 2: Explicit entry points
375
+ const explicitEntryPoints = [
376
+ 'src/server-factory.ts',
377
+ 'src/client.ts',
378
+ 'src/types.ts',
379
+ 'src/tools/tool-one.ts',
380
+ 'src/tools/tool-two.ts'
381
+ ];
382
+
383
+ // Build CLI entry point (bundled)
384
+ await esbuild.build({
385
+ entryPoints: ['src/index.ts'],
386
+ bundle: true,
387
+ outfile: 'dist/index.js',
388
+ platform: 'node',
389
+ target: 'node18',
390
+ format: 'esm',
391
+ sourcemap: true,
392
+ external: [
393
+ '@modelcontextprotocol/sdk',
394
+ // Add other peer dependencies
395
+ ],
396
+ banner: {
397
+ js: "import { createRequire } from 'module'; const require = createRequire(import.meta.url);"
398
+ },
399
+ alias: {
400
+ '@': './src'
401
+ },
402
+ minify: false,
403
+ keepNames: true
404
+ });
405
+
406
+ // Build library exports (unbundled, preserves module structure)
407
+ await esbuild.build({
408
+ entryPoints: await findEntryPoints('src'), // or explicitEntryPoints
409
+ bundle: false, // Key: don't bundle for library
410
+ outdir: 'dist',
411
+ outbase: 'src', // Preserve directory structure
412
+ platform: 'node',
413
+ target: 'node18',
414
+ format: 'esm',
415
+ sourcemap: true,
416
+ alias: {
417
+ '@': './src'
418
+ }
419
+ });
420
+
421
+ console.log('Build complete!');
422
+ ```
423
+
424
+ **Key Points:**
425
+ - **Simple servers**: Single bundled entry point
426
+ - **Library exports**: Dual build strategy (bundle CLI, preserve modules)
427
+ - `bundle: true` for standalone executable
428
+ - `bundle: false` + `outbase: 'src'` for library exports
429
+ - `external` array lists dependencies not to bundle (peer dependencies)
430
+ - `banner` adds CommonJS compatibility for ESM bundles
431
+ - `alias` enables path alias resolution (`@/` → `src/`)
432
+ - `target` specifies Node.js version compatibility
433
+ - Dynamic or explicit entry point discovery for libraries
434
+
435
+ ### esbuild.watch.js Structure
436
+
437
+ ```javascript
438
+ import * as esbuild from 'esbuild';
439
+
440
+ const ctx = await esbuild.context({
441
+ entryPoints: ['src/server.ts'],
442
+ bundle: true,
443
+ platform: 'node',
444
+ target: 'node20',
445
+ format: 'esm',
446
+ outfile: 'dist/server.js',
447
+ sourcemap: true,
448
+ external: [
449
+ 'weaviate-client',
450
+ 'firebase-admin',
451
+ '@modelcontextprotocol/sdk'
452
+ ],
453
+ banner: {
454
+ js: "import { createRequire } from 'module'; const require = createRequire(import.meta.url);"
455
+ },
456
+ alias: {
457
+ '@': './src'
458
+ }
459
+ });
460
+
461
+ await ctx.watch();
462
+ console.log('👀 Watching for changes...');
463
+ ```
464
+
465
+ **Key Points:**
466
+ - Uses `esbuild.context()` API for watch mode
467
+ - Same configuration as `esbuild.build.js` for consistency
468
+ - Automatically rebuilds on file changes
469
+ - Includes all the same options: `external`, `banner`, `alias`, etc.
470
+
471
+ ## Source Code Patterns
472
+
473
+ ### Tool Definition Pattern
474
+
475
+ Each tool file exports both definition and handler:
476
+
477
+ ```typescript
478
+ // src/tools/example-tool.ts
479
+ import { ClientWrapper } from '../client.js';
480
+
481
+ export const exampleTool = {
482
+ name: 'prefix_tool_name',
483
+ description: 'Clear description of what the tool does',
484
+ inputSchema: {
485
+ type: 'object',
486
+ properties: {
487
+ param1: {
488
+ type: 'string',
489
+ description: 'Parameter description'
490
+ },
491
+ param2: {
492
+ type: 'number',
493
+ description: 'Optional parameter',
494
+ default: 10
495
+ }
496
+ },
497
+ required: ['param1']
498
+ }
499
+ };
500
+
501
+ export async function handleExampleTool(
502
+ client: ClientWrapper,
503
+ args: any
504
+ ): Promise<string> {
505
+ try {
506
+ const result = await client.doSomething(args.param1, args.param2);
507
+ return JSON.stringify(result, null, 2);
508
+ } catch (error) {
509
+ throw new Error(`Failed to execute: ${error instanceof Error ? error.message : String(error)}`);
510
+ }
511
+ }
512
+ ```
513
+
514
+ **Key Points:**
515
+ - Tool definition is a plain object (MCP Tool schema)
516
+ - Handler is a separate async function
517
+ - Handler receives client instance and args
518
+ - Returns JSON string for MCP response
519
+ - Proper error handling
520
+
521
+ ### Server Factory Pattern (Multi-Tenant)
522
+
523
+ For use with `mcp-auth` or other multi-tenant wrappers:
524
+
525
+ ```typescript
526
+ // src/server-factory.ts
527
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
528
+ import { ClientWrapper } from './client.js';
529
+ import {
530
+ CallToolRequestSchema,
531
+ ListToolsRequestSchema,
532
+ ErrorCode,
533
+ McpError
534
+ } from '@modelcontextprotocol/sdk/types.js';
535
+
536
+ // Import all tools
537
+ import { toolOne, handleToolOne } from './tools/tool-one.js';
538
+ import { toolTwo, handleToolTwo } from './tools/tool-two.js';
539
+
540
+ export interface ServerOptions {
541
+ name?: string;
542
+ version?: string;
543
+ }
544
+
545
+ /**
546
+ * Create a server instance for a specific user/tenant
547
+ *
548
+ * @param accessToken - User's access token for external API
549
+ * @param userId - User identifier
550
+ * @param options - Optional server configuration
551
+ * @returns Configured MCP Server instance
552
+ */
553
+ export function createServer(
554
+ accessToken: string,
555
+ userId: string,
556
+ options: ServerOptions = {}
557
+ ): Server {
558
+ if (!accessToken) {
559
+ throw new Error('accessToken is required');
560
+ }
561
+
562
+ if (!userId) {
563
+ throw new Error('userId is required');
564
+ }
565
+
566
+ // Initialize client with user's credentials
567
+ const client = new ClientWrapper(accessToken);
568
+
569
+ // Create MCP server
570
+ const server = new Server(
571
+ {
572
+ name: options.name || 'mcp-server',
573
+ version: options.version || '1.0.0'
574
+ },
575
+ {
576
+ capabilities: {
577
+ tools: {}
578
+ }
579
+ }
580
+ );
581
+
582
+ // Register list_tools handler
583
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
584
+ return {
585
+ tools: [
586
+ toolOne,
587
+ toolTwo,
588
+ // ... all tool definitions
589
+ ]
590
+ };
591
+ });
592
+
593
+ // Register call_tool handler
594
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
595
+ const { name, arguments: args } = request.params;
596
+
597
+ try {
598
+ let result: string;
599
+
600
+ switch (name) {
601
+ case 'prefix_tool_one':
602
+ result = await handleToolOne(client, args);
603
+ break;
604
+
605
+ case 'prefix_tool_two':
606
+ result = await handleToolTwo(client, args);
607
+ break;
608
+
609
+ default:
610
+ throw new McpError(
611
+ ErrorCode.MethodNotFound,
612
+ `Unknown tool: ${name}`
613
+ );
614
+ }
615
+
616
+ return {
617
+ content: [
618
+ {
619
+ type: 'text',
620
+ text: result
621
+ }
622
+ ]
623
+ };
624
+ } catch (error) {
625
+ if (error instanceof McpError) {
626
+ throw error;
627
+ }
628
+
629
+ throw new McpError(
630
+ ErrorCode.InternalError,
631
+ `Tool execution failed: ${error instanceof Error ? error.message : String(error)}`
632
+ );
633
+ }
634
+ });
635
+
636
+ return server;
637
+ }
638
+ ```
639
+
640
+ **Key Points:**
641
+ - Factory function creates isolated server instances
642
+ - Each instance has its own client with user credentials
643
+ - No shared state between instances
644
+ - Compatible with `mcp-auth` wrapping pattern
645
+
646
+ ### Standalone Server Pattern
647
+
648
+ For direct stdio usage without multi-tenancy:
649
+
650
+ ```typescript
651
+ // src/server.ts
652
+ #!/usr/bin/env node
653
+
654
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
655
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
656
+ import {
657
+ CallToolRequestSchema,
658
+ ListToolsRequestSchema,
659
+ } from '@modelcontextprotocol/sdk/types.js';
660
+ import { config } from 'dotenv';
661
+ import { ClientWrapper } from './client.js';
662
+ import { logger } from './utils/logger.js';
663
+
664
+ // Import tools
665
+ import { ToolOne } from './tools/tool-one.js';
666
+ import { ToolTwo } from './tools/tool-two.js';
667
+
668
+ // Load environment variables
669
+ config();
670
+
671
+ class MCPServer {
672
+ private server: Server;
673
+ private client: ClientWrapper;
674
+ private toolOne: ToolOne;
675
+ private toolTwo: ToolTwo;
676
+
677
+ constructor() {
678
+ // Initialize server
679
+ this.server = new Server(
680
+ {
681
+ name: 'mcp-server',
682
+ version: '1.0.0',
683
+ },
684
+ {
685
+ capabilities: {
686
+ tools: {},
687
+ },
688
+ }
689
+ );
690
+
691
+ // Initialize client
692
+ const apiKey = process.env.API_KEY;
693
+ if (!apiKey) {
694
+ throw new Error('API_KEY environment variable is required');
695
+ }
696
+
697
+ this.client = new ClientWrapper(apiKey);
698
+
699
+ // Initialize tools
700
+ this.toolOne = new ToolOne(this.client);
701
+ this.toolTwo = new ToolTwo(this.client);
702
+
703
+ this.setupHandlers();
704
+ }
705
+
706
+ private setupHandlers(): void {
707
+ // List available tools
708
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => {
709
+ return {
710
+ tools: [
711
+ this.toolOne.getToolDefinition(),
712
+ this.toolTwo.getToolDefinition(),
713
+ ],
714
+ };
715
+ });
716
+
717
+ // Handle tool calls
718
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
719
+ const { name, arguments: args } = request.params;
720
+
721
+ try {
722
+ let result: any;
723
+
724
+ switch (name) {
725
+ case 'prefix_tool_one':
726
+ result = await this.toolOne.execute(args);
727
+ break;
728
+
729
+ case 'prefix_tool_two':
730
+ result = await this.toolTwo.execute(args);
731
+ break;
732
+
733
+ default:
734
+ throw new Error(`Unknown tool: ${name}`);
735
+ }
736
+
737
+ return {
738
+ content: [
739
+ {
740
+ type: 'text',
741
+ text: JSON.stringify(result, null, 2),
742
+ },
743
+ ],
744
+ };
745
+ } catch (error) {
746
+ logger.error(`Tool execution failed for ${name}:`, error);
747
+ return {
748
+ content: [
749
+ {
750
+ type: 'text',
751
+ text: JSON.stringify({
752
+ error: error instanceof Error ? error.message : 'Unknown error',
753
+ tool: name
754
+ }, null, 2),
755
+ },
756
+ ],
757
+ isError: true,
758
+ };
759
+ }
760
+ });
761
+ }
762
+
763
+ async start(): Promise<void> {
764
+ try {
765
+ logger.info('Starting MCP Server...');
766
+
767
+ // Connect to external service
768
+ await this.client.connect();
769
+
770
+ // Start MCP server with stdio transport
771
+ const transport = new StdioServerTransport();
772
+ await this.server.connect(transport);
773
+
774
+ // Don't log to stdout/stderr when using stdio transport
775
+ // It interferes with MCP JSON protocol
776
+
777
+ } catch (error) {
778
+ process.exit(1);
779
+ }
780
+ }
781
+
782
+ async stop(): Promise<void> {
783
+ await this.server.close();
784
+ }
785
+ }
786
+
787
+ // Handle graceful shutdown
788
+ const server = new MCPServer();
789
+
790
+ process.on('SIGINT', async () => {
791
+ await server.stop();
792
+ process.exit(0);
793
+ });
794
+
795
+ process.on('SIGTERM', async () => {
796
+ await server.stop();
797
+ process.exit(0);
798
+ });
799
+
800
+ // Start the server
801
+ server.start().catch((error) => {
802
+ process.exit(1);
803
+ });
804
+ ```
805
+
806
+ **Key Points:**
807
+ - Class-based server for encapsulation
808
+ - Environment variable configuration
809
+ - Graceful shutdown handling
810
+ - **Critical**: No stdout/stderr logging when using stdio transport
811
+ - Tool instances as class properties
812
+
813
+ ### Client Wrapper Pattern
814
+
815
+ ```typescript
816
+ // src/client.ts
817
+ export interface ClientConfig {
818
+ apiKey: string;
819
+ baseUrl?: string;
820
+ timeout?: number;
821
+ }
822
+
823
+ export class ClientWrapper {
824
+ private config: ClientConfig;
825
+ private isConnected = false;
826
+
827
+ constructor(apiKey: string, options?: Partial<ClientConfig>) {
828
+ this.config = {
829
+ apiKey,
830
+ baseUrl: options?.baseUrl || 'https://api.example.com',
831
+ timeout: options?.timeout || 30000
832
+ };
833
+ }
834
+
835
+ async connect(): Promise<void> {
836
+ // Initialize connection, validate credentials, etc.
837
+ this.isConnected = true;
838
+ }
839
+
840
+ async doSomething(param: string): Promise<any> {
841
+ if (!this.isConnected) {
842
+ throw new Error('Client not connected');
843
+ }
844
+
845
+ // Make API call
846
+ const response = await fetch(`${this.config.baseUrl}/endpoint`, {
847
+ method: 'POST',
848
+ headers: {
849
+ 'Authorization': `Bearer ${this.config.apiKey}`,
850
+ 'Content-Type': 'application/json'
851
+ },
852
+ body: JSON.stringify({ param })
853
+ });
854
+
855
+ if (!response.ok) {
856
+ throw new Error(`API error: ${response.statusText}`);
857
+ }
858
+
859
+ return response.json();
860
+ }
861
+
862
+ isClientConnected(): boolean {
863
+ return this.isConnected;
864
+ }
865
+ }
866
+ ```
867
+
868
+ **Key Points:**
869
+ - Encapsulates external API communication
870
+ - Accepts credentials in constructor (for multi-tenant)
871
+ - Connection state management
872
+ - Error handling
873
+
874
+ ### Logger Pattern (Stdio-Safe)
875
+
876
+ ```typescript
877
+ // src/utils/logger.ts
878
+ export enum LogLevel {
879
+ ERROR = 0,
880
+ WARN = 1,
881
+ INFO = 2,
882
+ DEBUG = 3
883
+ }
884
+
885
+ class Logger {
886
+ // No-op logger to avoid interfering with stdio MCP transport
887
+ // All logging methods do nothing to prevent JSON corruption
888
+
889
+ error(message: string, ...args: any[]): void {
890
+ // No-op when using stdio
891
+ // Could write to file or use process.stderr in non-stdio mode
892
+ }
893
+
894
+ warn(message: string, ...args: any[]): void {
895
+ // No-op
896
+ }
897
+
898
+ info(message: string, ...args: any[]): void {
899
+ // No-op
900
+ }
901
+
902
+ debug(message: string, ...args: any[]): void {
903
+ // No-op
904
+ }
905
+ }
906
+
907
+ export const logger = new Logger();
908
+ ```
909
+
910
+ **Key Points:**
911
+ - **Critical**: No console output when using stdio transport
912
+ - Stdio transport uses stdout/stdin for JSON-RPC
913
+ - Any console output corrupts the protocol
914
+ - Alternative: Write to file or use stderr carefully
915
+
916
+ ### Error Serializer Pattern
917
+
918
+ ```typescript
919
+ // src/utils/error-serializer.ts
920
+ export function serializeError(error: unknown): any {
921
+ if (error instanceof Error) {
922
+ return {
923
+ name: error.name,
924
+ message: error.message,
925
+ stack: error.stack,
926
+ ...(error as any) // Include any additional properties
927
+ };
928
+ }
929
+
930
+ return {
931
+ message: String(error)
932
+ };
933
+ }
934
+ ```
935
+
936
+ ## Integration with mcp-auth
937
+
938
+ ### Using AuthenticatedMCPServer
939
+
940
+ If building a new server with tool-level auth:
941
+
942
+ ```typescript
943
+ // src/index.ts
944
+ import { AuthenticatedMCPServer } from '@prmichaelsen/mcp-auth/server';
945
+ import { EnvAuthProvider } from '@prmichaelsen/mcp-auth/providers/env';
946
+ import { SimpleTokenResolver } from '@prmichaelsen/mcp-auth';
947
+ import { withAuth } from '@prmichaelsen/mcp-auth/server';
948
+
949
+ const server = new AuthenticatedMCPServer({
950
+ name: 'my-server',
951
+ authProvider: new EnvAuthProvider(),
952
+ tokenResolver: new SimpleTokenResolver({ tokenEnvVar: 'API_TOKEN' }),
953
+ resourceType: 'myapi',
954
+ transport: { type: 'stdio' }
955
+ });
956
+
957
+ server.registerTool('get_data', withAuth(async (args, accessToken, userId) => {
958
+ const client = new ClientWrapper(accessToken);
959
+ return client.getData(args);
960
+ }));
961
+
962
+ await server.start();
963
+ ```
964
+
965
+ ### Using Server Wrapping Pattern
966
+
967
+ If wrapping an existing server factory:
968
+
969
+ ```typescript
970
+ // Wrapper server using mcp-auth
971
+ import { wrapServer } from '@prmichaelsen/mcp-auth/wrapper';
972
+ import { createServer } from './server-factory.js';
973
+
974
+ const wrappedServer = wrapServer({
975
+ serverFactory: createServer,
976
+ authProvider: new JWTAuthProvider({ secret: process.env.JWT_SECRET }),
977
+ tokenResolver: new APITokenResolver({ apiUrl: process.env.API_URL }),
978
+ resourceType: 'myapi',
979
+ transport: { type: 'sse', port: 3000 }
980
+ });
981
+
982
+ await wrappedServer.start();
983
+ ```
984
+
985
+ ## Directory Organization Best Practices
986
+
987
+ ### Agent Directory
988
+
989
+ The `agent/` directory contains documentation and planning:
990
+
991
+ ```
992
+ agent/
993
+ ├── patterns/ # Architecture patterns
994
+ │ ├── bootstrap.md # This document
995
+ │ ├── library-services.md # Service layer patterns
996
+ │ └── ...
997
+
998
+ ├── tasks/ # Task tracking
999
+ │ ├── task-001.md
1000
+ │ └── ...
1001
+
1002
+ ├── milestones/ # Milestone planning
1003
+ │ ├── milestone-1.md
1004
+ │ └── ...
1005
+
1006
+ ├── progress.yaml # Progress tracking
1007
+ └── requirements.md # Requirements document
1008
+ ```
1009
+
1010
+ ### Types Organization
1011
+
1012
+ Types can be organized in two ways:
1013
+
1014
+ **Option 1: Flat structure** (simple projects)
1015
+ ```
1016
+ src/
1017
+ ├── types.ts # All types in one file
1018
+ └── ...
1019
+ ```
1020
+
1021
+ **Option 2: Types directory** (complex projects)
1022
+ ```
1023
+ src/
1024
+ ├── types/
1025
+ │ ├── mcp.ts # MCP-specific types
1026
+ │ ├── api.ts # External API types
1027
+ │ ├── domain.ts # Domain types
1028
+ │ └── index.ts # Re-exports
1029
+ └── ...
1030
+ ```
1031
+
1032
+ ### Utils Organization
1033
+
1034
+ ```
1035
+ src/
1036
+ ├── utils/
1037
+ │ ├── logger.ts # Logging utility
1038
+ │ ├── error-serializer.ts # Error handling
1039
+ │ ├── validation.ts # Input validation
1040
+ │ └── index.ts # Re-exports
1041
+ └── ...
1042
+ ```
1043
+
1044
+ ## Build Output Structure
1045
+
1046
+ After building, the output should mirror the source structure:
1047
+
1048
+ ```
1049
+ dist/
1050
+ ├── index.js # Bundled CLI entry
1051
+ ├── index.d.ts
1052
+ ├── server-factory.js # Unbundled library exports
1053
+ ├── server-factory.d.ts
1054
+ ├── client.js
1055
+ ├── client.d.ts
1056
+ ├── types.js
1057
+ ├── types.d.ts
1058
+ ├── tools/
1059
+ │ ├── tool-one.js
1060
+ │ ├── tool-one.d.ts
1061
+ │ ├── tool-two.js
1062
+ │ ├── tool-two.d.ts
1063
+ │ └── index.js
1064
+ └── utils/
1065
+ ├── logger.js
1066
+ ├── logger.d.ts
1067
+ └── ...
1068
+ ```
1069
+
1070
+ **Key Points:**
1071
+ - `index.js` is bundled (single file)
1072
+ - Other exports preserve module structure
1073
+ - Type definitions (`.d.ts`) for all modules
1074
+ - Source maps (`.js.map`) for debugging
1075
+
1076
+ ## Import Patterns
1077
+
1078
+ ### ESM Import Extensions
1079
+
1080
+ Always include `.js` extension in imports (even for `.ts` files):
1081
+
1082
+ ```typescript
1083
+ // ✅ Correct
1084
+ import { ClientWrapper } from './client.js';
1085
+ import { toolOne } from './tools/tool-one.js';
1086
+
1087
+ // ❌ Wrong
1088
+ import { ClientWrapper } from './client';
1089
+ import { toolOne } from './tools/tool-one';
1090
+ ```
1091
+
1092
+ ### Re-export Patterns
1093
+
1094
+ ```typescript
1095
+ // src/tools/index.ts
1096
+ export * from './tool-one.js';
1097
+ export * from './tool-two.js';
1098
+
1099
+ // Usage
1100
+ import { toolOne, toolTwo } from './tools/index.js';
1101
+ ```
1102
+
1103
+ ## Environment Configuration
1104
+
1105
+ ### .env.example
1106
+
1107
+ ```bash
1108
+ # API Configuration
1109
+ API_KEY=your_api_key_here
1110
+ API_URL=https://api.example.com
1111
+
1112
+ # Server Configuration
1113
+ PORT=3000
1114
+ NODE_ENV=development
1115
+
1116
+ # Logging
1117
+ LOG_LEVEL=info
1118
+ ```
1119
+
1120
+ ### Environment Loading
1121
+
1122
+ ```typescript
1123
+ import { config } from 'dotenv';
1124
+
1125
+ // Load at server startup
1126
+ config();
1127
+
1128
+ // Access variables
1129
+ const apiKey = process.env.API_KEY;
1130
+ if (!apiKey) {
1131
+ throw new Error('API_KEY is required');
1132
+ }
1133
+ ```
1134
+
1135
+ ## Testing Considerations
1136
+
1137
+ While not covered in detail, consider:
1138
+
1139
+ ```
1140
+ src/
1141
+ ├── tools/
1142
+ │ ├── tool-one.ts
1143
+ │ ├── tool-one.test.ts # Co-located tests
1144
+ │ └── ...
1145
+ ```
1146
+
1147
+ Or separate test directory:
1148
+
1149
+ ```
1150
+ tests/
1151
+ ├── tools/
1152
+ │ ├── tool-one.test.ts
1153
+ │ └── ...
1154
+ └── integration/
1155
+ └── ...
1156
+ ```
1157
+
1158
+ ## Common Patterns Summary
1159
+
1160
+ ### 1. Tool Organization
1161
+ - One file per tool
1162
+ - Export definition and handler separately
1163
+ - Handler receives client and args
1164
+ - Return JSON strings
1165
+
1166
+ ### 2. Server Patterns
1167
+ - **Factory**: For multi-tenant (returns Server instance)
1168
+ - **Class**: For standalone (manages lifecycle)
1169
+ - Both patterns supported
1170
+
1171
+ ### 3. Build Strategy
1172
+ - **Bundle**: CLI entry point (single file)
1173
+ - **Preserve**: Library exports (module structure)
1174
+ - TypeScript declarations always generated
1175
+
1176
+ ### 4. Client Pattern
1177
+ - Wrapper class for external API
1178
+ - Accept credentials in constructor
1179
+ - Stateful connection management
1180
+
1181
+ ### 5. Logging Pattern
1182
+ - No-op for stdio transport
1183
+ - File or stderr for other transports
1184
+ - Never use console.log with stdio
1185
+
1186
+ ### 6. Type Safety
1187
+ - Strong typing throughout
1188
+ - Separate type files or directories
1189
+ - Export types for library consumers
1190
+
1191
+ ### 7. Error Handling
1192
+ - Serialize errors for MCP responses
1193
+ - Proper error types (McpError)
1194
+ - Graceful degradation
1195
+
1196
+ ## Compatibility Checklist
1197
+
1198
+ When building a server compatible with `mcp-auth`:
1199
+
1200
+ - ✅ Export a factory function that accepts `(accessToken, userId, options?)`
1201
+ - ✅ Factory returns a configured `Server` instance
1202
+ - ✅ No shared state between server instances
1203
+ - ✅ Client wrapper accepts credentials in constructor
1204
+ - ✅ Tools are stateless (receive client as parameter)
1205
+ - ✅ Proper TypeScript types exported
1206
+ - ✅ ESM with `.js` extensions in imports
1207
+ - ✅ Dual build: bundled CLI + preserved modules
1208
+
1209
+ ## Migration Path
1210
+
1211
+ ### From Standalone to Multi-Tenant
1212
+
1213
+ 1. Extract server creation into factory function
1214
+ 2. Move credential loading from env to factory parameters
1215
+ 3. Ensure no shared state between instances
1216
+ 4. Add factory export to package.json
1217
+ 5. Update build to preserve module structure
1218
+
1219
+ ### From Multi-Tenant to mcp-auth Integration
1220
+
1221
+ 1. Keep existing factory function
1222
+ 2. Add mcp-auth wrapper in separate entry point
1223
+ 3. Configure auth provider and token resolver
1224
+ 4. Deploy wrapped server for remote access
1225
+ 5. Keep factory for direct usage
1226
+
1227
+ ## Conclusion
1228
+
1229
+ This bootstrap pattern provides a foundation for building MCP servers that are:
1230
+
1231
+ - **Modular**: Clear separation of concerns
1232
+ - **Type-safe**: Strong TypeScript typing
1233
+ - **Multi-tenant ready**: Isolated instances per user
1234
+ - **Library-friendly**: Dual build strategy
1235
+ - **mcp-auth compatible**: Works with authentication framework
1236
+
1237
+ The pattern emphasizes **organization and structure** over specific implementations, allowing flexibility in choosing tools and technologies while maintaining consistency and compatibility.