@girardmedia/bootspring 3.3.2 → 3.4.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 (171) hide show
  1. package/assets/agents/accessibility-auditor.md +39 -0
  2. package/assets/agents/api-designer.md +40 -0
  3. package/assets/agents/auth-implementer.md +64 -0
  4. package/assets/agents/bug-hunter.md +42 -0
  5. package/assets/agents/bundle-analyzer.md +40 -0
  6. package/assets/agents/cache-optimizer.md +55 -0
  7. package/assets/agents/changelog-writer.md +55 -0
  8. package/assets/agents/ci-cd-builder.md +40 -0
  9. package/assets/agents/code-explainer.md +39 -0
  10. package/assets/agents/code-reviewer.md +39 -0
  11. package/assets/agents/cost-optimizer.md +57 -0
  12. package/assets/agents/cron-scheduler.md +51 -0
  13. package/assets/agents/data-seeder.md +56 -0
  14. package/assets/agents/database-architect.md +40 -0
  15. package/assets/agents/dependency-updater.md +40 -0
  16. package/assets/agents/deploy-checker.md +40 -0
  17. package/assets/agents/docker-optimizer.md +40 -0
  18. package/assets/agents/documentation-writer.md +40 -0
  19. package/assets/agents/email-builder.md +55 -0
  20. package/assets/agents/env-setup.md +40 -0
  21. package/assets/agents/error-handler.md +40 -0
  22. package/assets/agents/eslint-fixer.md +46 -0
  23. package/assets/agents/feature-flagger.md +69 -0
  24. package/assets/agents/git-detective.md +39 -0
  25. package/assets/agents/graphql-builder.md +60 -0
  26. package/assets/agents/incident-responder.md +59 -0
  27. package/assets/agents/log-analyzer.md +39 -0
  28. package/assets/agents/migration-planner.md +41 -0
  29. package/assets/agents/monorepo-navigator.md +39 -0
  30. package/assets/agents/nextjs-expert.md +57 -0
  31. package/assets/agents/notification-builder.md +56 -0
  32. package/assets/agents/onboarding-guide.md +39 -0
  33. package/assets/agents/performance-profiler.md +40 -0
  34. package/assets/agents/prisma-expert.md +57 -0
  35. package/assets/agents/rate-limiter.md +58 -0
  36. package/assets/agents/react-expert.md +58 -0
  37. package/assets/agents/refactorer.md +42 -0
  38. package/assets/agents/regex-builder.md +46 -0
  39. package/assets/agents/release-manager.md +40 -0
  40. package/assets/agents/s3-manager.md +58 -0
  41. package/assets/agents/schema-validator.md +40 -0
  42. package/assets/agents/search-builder.md +62 -0
  43. package/assets/agents/security-auditor.md +39 -0
  44. package/assets/agents/sitemap-generator.md +53 -0
  45. package/assets/agents/stripe-integrator.md +59 -0
  46. package/assets/agents/tailwind-expert.md +55 -0
  47. package/assets/agents/tech-debt-tracker.md +39 -0
  48. package/assets/agents/test-writer.md +42 -0
  49. package/assets/agents/type-fixer.md +45 -0
  50. package/assets/agents/webhook-builder.md +54 -0
  51. package/assets/rules/cpp.md +53 -0
  52. package/assets/rules/css.md +52 -0
  53. package/assets/rules/go.md +50 -0
  54. package/assets/rules/html.md +52 -0
  55. package/assets/rules/java.md +51 -0
  56. package/assets/rules/kotlin.md +50 -0
  57. package/assets/rules/php.md +51 -0
  58. package/assets/rules/python.md +51 -0
  59. package/assets/rules/ruby.md +51 -0
  60. package/assets/rules/rust.md +49 -0
  61. package/assets/rules/shell.md +52 -0
  62. package/assets/rules/sql.md +49 -0
  63. package/assets/rules/swift.md +50 -0
  64. package/assets/rules/typescript.md +52 -0
  65. package/assets/rules/yaml-json.md +51 -0
  66. package/assets/skills/accessibility.md +210 -0
  67. package/assets/skills/agent-patterns.md +387 -0
  68. package/assets/skills/ai-integration.md +263 -0
  69. package/assets/skills/animation-patterns.md +224 -0
  70. package/assets/skills/api-design.md +218 -0
  71. package/assets/skills/api-gateway.md +341 -0
  72. package/assets/skills/api-versioning.md +226 -0
  73. package/assets/skills/astro-patterns.md +233 -0
  74. package/assets/skills/auth-patterns.md +248 -0
  75. package/assets/skills/aws-patterns.md +171 -0
  76. package/assets/skills/background-jobs.md +162 -0
  77. package/assets/skills/browser-extensions.md +309 -0
  78. package/assets/skills/caching-patterns.md +253 -0
  79. package/assets/skills/ci-cd.md +251 -0
  80. package/assets/skills/cli-development.md +296 -0
  81. package/assets/skills/code-review.md +185 -0
  82. package/assets/skills/cron-patterns.md +327 -0
  83. package/assets/skills/data-fetching.md +231 -0
  84. package/assets/skills/database-migrations.md +346 -0
  85. package/assets/skills/database-patterns.md +219 -0
  86. package/assets/skills/debugging.md +281 -0
  87. package/assets/skills/design-system.md +289 -0
  88. package/assets/skills/django-patterns.md +182 -0
  89. package/assets/skills/docker-patterns.md +235 -0
  90. package/assets/skills/e2e-testing.md +287 -0
  91. package/assets/skills/edge-computing.md +268 -0
  92. package/assets/skills/electron-patterns.md +266 -0
  93. package/assets/skills/email-templates.md +206 -0
  94. package/assets/skills/error-handling.md +265 -0
  95. package/assets/skills/event-driven.md +232 -0
  96. package/assets/skills/express-patterns.md +239 -0
  97. package/assets/skills/fastapi-patterns.md +198 -0
  98. package/assets/skills/feature-flags.md +212 -0
  99. package/assets/skills/figma-to-code.md +298 -0
  100. package/assets/skills/file-upload.md +228 -0
  101. package/assets/skills/forms-patterns.md +264 -0
  102. package/assets/skills/gcp-patterns.md +189 -0
  103. package/assets/skills/git-workflow.md +187 -0
  104. package/assets/skills/golang-patterns.md +185 -0
  105. package/assets/skills/graphql-patterns.md +244 -0
  106. package/assets/skills/i18n-patterns.md +172 -0
  107. package/assets/skills/image-processing.md +350 -0
  108. package/assets/skills/java-springboot.md +226 -0
  109. package/assets/skills/kotlin-patterns.md +207 -0
  110. package/assets/skills/kubernetes-patterns.md +326 -0
  111. package/assets/skills/laravel-patterns.md +261 -0
  112. package/assets/skills/llm-fine-tuning.md +335 -0
  113. package/assets/skills/load-testing.md +303 -0
  114. package/assets/skills/logging-observability.md +228 -0
  115. package/assets/skills/markdown-processing.md +318 -0
  116. package/assets/skills/mcp-server-patterns.md +292 -0
  117. package/assets/skills/microservices.md +272 -0
  118. package/assets/skills/migration-patterns.md +239 -0
  119. package/assets/skills/mongodb-patterns.md +189 -0
  120. package/assets/skills/monorepo-patterns.md +287 -0
  121. package/assets/skills/nextjs-app-router.md +237 -0
  122. package/assets/skills/notification-patterns.md +348 -0
  123. package/assets/skills/oauth-patterns.md +246 -0
  124. package/assets/skills/payment-integration.md +222 -0
  125. package/assets/skills/pdf-generation.md +307 -0
  126. package/assets/skills/performance-optimization.md +277 -0
  127. package/assets/skills/php-patterns.md +210 -0
  128. package/assets/skills/prisma-patterns.md +241 -0
  129. package/assets/skills/prompt-engineering.md +193 -0
  130. package/assets/skills/pwa-patterns.md +247 -0
  131. package/assets/skills/python-patterns.md +158 -0
  132. package/assets/skills/python-testing.md +172 -0
  133. package/assets/skills/queue-patterns.md +295 -0
  134. package/assets/skills/rag-patterns.md +159 -0
  135. package/assets/skills/rate-limiting.md +319 -0
  136. package/assets/skills/react-components.md +201 -0
  137. package/assets/skills/react-native-patterns.md +299 -0
  138. package/assets/skills/real-time-patterns.md +181 -0
  139. package/assets/skills/redis-patterns.md +188 -0
  140. package/assets/skills/refactoring.md +218 -0
  141. package/assets/skills/regex-patterns.md +191 -0
  142. package/assets/skills/remix-patterns.md +262 -0
  143. package/assets/skills/responsive-design.md +199 -0
  144. package/assets/skills/ruby-rails-patterns.md +178 -0
  145. package/assets/skills/rust-patterns.md +211 -0
  146. package/assets/skills/search-patterns.md +227 -0
  147. package/assets/skills/security-hardening.md +237 -0
  148. package/assets/skills/seo-patterns.md +179 -0
  149. package/assets/skills/serverless-patterns.md +223 -0
  150. package/assets/skills/sql-optimization.md +154 -0
  151. package/assets/skills/state-management.md +254 -0
  152. package/assets/skills/storybook-patterns.md +330 -0
  153. package/assets/skills/svelte-patterns.md +258 -0
  154. package/assets/skills/swift-patterns.md +227 -0
  155. package/assets/skills/tailwind-patterns.md +272 -0
  156. package/assets/skills/tdd-workflow.md +199 -0
  157. package/assets/skills/terraform-patterns.md +270 -0
  158. package/assets/skills/testing-react.md +240 -0
  159. package/assets/skills/testing-vitest.md +232 -0
  160. package/assets/skills/typescript-strict.md +159 -0
  161. package/assets/skills/video-processing.md +340 -0
  162. package/assets/skills/vue-patterns.md +247 -0
  163. package/assets/skills/web-workers.md +327 -0
  164. package/assets/skills/webhooks-patterns.md +283 -0
  165. package/assets/skills/websocket-patterns.md +306 -0
  166. package/dist/cli/index.js +941 -958
  167. package/dist/core/index.d.ts +341 -11
  168. package/dist/core.js +58 -95
  169. package/dist/mcp/index.d.ts +33 -1
  170. package/dist/mcp-server.js +177 -255
  171. package/package.json +4 -1
