what-mcp 0.1.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 (3) hide show
  1. package/README.md +72 -0
  2. package/package.json +31 -0
  3. package/src/index.js +696 -0
package/README.md ADDED
@@ -0,0 +1,72 @@
1
+ # what-mcp
2
+
3
+ [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) server for [What Framework](https://whatfw.com). Gives AI assistants access to What Framework documentation, API references, and code examples.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install what-mcp
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### With Claude Desktop
14
+
15
+ Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json`):
16
+
17
+ ```json
18
+ {
19
+ "mcpServers": {
20
+ "what-framework": {
21
+ "command": "npx",
22
+ "args": ["what-mcp"]
23
+ }
24
+ }
25
+ }
26
+ ```
27
+
28
+ ### Run directly
29
+
30
+ ```bash
31
+ npx what-mcp
32
+ ```
33
+
34
+ The server communicates over stdio using the MCP protocol.
35
+
36
+ ## Available Tools
37
+
38
+ | Tool | Description |
39
+ |---|---|
40
+ | `what_overview` | Framework overview and key features |
41
+ | `what_signals` | Signals and reactive primitives (`signal`, `computed`, `effect`, `batch`) |
42
+ | `what_components` | Components, `h()` function, and mounting |
43
+ | `what_hooks` | React-compatible hooks (`useState`, `useEffect`, `useMemo`, etc.) |
44
+ | `what_islands` | Islands architecture and partial hydration |
45
+ | `what_routing` | File-based and programmatic routing |
46
+ | `what_forms` | Form utilities and validation rules |
47
+ | `what_data_fetching` | Data fetching with `useSWR` and `useQuery` |
48
+ | `what_animation` | Animation primitives (springs, tweens, gestures) |
49
+ | `what_accessibility` | Accessibility utilities (focus traps, ARIA helpers, screen reader) |
50
+ | `what_skeleton` | Skeleton loaders and loading states |
51
+ | `what_ssr` | Server-side rendering and static generation |
52
+ | `what_cli` | CLI commands and configuration |
53
+ | `what_search` | Search across all documentation topics |
54
+
55
+ ## Example
56
+
57
+ When connected, an AI assistant can use these tools to answer questions about What Framework:
58
+
59
+ - "How do I create a signal?" -> calls `what_signals`
60
+ - "Show me how routing works" -> calls `what_routing`
61
+ - "How do I set up SSR?" -> calls `what_ssr`
62
+ - "Search for useForm" -> calls `what_search` with query "useForm"
63
+
64
+ ## Links
65
+
66
+ - [Documentation](https://whatfw.com)
67
+ - [GitHub](https://github.com/zvndev/what-fw)
68
+ - [MCP Specification](https://modelcontextprotocol.io/)
69
+
70
+ ## License
71
+
72
+ MIT
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "what-mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP Server for What Framework documentation and assistance",
5
+ "type": "module",
6
+ "main": "src/index.js",
7
+ "bin": {
8
+ "what-mcp": "./src/index.js"
9
+ },
10
+ "files": [
11
+ "src"
12
+ ],
13
+ "keywords": [
14
+ "mcp",
15
+ "what-framework",
16
+ "documentation"
17
+ ],
18
+ "author": "",
19
+ "license": "MIT",
20
+ "dependencies": {
21
+ "@modelcontextprotocol/sdk": "^1.0.0"
22
+ },
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "https://github.com/zvndev/what-fw"
26
+ },
27
+ "bugs": {
28
+ "url": "https://github.com/zvndev/what-fw/issues"
29
+ },
30
+ "homepage": "https://whatfw.com"
31
+ }
package/src/index.js ADDED
@@ -0,0 +1,696 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * What Framework MCP Server
5
+ *
6
+ * Provides documentation and assistance for the What Framework.
7
+ * Exposes tools for getting API reference, examples, and guidance.
8
+ */
9
+
10
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
11
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
12
+ import {
13
+ CallToolRequestSchema,
14
+ ListToolsRequestSchema,
15
+ } from '@modelcontextprotocol/sdk/types.js';
16
+
17
+ // Framework documentation content
18
+ const DOCS = {
19
+ overview: `
20
+ What Framework is a lightweight, signals-based web framework.
21
+
22
+ Key features:
23
+ - Signals: Fine-grained reactivity without virtual DOM diffing
24
+ - Islands: Zero JS by default, hydrate components on demand
25
+ - ~4kB gzipped: Tiny bundle size
26
+ - React-compatible hooks: useState, useEffect, useMemo backed by signals
27
+ - File-based routing: Drop files in pages/, get routes
28
+ - SSR/SSG/Hybrid: Choose rendering mode per page
29
+ - TypeScript: Full type definitions included
30
+ `,
31
+
32
+ signals: `
33
+ ## Signals
34
+
35
+ Signals are reactive primitives that hold values and notify subscribers when changed.
36
+
37
+ \`\`\`js
38
+ import { signal, computed, effect, batch, untrack } from 'what';
39
+
40
+ // Create a signal
41
+ const count = signal(0);
42
+
43
+ // Read value
44
+ count(); // 0
45
+
46
+ // Write value
47
+ count.set(5); // set to 5
48
+ count.set(c => c + 1); // increment
49
+
50
+ // Read without tracking
51
+ count.peek();
52
+
53
+ // Subscribe to changes
54
+ const unsub = count.subscribe(value => console.log(value));
55
+
56
+ // Computed (derived) signals
57
+ const doubled = computed(() => count() * 2);
58
+
59
+ // Effects (side effects)
60
+ const dispose = effect(() => {
61
+ console.log('Count is:', count());
62
+ });
63
+
64
+ // Batch multiple updates
65
+ batch(() => {
66
+ a.set(1);
67
+ b.set(2);
68
+ // Effects run once at the end
69
+ });
70
+
71
+ // Read without subscribing in an effect
72
+ effect(() => {
73
+ const val = untrack(() => someSignal());
74
+ });
75
+ \`\`\`
76
+ `,
77
+
78
+ components: `
79
+ ## Components
80
+
81
+ Components are functions that return VNodes.
82
+
83
+ \`\`\`js
84
+ import { h, mount, signal } from 'what';
85
+
86
+ // Simple component
87
+ function Greeting({ name }) {
88
+ return h('div', null, 'Hello, ', name);
89
+ }
90
+
91
+ // Stateful component with signals
92
+ function Counter() {
93
+ const count = signal(0);
94
+
95
+ return h('div', null,
96
+ h('p', null, 'Count: ', () => count()),
97
+ h('button', { onClick: () => count.set(c => c + 1) }, '+'),
98
+ );
99
+ }
100
+
101
+ // Mount to DOM
102
+ mount(h(Counter), '#app');
103
+ \`\`\`
104
+
105
+ ### h() Function
106
+
107
+ \`\`\`js
108
+ h(tag, props, ...children)
109
+ h('div', { class: 'box' }, 'Hello')
110
+ h('input', { type: 'text', onInput: e => {} })
111
+ h(MyComponent, { name: 'World' })
112
+ \`\`\`
113
+
114
+ Props handling:
115
+ - \`class\` / \`className\` → el.className
116
+ - \`style\` (object or string) → el.style
117
+ - \`on*\` → event listeners (onClick → click)
118
+ - \`ref\` → ref.current = el
119
+ - \`key\` → list reconciliation
120
+ `,
121
+
122
+ hooks: `
123
+ ## Hooks
124
+
125
+ React-compatible hooks backed by signals.
126
+
127
+ \`\`\`js
128
+ import { useState, useEffect, useMemo, useCallback, useRef, useReducer, createContext, useContext } from 'what';
129
+
130
+ // State
131
+ const [count, setCount] = useState(0);
132
+ setCount(5);
133
+ setCount(c => c + 1);
134
+
135
+ // Effect
136
+ useEffect(() => {
137
+ const id = setInterval(tick, 1000);
138
+ return () => clearInterval(id);
139
+ }, [dependency]);
140
+
141
+ // Memo
142
+ const expensive = useMemo(() => compute(a, b), [a, b]);
143
+
144
+ // Callback
145
+ const handler = useCallback(e => doStuff(e), [dep]);
146
+
147
+ // Ref
148
+ const ref = useRef(null);
149
+ h('input', { ref }); // ref.current = <input>
150
+
151
+ // Reducer
152
+ const [state, dispatch] = useReducer(reducer, initialState);
153
+
154
+ // Context
155
+ const ThemeCtx = createContext('light');
156
+ h(ThemeCtx.Provider, { value: 'dark' }, children);
157
+ const theme = useContext(ThemeCtx);
158
+ \`\`\`
159
+ `,
160
+
161
+ islands: `
162
+ ## Islands Architecture
163
+
164
+ Islands let you send zero JavaScript by default, hydrating only interactive parts.
165
+
166
+ \`\`\`js
167
+ import { island, Island } from 'what/server';
168
+
169
+ // Register an island with hydration strategy
170
+ island('cart', () => import('./islands/cart.js'), {
171
+ mode: 'action', // Hydrate on first interaction
172
+ });
173
+
174
+ // Use in a page
175
+ function Page() {
176
+ return h('div', null,
177
+ h('nav', null, 'Static nav — no JS'),
178
+ h(Island, { name: 'cart' }), // Interactive island
179
+ h('footer', null, 'Static footer — no JS'),
180
+ );
181
+ }
182
+ \`\`\`
183
+
184
+ ### Hydration Modes
185
+
186
+ - \`'idle'\`: Hydrate when browser is idle
187
+ - \`'visible'\`: Hydrate when visible (IntersectionObserver)
188
+ - \`'action'\`: Hydrate on first click/focus
189
+ - \`'media'\`: Hydrate when media query matches
190
+ - \`'load'\`: Hydrate immediately
191
+ - \`'static'\`: Never hydrate (server only)
192
+ `,
193
+
194
+ routing: `
195
+ ## Routing
196
+
197
+ File-based and programmatic routing.
198
+
199
+ \`\`\`js
200
+ import { Router, Link, navigate, route } from 'what/router';
201
+
202
+ // Declare routes
203
+ h(Router, {
204
+ routes: [
205
+ { path: '/', component: Home },
206
+ { path: '/about', component: About },
207
+ { path: '/users/:id', component: User },
208
+ { path: '/blog/*', component: BlogLayout },
209
+ ],
210
+ fallback: h(NotFound),
211
+ });
212
+
213
+ // Navigation
214
+ h(Link, { href: '/about' }, 'About');
215
+ navigate('/dashboard');
216
+ navigate('/login', { replace: true });
217
+
218
+ // Reactive route state
219
+ route.path(); // current path
220
+ route.params(); // { id: '123' }
221
+ route.query(); // { page: '1' }
222
+ \`\`\`
223
+
224
+ ### Nested Layouts
225
+
226
+ \`\`\`js
227
+ {
228
+ path: '/dashboard',
229
+ component: DashboardLayout,
230
+ children: [
231
+ { path: '', component: DashboardHome },
232
+ { path: 'settings', component: Settings },
233
+ ],
234
+ }
235
+ \`\`\`
236
+ `,
237
+
238
+ forms: `
239
+ ## Form Utilities
240
+
241
+ React Hook Form-like API.
242
+
243
+ \`\`\`js
244
+ import { useForm, rules, simpleResolver } from 'what';
245
+
246
+ function SignupForm() {
247
+ const { register, handleSubmit, formState } = useForm({
248
+ defaultValues: { email: '', password: '' },
249
+ resolver: simpleResolver({
250
+ email: [rules.required(), rules.email()],
251
+ password: [rules.required(), rules.minLength(8)],
252
+ }),
253
+ });
254
+
255
+ return h('form', { onSubmit: handleSubmit(onSubmit) },
256
+ h('input', { ...register('email') }),
257
+ h('input', { ...register('password'), type: 'password' }),
258
+ h('button', { type: 'submit' }, 'Submit'),
259
+ );
260
+ }
261
+ \`\`\`
262
+
263
+ ### Validation Rules
264
+
265
+ \`\`\`js
266
+ rules.required(message?)
267
+ rules.minLength(min, message?)
268
+ rules.maxLength(max, message?)
269
+ rules.min(min, message?)
270
+ rules.max(max, message?)
271
+ rules.pattern(regex, message?)
272
+ rules.email(message?)
273
+ rules.url(message?)
274
+ rules.match(field, message?)
275
+ rules.custom(validator)
276
+ \`\`\`
277
+ `,
278
+
279
+ dataFetching: `
280
+ ## Data Fetching
281
+
282
+ SWR-like hooks for data fetching.
283
+
284
+ \`\`\`js
285
+ import { useSWR, useQuery, useInfiniteQuery } from 'what';
286
+
287
+ // useSWR
288
+ const { data, error, isLoading, mutate, revalidate } = useSWR(
289
+ 'user-data',
290
+ (key) => fetch('/api/user').then(r => r.json()),
291
+ { revalidateOnFocus: true }
292
+ );
293
+
294
+ // useQuery (TanStack Query-like)
295
+ const { data, error, isLoading, refetch } = useQuery({
296
+ queryKey: ['todos', userId],
297
+ queryFn: ({ queryKey }) => fetchTodos(queryKey[1]),
298
+ staleTime: 5000,
299
+ cacheTime: 5 * 60 * 1000,
300
+ });
301
+
302
+ // useInfiniteQuery
303
+ const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
304
+ queryKey: ['posts'],
305
+ queryFn: ({ pageParam }) => fetchPosts(pageParam),
306
+ getNextPageParam: (lastPage) => lastPage.nextCursor,
307
+ initialPageParam: 0,
308
+ });
309
+ \`\`\`
310
+
311
+ ### Cache Management
312
+
313
+ \`\`\`js
314
+ import { invalidateQueries, prefetchQuery, setQueryData, clearCache } from 'what';
315
+
316
+ invalidateQueries('user-data');
317
+ prefetchQuery('user', fetcher);
318
+ setQueryData('user', { name: 'John' });
319
+ clearCache();
320
+ \`\`\`
321
+ `,
322
+
323
+ animation: `
324
+ ## Animation
325
+
326
+ Physics-based springs and tweens.
327
+
328
+ \`\`\`js
329
+ import { spring, tween, easings, useGesture } from 'what';
330
+
331
+ // Spring animation
332
+ const x = spring(0, { stiffness: 100, damping: 10 });
333
+ x.set(100); // Animate to 100
334
+ x.current(); // Current value
335
+ x.snap(50); // Immediately set to 50
336
+
337
+ // Tween animation
338
+ const t = tween(0, 100, {
339
+ duration: 300,
340
+ easing: easings.easeOutQuad,
341
+ });
342
+
343
+ // Gesture handling
344
+ useGesture(ref, {
345
+ onDrag: ({ deltaX, deltaY, velocity }) => {},
346
+ onSwipe: ({ direction }) => {},
347
+ onTap: ({ x, y }) => {},
348
+ onPinch: ({ scale }) => {},
349
+ });
350
+ \`\`\`
351
+ `,
352
+
353
+ accessibility: `
354
+ ## Accessibility
355
+
356
+ Built-in accessibility utilities.
357
+
358
+ \`\`\`js
359
+ import {
360
+ useFocusTrap, FocusTrap,
361
+ announce, announceAssertive,
362
+ SkipLink,
363
+ useAriaExpanded,
364
+ useRovingTabIndex,
365
+ VisuallyHidden,
366
+ Keys, onKey,
367
+ } from 'what';
368
+
369
+ // Focus trap for modals
370
+ const trap = useFocusTrap(modalRef);
371
+ trap.activate();
372
+ trap.deactivate();
373
+
374
+ // Or as component
375
+ h(FocusTrap, { active: isOpen }, h(Modal));
376
+
377
+ // Screen reader announcements
378
+ announce('Item added to cart');
379
+ announceAssertive('Error: Form invalid');
380
+
381
+ // Skip navigation
382
+ h(SkipLink, { href: '#main' }, 'Skip to content');
383
+
384
+ // ARIA helpers
385
+ const { expanded, buttonProps, panelProps } = useAriaExpanded(false);
386
+
387
+ // Keyboard navigation
388
+ const { getItemProps } = useRovingTabIndex(items.length);
389
+
390
+ // Key handling
391
+ h('input', { onKeyDown: onKey(Keys.Enter, submit) });
392
+ \`\`\`
393
+ `,
394
+
395
+ skeleton: `
396
+ ## Skeleton Loaders
397
+
398
+ Loading state components.
399
+
400
+ \`\`\`js
401
+ import { Skeleton, SkeletonText, SkeletonCard, Spinner, LoadingDots } from 'what';
402
+
403
+ // Basic skeleton
404
+ h(Skeleton, { width: 200, height: 20 })
405
+
406
+ // Circle avatar
407
+ h(Skeleton, { width: 50, height: 50, circle: true })
408
+
409
+ // Text lines
410
+ h(SkeletonText, { lines: 3 })
411
+
412
+ // Card placeholder
413
+ h(SkeletonCard, { imageHeight: 200 })
414
+
415
+ // Spinner
416
+ h(Spinner, { size: 24 })
417
+
418
+ // Loading dots
419
+ h(LoadingDots, { size: 8 })
420
+ \`\`\`
421
+
422
+ Variants: 'shimmer' (default), 'pulse', 'wave'
423
+ `,
424
+
425
+ ssr: `
426
+ ## Server-Side Rendering
427
+
428
+ SSR, SSG, and hybrid rendering.
429
+
430
+ \`\`\`js
431
+ import { renderToString, renderToStream, definePage, server } from 'what/server';
432
+
433
+ // Render to string
434
+ const html = await renderToString(h(App));
435
+
436
+ // Stream rendering
437
+ for await (const chunk of renderToStream(h(App))) {
438
+ response.write(chunk);
439
+ }
440
+
441
+ // Per-page config
442
+ export const page = definePage({
443
+ mode: 'static', // 'static' | 'server' | 'client' | 'hybrid'
444
+ });
445
+
446
+ // Server-only component
447
+ const Header = server(({ title }) => h('header', null, title));
448
+ \`\`\`
449
+ `,
450
+
451
+ cli: `
452
+ ## CLI Commands
453
+
454
+ \`\`\`bash
455
+ what dev # Dev server with HMR
456
+ what build # Production build
457
+ what preview # Preview production build
458
+ what generate # Static site generation
459
+ \`\`\`
460
+
461
+ ## Configuration
462
+
463
+ \`\`\`js
464
+ // what.config.js
465
+ export default {
466
+ mode: 'hybrid',
467
+ pagesDir: 'src/pages',
468
+ outDir: 'dist',
469
+ islands: true,
470
+ port: 3000,
471
+ };
472
+ \`\`\`
473
+ `,
474
+ };
475
+
476
+ // Create server
477
+ const server = new Server(
478
+ {
479
+ name: 'what-framework',
480
+ version: '0.1.0',
481
+ },
482
+ {
483
+ capabilities: {
484
+ tools: {},
485
+ },
486
+ }
487
+ );
488
+
489
+ // List available tools
490
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
491
+ return {
492
+ tools: [
493
+ {
494
+ name: 'what_overview',
495
+ description: 'Get an overview of What Framework',
496
+ inputSchema: {
497
+ type: 'object',
498
+ properties: {},
499
+ required: [],
500
+ },
501
+ },
502
+ {
503
+ name: 'what_signals',
504
+ description: 'Learn about signals and reactive primitives in What Framework',
505
+ inputSchema: {
506
+ type: 'object',
507
+ properties: {},
508
+ required: [],
509
+ },
510
+ },
511
+ {
512
+ name: 'what_components',
513
+ description: 'Learn about creating components and the h() function',
514
+ inputSchema: {
515
+ type: 'object',
516
+ properties: {},
517
+ required: [],
518
+ },
519
+ },
520
+ {
521
+ name: 'what_hooks',
522
+ description: 'Learn about React-compatible hooks (useState, useEffect, etc.)',
523
+ inputSchema: {
524
+ type: 'object',
525
+ properties: {},
526
+ required: [],
527
+ },
528
+ },
529
+ {
530
+ name: 'what_islands',
531
+ description: 'Learn about islands architecture and partial hydration',
532
+ inputSchema: {
533
+ type: 'object',
534
+ properties: {},
535
+ required: [],
536
+ },
537
+ },
538
+ {
539
+ name: 'what_routing',
540
+ description: 'Learn about file-based and programmatic routing',
541
+ inputSchema: {
542
+ type: 'object',
543
+ properties: {},
544
+ required: [],
545
+ },
546
+ },
547
+ {
548
+ name: 'what_forms',
549
+ description: 'Learn about form utilities and validation',
550
+ inputSchema: {
551
+ type: 'object',
552
+ properties: {},
553
+ required: [],
554
+ },
555
+ },
556
+ {
557
+ name: 'what_data_fetching',
558
+ description: 'Learn about data fetching with useSWR and useQuery',
559
+ inputSchema: {
560
+ type: 'object',
561
+ properties: {},
562
+ required: [],
563
+ },
564
+ },
565
+ {
566
+ name: 'what_animation',
567
+ description: 'Learn about animation primitives (spring, tween, gestures)',
568
+ inputSchema: {
569
+ type: 'object',
570
+ properties: {},
571
+ required: [],
572
+ },
573
+ },
574
+ {
575
+ name: 'what_accessibility',
576
+ description: 'Learn about accessibility utilities',
577
+ inputSchema: {
578
+ type: 'object',
579
+ properties: {},
580
+ required: [],
581
+ },
582
+ },
583
+ {
584
+ name: 'what_skeleton',
585
+ description: 'Learn about skeleton loaders and loading states',
586
+ inputSchema: {
587
+ type: 'object',
588
+ properties: {},
589
+ required: [],
590
+ },
591
+ },
592
+ {
593
+ name: 'what_ssr',
594
+ description: 'Learn about server-side rendering and static generation',
595
+ inputSchema: {
596
+ type: 'object',
597
+ properties: {},
598
+ required: [],
599
+ },
600
+ },
601
+ {
602
+ name: 'what_cli',
603
+ description: 'Learn about CLI commands and configuration',
604
+ inputSchema: {
605
+ type: 'object',
606
+ properties: {},
607
+ required: [],
608
+ },
609
+ },
610
+ {
611
+ name: 'what_search',
612
+ description: 'Search across all What Framework documentation',
613
+ inputSchema: {
614
+ type: 'object',
615
+ properties: {
616
+ query: {
617
+ type: 'string',
618
+ description: 'Search query',
619
+ },
620
+ },
621
+ required: ['query'],
622
+ },
623
+ },
624
+ ],
625
+ };
626
+ });
627
+
628
+ // Handle tool calls
629
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
630
+ const { name, arguments: args } = request.params;
631
+
632
+ switch (name) {
633
+ case 'what_overview':
634
+ return { content: [{ type: 'text', text: DOCS.overview }] };
635
+ case 'what_signals':
636
+ return { content: [{ type: 'text', text: DOCS.signals }] };
637
+ case 'what_components':
638
+ return { content: [{ type: 'text', text: DOCS.components }] };
639
+ case 'what_hooks':
640
+ return { content: [{ type: 'text', text: DOCS.hooks }] };
641
+ case 'what_islands':
642
+ return { content: [{ type: 'text', text: DOCS.islands }] };
643
+ case 'what_routing':
644
+ return { content: [{ type: 'text', text: DOCS.routing }] };
645
+ case 'what_forms':
646
+ return { content: [{ type: 'text', text: DOCS.forms }] };
647
+ case 'what_data_fetching':
648
+ return { content: [{ type: 'text', text: DOCS.dataFetching }] };
649
+ case 'what_animation':
650
+ return { content: [{ type: 'text', text: DOCS.animation }] };
651
+ case 'what_accessibility':
652
+ return { content: [{ type: 'text', text: DOCS.accessibility }] };
653
+ case 'what_skeleton':
654
+ return { content: [{ type: 'text', text: DOCS.skeleton }] };
655
+ case 'what_ssr':
656
+ return { content: [{ type: 'text', text: DOCS.ssr }] };
657
+ case 'what_cli':
658
+ return { content: [{ type: 'text', text: DOCS.cli }] };
659
+ case 'what_search': {
660
+ const query = args?.query?.toLowerCase() || '';
661
+ const results = [];
662
+
663
+ for (const [topic, content] of Object.entries(DOCS)) {
664
+ if (content.toLowerCase().includes(query)) {
665
+ results.push({
666
+ topic,
667
+ excerpt: content.substring(0, 200) + '...',
668
+ });
669
+ }
670
+ }
671
+
672
+ if (results.length === 0) {
673
+ return {
674
+ content: [{ type: 'text', text: `No results found for "${query}"` }],
675
+ };
676
+ }
677
+
678
+ const text = results
679
+ .map((r) => `## ${r.topic}\n${r.excerpt}`)
680
+ .join('\n\n---\n\n');
681
+
682
+ return { content: [{ type: 'text', text }] };
683
+ }
684
+ default:
685
+ throw new Error(`Unknown tool: ${name}`);
686
+ }
687
+ });
688
+
689
+ // Start server
690
+ async function main() {
691
+ const transport = new StdioServerTransport();
692
+ await server.connect(transport);
693
+ console.error('What Framework MCP Server running on stdio');
694
+ }
695
+
696
+ main().catch(console.error);