@pyreon/mcp 0.5.0 → 0.5.2

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.
package/README.md ADDED
@@ -0,0 +1,329 @@
1
+ # @pyreon/mcp
2
+
3
+ Model Context Protocol server for AI-assisted Pyreon development. Gives AI coding assistants direct access to Pyreon's API reference, code validation, React-to-Pyreon migration, and project scanning.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ bun add -d @pyreon/mcp
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```bash
14
+ bunx @pyreon/mcp # starts stdio MCP server
15
+ ```
16
+
17
+ ## IDE Integration
18
+
19
+ ### Claude Code
20
+
21
+ ```json
22
+ // .mcp.json (project root)
23
+ {
24
+ "mcpServers": {
25
+ "pyreon": {
26
+ "command": "bunx",
27
+ "args": ["@pyreon/mcp"]
28
+ }
29
+ }
30
+ }
31
+ ```
32
+
33
+ ### Cursor
34
+
35
+ ```json
36
+ // .cursor/mcp.json
37
+ {
38
+ "mcpServers": {
39
+ "pyreon": {
40
+ "command": "bunx",
41
+ "args": ["@pyreon/mcp"]
42
+ }
43
+ }
44
+ }
45
+ ```
46
+
47
+ ### Windsurf
48
+
49
+ ```json
50
+ // .windsurf/mcp.json (same format as Cursor)
51
+ {
52
+ "mcpServers": {
53
+ "pyreon": {
54
+ "command": "bunx",
55
+ "args": ["@pyreon/mcp"]
56
+ }
57
+ }
58
+ }
59
+ ```
60
+
61
+ ## Tools
62
+
63
+ ### `get_api` — Look up any Pyreon API
64
+
65
+ ```
66
+ get_api({ package: "reactivity", symbol: "signal" })
67
+ ```
68
+
69
+ Returns signature, usage example, common mistakes. Covers all `@pyreon/*` packages.
70
+
71
+ **Example response:**
72
+
73
+ ```
74
+ ## @pyreon/reactivity — signal
75
+
76
+ **Signature:**
77
+ signal<T>(initialValue: T): Signal<T>
78
+
79
+ **Usage:**
80
+ const count = signal(0)
81
+ count() // read: 0
82
+ count.set(1) // write
83
+ count.update(n => n + 1) // update
84
+
85
+ **Common mistakes:**
86
+ - Using .value instead of calling the signal: count() not count.value
87
+ - Forgetting to call signal() when reading in JSX: {count()} not {count}
88
+ ```
89
+
90
+ ### `validate` — Check code for anti-patterns
91
+
92
+ ```
93
+ validate({ code: "import { useState } from 'react'" })
94
+ ```
95
+
96
+ Returns diagnostics for React patterns, wrong imports, and common Pyreon mistakes.
97
+
98
+ ### `migrate_react` — Convert React code to Pyreon
99
+
100
+ ```
101
+ migrate_react({
102
+ code: `
103
+ import { useState, useEffect } from "react"
104
+
105
+ function Timer() {
106
+ const [seconds, setSeconds] = useState(0)
107
+
108
+ useEffect(() => {
109
+ const id = setInterval(() => setSeconds(s => s + 1), 1000)
110
+ return () => clearInterval(id)
111
+ }, [])
112
+
113
+ return <div className="timer">{seconds}s</div>
114
+ }
115
+ `
116
+ })
117
+ ```
118
+
119
+ **Response:**
120
+
121
+ ```tsx
122
+ import { signal, effect } from "@pyreon/reactivity"
123
+
124
+ function Timer() {
125
+ const seconds = signal(0)
126
+
127
+ effect(() => {
128
+ const id = setInterval(() => seconds.update(s => s + 1), 1000)
129
+ return () => clearInterval(id)
130
+ })
131
+
132
+ return <div class="timer">{seconds()}s</div>
133
+ }
134
+ ```
135
+
136
+ ### `diagnose` — Parse error messages
137
+
138
+ ```
139
+ diagnose({ error: "TypeError: count is not a function" })
140
+ ```
141
+
142
+ **Response:**
143
+
144
+ ```
145
+ **Cause:** Signal accessed without calling it — signals are functions.
146
+ **Fix:** Call the signal to read its value: count() instead of count.
147
+
148
+ // Wrong:
149
+ <div>{count}</div>
150
+
151
+ // Right:
152
+ <div>{count()}</div>
153
+ ```
154
+
155
+ ### `get_routes` — List project routes
156
+
157
+ ```
158
+ get_routes({})
159
+ ```
160
+
161
+ Scans for `createRouter([...])` and `const routes = [...]` patterns.
162
+
163
+ ### `get_components` — List components with props and signals
164
+
165
+ ```
166
+ get_components({})
167
+ ```
168
+
169
+ Returns component names, file paths, props, and signal usage.
170
+
171
+ ## Pyreon Patterns for AI
172
+
173
+ These are the key patterns the MCP server teaches AI assistants:
174
+
175
+ ### State management
176
+
177
+ ```tsx
178
+ // React // Pyreon
179
+ const [x, setX] = useState(0) const x = signal(0)
180
+ const val = useMemo(() => a+b, [a,b]) const val = computed(() => a()+b())
181
+ useEffect(() => { ... }, [dep]) effect(() => { ... })
182
+ ```
183
+
184
+ ### JSX differences
185
+
186
+ ```tsx
187
+ // React // Pyreon
188
+ <div className="box" /> <div class="box" />
189
+ <label htmlFor="name" /> <label for="name" />
190
+ <input onChange={handler} /> <input onInput={handler} />
191
+ {condition && <Child />} <Show when={condition}><Child /></Show>
192
+ {items.map(i => <li key={i.id}>)} <For each={items} by={i => i.id}>{i => <li>}</For>
193
+ ```
194
+
195
+ ### Component patterns
196
+
197
+ ```tsx
198
+ // React: hooks, re-renders entire component
199
+ function Counter() {
200
+ const [count, setCount] = useState(0)
201
+ return <button onClick={() => setCount(c => c + 1)}>{count}</button>
202
+ }
203
+
204
+ // Pyreon: signals, fine-grained DOM updates (no re-render)
205
+ function Counter() {
206
+ const count = signal(0)
207
+ return <button onClick={() => count.update(c => c + 1)}>{count()}</button>
208
+ }
209
+ ```
210
+
211
+ ### Refs
212
+
213
+ ```tsx
214
+ // React
215
+ const ref = useRef<HTMLDivElement>(null)
216
+ <div ref={ref} />
217
+
218
+ // Pyreon: object ref
219
+ const ref = { current: null as HTMLDivElement | null }
220
+ <div ref={ref} />
221
+
222
+ // Pyreon: callback ref
223
+ <div ref={(el) => { myElement = el }} />
224
+ ```
225
+
226
+ ### Context
227
+
228
+ ```tsx
229
+ // React
230
+ const ThemeCtx = React.createContext("light")
231
+ <ThemeCtx.Provider value="dark"><App /></ThemeCtx.Provider>
232
+ const theme = useContext(ThemeCtx)
233
+
234
+ // Pyreon
235
+ const ThemeCtx = createContext<string>("light")
236
+ <ThemeCtx.Provider value="dark"><App /></ThemeCtx.Provider>
237
+ const theme = useContext(ThemeCtx) // same API
238
+ ```
239
+
240
+ ### Lifecycle
241
+
242
+ ```tsx
243
+ // React
244
+ useEffect(() => {
245
+ const handler = () => { ... }
246
+ window.addEventListener("resize", handler)
247
+ return () => window.removeEventListener("resize", handler)
248
+ }, [])
249
+
250
+ // Pyreon
251
+ onMount(() => {
252
+ const handler = () => { ... }
253
+ window.addEventListener("resize", handler)
254
+ return () => window.removeEventListener("resize", handler) // cleanup
255
+ })
256
+ ```
257
+
258
+ ### Routing
259
+
260
+ ```tsx
261
+ // React Router
262
+ <BrowserRouter>
263
+ <Routes>
264
+ <Route path="/" element={<Home />} />
265
+ <Route path="/user/:id" element={<User />} />
266
+ </Routes>
267
+ </BrowserRouter>
268
+
269
+ // Pyreon Router
270
+ const router = createRouter({
271
+ routes: [
272
+ { path: "/", component: Home },
273
+ { path: "/user/:id", component: User },
274
+ ],
275
+ })
276
+ <RouterProvider router={router}>
277
+ <RouterView />
278
+ </RouterProvider>
279
+ ```
280
+
281
+ ### Lists
282
+
283
+ ```tsx
284
+ // React: key on element
285
+ {items.map(item => (
286
+ <li key={item.id}>{item.name}</li>
287
+ ))}
288
+
289
+ // Pyreon: by prop on For (not key — JSX extracts key specially)
290
+ <For each={items} by={item => item.id}>
291
+ {item => <li>{item.name}</li>}
292
+ </For>
293
+ ```
294
+
295
+ ### Lazy loading
296
+
297
+ ```tsx
298
+ // React
299
+ const Dashboard = React.lazy(() => import("./Dashboard"))
300
+ <Suspense fallback={<Loading />}>
301
+ <Dashboard />
302
+ </Suspense>
303
+
304
+ // Pyreon
305
+ const Dashboard = lazy(() => import("./Dashboard"))
306
+ <Suspense fallback={<Loading />}>
307
+ <Dashboard />
308
+ </Suspense>
309
+ ```
310
+
311
+ ### Islands (partial hydration)
312
+
313
+ ```tsx
314
+ // Server: define island
315
+ import { island } from "@pyreon/server"
316
+ const Counter = island(() => import("./Counter"), {
317
+ name: "Counter",
318
+ hydrate: "visible", // load | idle | visible | media(...) | never
319
+ })
320
+
321
+ // Use in page (renders static HTML, hydrates on client)
322
+ <Counter initial={0} />
323
+
324
+ // Client: register islands
325
+ import { hydrateIslands } from "@pyreon/server/client"
326
+ hydrateIslands({
327
+ Counter: () => import("./Counter"),
328
+ })
329
+ ```