@@ -0,0 +1,318 @@
1
+ ---
2
+ name: markdown-processing
3
+ description: Markdown processing patterns with remark/rehype, MDX compilation, syntax highlighting, custom plugins, and content pipelines.
4
+ ---
5
+
6
+ # Markdown Processing Patterns
7
+
8
+ ## When to Use
9
+ Use these patterns when building documentation sites, blogs, CMSs, or any application that renders markdown content. The unified ecosystem (remark for markdown, rehype for HTML) provides a plugin pipeline for transforming content. Use MDX when you need interactive React components inside markdown. These patterns cover the full pipeline from raw markdown to styled, interactive HTML.
10
+
11
+ ## How It Works
12
+
13
+ ### Remark/Rehype Pipeline
14
+
15
+ ```typescript
16
+ // src/markdown/processor.ts
17
+ import { unified } from 'unified';
18
+ import remarkParse from 'remark-parse';
19
+ import remarkGfm from 'remark-gfm';
20
+ import remarkMath from 'remark-math';
21
+ import remarkRehype from 'remark-rehype';
22
+ import rehypeSlug from 'rehype-slug';
23
+ import rehypeAutolinkHeadings from 'rehype-autolink-headings';
24
+ import rehypeHighlight from 'rehype-highlight';
25
+ import rehypeKatex from 'rehype-katex';
26
+ import rehypeSanitize from 'rehype-sanitize';
27
+ import rehypeStringify from 'rehype-stringify';
28
+
29
+ export async function processMarkdown(markdown: string): Promise<{
30
+ html: string;
31
+ headings: Array<{ depth: number; text: string; id: string }>;
32
+ }> {
33
+ const headings: Array<{ depth: number; text: string; id: string }> = [];
34
+
35
+ const result = await unified()
36
+ .use(remarkParse)
37
+ .use(remarkGfm) // tables, strikethrough, task lists
38
+ .use(remarkMath) // LaTeX math blocks
39
+ .use(extractHeadings, { headings }) // custom plugin
40
+ .use(remarkRehype, { allowDangerousHtml: false })
41
+ .use(rehypeSlug) // add id to headings
42
+ .use(rehypeAutolinkHeadings, { behavior: 'wrap' })
43
+ .use(rehypeHighlight) // syntax highlighting
44
+ .use(rehypeKatex) // render math
45
+ .use(rehypeSanitize) // XSS protection
46
+ .use(rehypeStringify)
47
+ .process(markdown);
48
+
49
+ return { html: String(result), headings };
50
+ }
51
+ ```
52
+
53
+ ### Custom Remark Plugin
54
+
55
+ ```typescript
56
+ // src/markdown/plugins/extract-headings.ts
57
+ import { visit } from 'unist-util-visit';
58
+ import { toString } from 'mdast-util-to-string';
59
+ import type { Root, Heading } from 'mdast';
60
+ import type { Plugin } from 'unified';
61
+
62
+ interface Options {
63
+ headings: Array<{ depth: number; text: string; id: string }>;
64
+ }
65
+
66
+ export const extractHeadings: Plugin<[Options], Root> = (options) => {
67
+ return (tree) => {
68
+ visit(tree, 'heading', (node: Heading) => {
69
+ const text = toString(node);
70
+ const id = text.toLowerCase().replace(/[^\w]+/g, '-').replace(/(^-|-$)/g, '');
71
+ options.headings.push({ depth: node.depth, text, id });
72
+ });
73
+ };
74
+ };
75
+ ```
76
+
77
+ ### Reading Time Plugin
78
+
79
+ ```typescript
80
+ // src/markdown/plugins/reading-time.ts
81
+ import { toString } from 'mdast-util-to-string';
82
+ import type { Root } from 'mdast';
83
+ import type { Plugin } from 'unified';
84
+ import type { VFile } from 'vfile';
85
+
86
+ const WORDS_PER_MINUTE = 200;
87
+
88
+ export const remarkReadingTime: Plugin<[], Root> = () => {
89
+ return (tree: Root, file: VFile) => {
90
+ const text = toString(tree);
91
+ const words = text.split(/\s+/).filter(Boolean).length;
92
+ const minutes = Math.max(1, Math.ceil(words / WORDS_PER_MINUTE));
93
+
94
+ (file.data as any).readingTime = { words, minutes, text: `${minutes} min read` };
95
+ };
96
+ };
97
+ ```
98
+
99
+ ### Syntax Highlighting with Shiki
100
+
101
+ ```typescript
102
+ // src/markdown/highlighter.ts
103
+ import { createHighlighter, type BundledLanguage } from 'shiki';
104
+
105
+ let highlighter: Awaited<ReturnType<typeof createHighlighter>> | null = null;
106
+
107
+ async function getHighlighter() {
108
+ if (!highlighter) {
109
+ highlighter = await createHighlighter({
110
+ themes: ['github-dark', 'github-light'],
111
+ langs: ['typescript', 'javascript', 'jsx', 'tsx', 'python', 'bash', 'json', 'sql', 'css', 'html'],
112
+ });
113
+ }
114
+ return highlighter;
115
+ }
116
+
117
+ export async function highlightCode(code: string, lang: string, theme: 'dark' | 'light' = 'dark') {
118
+ const h = await getHighlighter();
119
+ return h.codeToHtml(code.trim(), {
120
+ lang: lang as BundledLanguage,
121
+ theme: theme === 'dark' ? 'github-dark' : 'github-light',
122
+ });
123
+ }
124
+
125
+ // Rehype plugin using Shiki
126
+ import { visit } from 'unist-util-visit';
127
+ import type { Element, Root } from 'hast';
128
+
129
+ export function rehypeShiki(): (tree: Root) => Promise<void> {
130
+ return async (tree) => {
131
+ const h = await getHighlighter();
132
+ const promises: Promise<void>[] = [];
133
+
134
+ visit(tree, 'element', (node: Element, _index, parent) => {
135
+ if (node.tagName !== 'code' || (parent as Element)?.tagName !== 'pre') return;
136
+
137
+ const className = (node.properties?.className as string[]) ?? [];
138
+ const lang = className.find((c) => c.startsWith('language-'))?.replace('language-', '') ?? 'text';
139
+
140
+ const code = toString(node);
141
+ promises.push(
142
+ (async () => {
143
+ const html = h.codeToHtml(code, { lang: lang as BundledLanguage, theme: 'github-dark' });
144
+ // Replace the pre>code with Shiki output
145
+ Object.assign(parent!, { type: 'raw', value: html });
146
+ })()
147
+ );
148
+ });
149
+
150
+ await Promise.all(promises);
151
+ };
152
+ }
153
+ ```
154
+
155
+ ### MDX Compilation
156
+
157
+ ```typescript
158
+ // src/markdown/mdx.ts
159
+ import { compile, run } from '@mdx-js/mdx';
160
+ import * as runtime from 'react/jsx-runtime';
161
+ import remarkGfm from 'remark-gfm';
162
+ import rehypeSlug from 'rehype-slug';
163
+
164
+ // Custom components available in MDX
165
+ const components = {
166
+ Callout: ({ type, children }: { type: 'info' | 'warning' | 'error'; children: React.ReactNode }) => (
167
+ <div className={`callout callout-${type}`} role="alert">{children}</div>
168
+ ),
169
+ CodeTabs: ({ children }: { children: React.ReactNode }) => (
170
+ <div className="code-tabs">{children}</div>
171
+ ),
172
+ };
173
+
174
+ export async function compileMDX(source: string) {
175
+ const code = String(
176
+ await compile(source, {
177
+ outputFormat: 'function-body',
178
+ remarkPlugins: [remarkGfm],
179
+ rehypePlugins: [rehypeSlug],
180
+ })
181
+ );
182
+
183
+ const { default: Content } = await run(code, {
184
+ ...runtime,
185
+ baseUrl: import.meta.url,
186
+ });
187
+
188
+ return { Content, components };
189
+ }
190
+
191
+ // Usage in React
192
+ function MDXRenderer({ source }: { source: string }) {
193
+ const [Content, setContent] = useState<React.FC | null>(null);
194
+
195
+ useEffect(() => {
196
+ compileMDX(source).then(({ Content }) => setContent(() => Content));
197
+ }, [source]);
198
+
199
+ if (!Content) return <div>Loading...</div>;
200
+ return <Content components={components} />;
201
+ }
202
+ ```
203
+
204
+ ### Content Pipeline (Frontmatter + Processing)
205
+
206
+ ```typescript
207
+ // src/markdown/content-pipeline.ts
208
+ import matter from 'gray-matter';
209
+ import { z } from 'zod';
210
+
211
+ const frontmatterSchema = z.object({
212
+ title: z.string(),
213
+ description: z.string().optional(),
214
+ date: z.coerce.date(),
215
+ tags: z.array(z.string()).default([]),
216
+ draft: z.boolean().default(false),
217
+ author: z.string().optional(),
218
+ });
219
+
220
+ type Frontmatter = z.infer<typeof frontmatterSchema>;
221
+
222
+ export interface ProcessedContent {
223
+ frontmatter: Frontmatter;
224
+ html: string;
225
+ headings: Array<{ depth: number; text: string; id: string }>;
226
+ readingTime: { words: number; minutes: number; text: string };
227
+ excerpt: string;
228
+ }
229
+
230
+ export async function processContentFile(filepath: string): Promise<ProcessedContent> {
231
+ const raw = await fs.readFile(filepath, 'utf-8');
232
+ const { data, content } = matter(raw);
233
+
234
+ // Validate frontmatter
235
+ const frontmatter = frontmatterSchema.parse(data);
236
+
237
+ // Process markdown
238
+ const { html, headings } = await processMarkdown(content);
239
+
240
+ // Generate excerpt (first paragraph)
241
+ const excerpt = content.split('\n\n')[0].replace(/[#*_`]/g, '').trim().slice(0, 200);
242
+
243
+ return { frontmatter, html, headings, readingTime: { words: 0, minutes: 0, text: '' }, excerpt };
244
+ }
245
+
246
+ // Build a searchable index
247
+ export async function buildContentIndex(contentDir: string) {
248
+ const files = await glob('**/*.md', { cwd: contentDir });
249
+ const index = await Promise.all(
250
+ files.map(async (file) => {
251
+ const content = await processContentFile(path.join(contentDir, file));
252
+ return {
253
+ slug: file.replace(/\.md$/, ''),
254
+ ...content.frontmatter,
255
+ excerpt: content.excerpt,
256
+ headings: content.headings,
257
+ };
258
+ })
259
+ );
260
+
261
+ return index
262
+ .filter((item) => !item.draft)
263
+ .sort((a, b) => b.date.getTime() - a.date.getTime());
264
+ }
265
+ ```
266
+
267
+ ### Custom Rehype Plugin (Image Optimization)
268
+
269
+ ```typescript
270
+ // src/markdown/plugins/rehype-optimized-images.ts
271
+ import { visit } from 'unist-util-visit';
272
+ import type { Element, Root } from 'hast';
273
+
274
+ export function rehypeOptimizedImages(options: { basePath: string; widths: number[] }) {
275
+ return (tree: Root) => {
276
+ visit(tree, 'element', (node: Element) => {
277
+ if (node.tagName !== 'img') return;
278
+
279
+ const src = node.properties?.src as string;
280
+ if (!src || src.startsWith('http')) return;
281
+
282
+ const srcset = options.widths
283
+ .map((w) => `${options.basePath}/_next/image?url=${encodeURIComponent(src)}&w=${w} ${w}w`)
284
+ .join(', ');
285
+
286
+ node.properties = {
287
+ ...node.properties,
288
+ srcset,
289
+ sizes: '(max-width: 768px) 100vw, 768px',
290
+ loading: 'lazy',
291
+ decoding: 'async',
292
+ };
293
+ });
294
+ };
295
+ }
296
+ ```
297
+
298
+ ## Examples
299
+
300
+ | Plugin | Phase | Purpose |
301
+ |--------|-------|---------|
302
+ | `remark-gfm` | Parse | Tables, task lists, autolinks |
303
+ | `remark-math` | Parse | LaTeX math expressions |
304
+ | `remark-rehype` | Transform | Markdown AST to HTML AST |
305
+ | `rehype-slug` | Transform | Add IDs to headings |
306
+ | `rehype-highlight` | Transform | Syntax highlighting |
307
+ | `rehype-sanitize` | Transform | XSS protection |
308
+ | `rehype-stringify` | Stringify | HTML AST to string |
309
+
310
+ ## Checklist
311
+ - [ ] Markdown processed through unified pipeline (remark + rehype)
312
+ - [ ] GFM enabled for tables, task lists, and strikethrough
313
+ - [ ] Syntax highlighting uses Shiki or rehype-highlight with language detection
314
+ - [ ] Headings have slugified IDs for anchor links
315
+ - [ ] HTML output sanitized to prevent XSS from user content
316
+ - [ ] Frontmatter validated with Zod schema
317
+ - [ ] Reading time calculated and exposed to templates
318
+ - [ ] Images have lazy loading, srcset, and sizes attributes
@@ -0,0 +1,292 @@
1
+ ---
2
+ name: mcp-server-patterns
3
+ description: Build MCP servers — tool definitions, resource providers, stdio transport, input validation, error handling, and testing.
4
+ ---
5
+
6
+ # MCP Server Patterns
7
+
8
+ ## When to Use
9
+
10
+ Apply these patterns when building Model Context Protocol (MCP) servers that
11
+ expose tools and resources to AI assistants (Claude, Copilot, etc.). MCP
12
+ servers act as a bridge between AI models and external capabilities — file
13
+ systems, APIs, databases, or custom business logic. Use this skill when
14
+ creating tool integrations, resource providers, or extending an existing
15
+ MCP server with new capabilities.
16
+
17
+ ## How It Works
18
+
19
+ ### 1. Server Setup — stdio Transport
20
+
21
+ ```typescript
22
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
23
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
24
+ import { z } from 'zod';
25
+
26
+ const server = new McpServer({
27
+ name: 'my-tools',
28
+ version: '1.0.0',
29
+ });
30
+
31
+ // Start the server on stdio
32
+ const transport = new StdioServerTransport();
33
+ await server.connect(transport);
34
+ ```
35
+
36
+ ### 2. Tool Definitions — Input Schema and Handler
37
+
38
+ ```typescript
39
+ // Simple tool with validated inputs
40
+ server.tool(
41
+ 'search_files',
42
+ 'Search for files matching a pattern in the project directory',
43
+ {
44
+ pattern: z.string().describe('Glob pattern to match (e.g., "**/*.ts")'),
45
+ directory: z.string().optional().describe('Starting directory (default: project root)'),
46
+ maxResults: z.number().int().min(1).max(100).default(20).describe('Maximum results to return'),
47
+ },
48
+ async ({ pattern, directory, maxResults }) => {
49
+ const results = await globSearch(pattern, {
50
+ cwd: directory ?? process.cwd(),
51
+ limit: maxResults,
52
+ });
53
+
54
+ return {
55
+ content: [{
56
+ type: 'text',
57
+ text: results.length > 0
58
+ ? results.map(f => `- ${f}`).join('\n')
59
+ : `No files found matching "${pattern}"`,
60
+ }],
61
+ };
62
+ }
63
+ );
64
+
65
+ // Tool that returns structured data
66
+ server.tool(
67
+ 'query_database',
68
+ 'Run a read-only SQL query against the project database',
69
+ {
70
+ query: z.string().describe('SQL SELECT query to execute'),
71
+ },
72
+ async ({ query }) => {
73
+ // Validate query is read-only
74
+ const normalized = query.trim().toUpperCase();
75
+ if (!normalized.startsWith('SELECT') && !normalized.startsWith('WITH')) {
76
+ return {
77
+ content: [{ type: 'text', text: 'Error: Only SELECT queries are allowed' }],
78
+ isError: true,
79
+ };
80
+ }
81
+
82
+ try {
83
+ const rows = await db.query(query);
84
+ return {
85
+ content: [{
86
+ type: 'text',
87
+ text: JSON.stringify(rows, null, 2),
88
+ }],
89
+ };
90
+ } catch (err) {
91
+ return {
92
+ content: [{ type: 'text', text: `Query error: ${(err as Error).message}` }],
93
+ isError: true,
94
+ };
95
+ }
96
+ }
97
+ );
98
+ ```
99
+
100
+ ### 3. Resource Providers — Expose Contextual Data
101
+
102
+ ```typescript
103
+ // Static resource
104
+ server.resource(
105
+ 'project-config',
106
+ 'project://config',
107
+ async (uri) => ({
108
+ contents: [{
109
+ uri: uri.href,
110
+ mimeType: 'application/json',
111
+ text: JSON.stringify(await loadProjectConfig(), null, 2),
112
+ }],
113
+ })
114
+ );
115
+
116
+ // Dynamic resource with URI template
117
+ server.resource(
118
+ 'file-contents',
119
+ new ResourceTemplate('file:///{path}', { list: undefined }),
120
+ async (uri, { path }) => {
121
+ const content = await fs.readFile(path as string, 'utf-8');
122
+ return {
123
+ contents: [{
124
+ uri: uri.href,
125
+ mimeType: getMimeType(path as string),
126
+ text: content,
127
+ }],
128
+ };
129
+ }
130
+ );
131
+ ```
132
+
133
+ ### 4. Error Handling — Graceful Failure
134
+
135
+ ```typescript
136
+ server.tool(
137
+ 'deploy_preview',
138
+ 'Deploy a preview environment for the current branch',
139
+ {
140
+ branch: z.string().describe('Git branch to deploy'),
141
+ },
142
+ async ({ branch }) => {
143
+ try {
144
+ // Validate branch exists
145
+ const branches = await git.listBranches();
146
+ if (!branches.includes(branch)) {
147
+ return {
148
+ content: [{
149
+ type: 'text',
150
+ text: `Branch "${branch}" not found. Available branches:\n${branches.join('\n')}`,
151
+ }],
152
+ isError: true,
153
+ };
154
+ }
155
+
156
+ const result = await deployPreview(branch);
157
+ return {
158
+ content: [{
159
+ type: 'text',
160
+ text: `Preview deployed successfully.\nURL: ${result.url}\nStatus: ${result.status}`,
161
+ }],
162
+ };
163
+ } catch (err) {
164
+ const message = err instanceof Error ? err.message : String(err);
165
+ return {
166
+ content: [{
167
+ type: 'text',
168
+ text: `Deployment failed: ${message}\n\nTroubleshooting:\n- Check that Docker is running\n- Verify cloud credentials are configured`,
169
+ }],
170
+ isError: true,
171
+ };
172
+ }
173
+ }
174
+ );
175
+ ```
176
+
177
+ ### 5. Tool Design Principles
178
+
179
+ ```typescript
180
+ // GOOD: Clear name, specific description, constrained inputs
181
+ server.tool(
182
+ 'create_github_issue',
183
+ 'Create a new issue in the specified GitHub repository',
184
+ {
185
+ repo: z.string().regex(/^[\w-]+\/[\w-]+$/).describe('Repository (owner/name)'),
186
+ title: z.string().min(1).max(256).describe('Issue title'),
187
+ body: z.string().optional().describe('Issue body in markdown'),
188
+ labels: z.array(z.string()).optional().describe('Labels to apply'),
189
+ },
190
+ handler
191
+ );
192
+
193
+ // BAD: Vague name, no input validation, overly broad
194
+ server.tool(
195
+ 'do_github_stuff',
196
+ 'Does stuff with GitHub',
197
+ { data: z.any() },
198
+ handler
199
+ );
200
+ ```
201
+
202
+ **Tool design rules:**
203
+ - Name: verb_noun format, lowercase with underscores
204
+ - Description: one sentence explaining what the tool does and when to use it
205
+ - Inputs: use Zod for validation with `.describe()` on every field
206
+ - Outputs: return structured text, not raw JSON dumps
207
+ - Errors: return `isError: true` with actionable messages, not stack traces
208
+
209
+ ### 6. Multi-Tool Server Pattern
210
+
211
+ ```typescript
212
+ // Organize tools by domain in separate files
213
+ // tools/files.ts
214
+ export function registerFileTools(server: McpServer) {
215
+ server.tool('read_file', '...', schema, handler);
216
+ server.tool('write_file', '...', schema, handler);
217
+ server.tool('search_files', '...', schema, handler);
218
+ }
219
+
220
+ // tools/git.ts
221
+ export function registerGitTools(server: McpServer) {
222
+ server.tool('git_status', '...', schema, handler);
223
+ server.tool('git_diff', '...', schema, handler);
224
+ server.tool('git_log', '...', schema, handler);
225
+ }
226
+
227
+ // index.ts
228
+ const server = new McpServer({ name: 'dev-tools', version: '1.0.0' });
229
+ registerFileTools(server);
230
+ registerGitTools(server);
231
+ ```
232
+
233
+ ### 7. Testing MCP Tools
234
+
235
+ ```typescript
236
+ import { describe, it, expect, vi } from 'vitest';
237
+
238
+ describe('search_files tool', () => {
239
+ it('returns matching files', async () => {
240
+ const result = await searchFilesHandler({
241
+ pattern: '**/*.ts',
242
+ directory: '/tmp/test-project',
243
+ maxResults: 5,
244
+ });
245
+
246
+ expect(result.isError).toBeUndefined();
247
+ expect(result.content[0].text).toContain('.ts');
248
+ });
249
+
250
+ it('returns empty message when no files match', async () => {
251
+ const result = await searchFilesHandler({
252
+ pattern: '**/*.xyz',
253
+ directory: '/tmp/test-project',
254
+ maxResults: 5,
255
+ });
256
+
257
+ expect(result.content[0].text).toContain('No files found');
258
+ });
259
+
260
+ it('returns error for invalid directory', async () => {
261
+ const result = await searchFilesHandler({
262
+ pattern: '**/*.ts',
263
+ directory: '/nonexistent',
264
+ maxResults: 5,
265
+ });
266
+
267
+ expect(result.isError).toBe(true);
268
+ });
269
+ });
270
+ ```
271
+
272
+ ## Examples
273
+
274
+ | Component | Purpose | Protocol Role |
275
+ |-----------|---------|---------------|
276
+ | Tool | Execute actions (read, write, deploy) | `tools/call` |
277
+ | Resource | Expose read-only context (config, schema) | `resources/read` |
278
+ | Prompt | Predefined prompt templates | `prompts/get` |
279
+ | Transport | Communication channel | stdio, SSE, HTTP |
280
+
281
+ ## Checklist
282
+
283
+ - [ ] All tools have descriptive names (verb_noun) and one-sentence descriptions
284
+ - [ ] Input schemas use Zod with `.describe()` on every parameter
285
+ - [ ] Tools validate inputs and return `isError: true` on failure
286
+ - [ ] Error messages include troubleshooting hints, not raw exceptions
287
+ - [ ] Read-only operations separated from write operations
288
+ - [ ] Destructive tools require explicit confirmation parameters
289
+ - [ ] Resources expose context data that tools can reference
290
+ - [ ] Tools organized by domain in separate files
291
+ - [ ] Each tool handler has unit tests covering success, error, and edge cases
292
+ - [ ] Server logs to stderr (not stdout) to avoid corrupting stdio transport