@pyreon/mcp 0.5.0 → 0.5.1
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 +329 -0
- package/lib/analysis/index.js.html +1 -1
- package/lib/index.js +2 -160
- package/lib/index.js.map +1 -1
- package/lib/types/index.d.ts +2 -169
- package/lib/types/index.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/project-scanner.ts +8 -213
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
|
+
```
|