Skip to content

Commit 92109cb

Browse files
authored
Merge pull request #6 from deeleeramone/feature/fastmcp-3
refactor for fastmcp3
2 parents e882a5e + 09e48ae commit 92109cb

32 files changed

Lines changed: 2049 additions & 154 deletions

File tree

.github/workflows/test-pywry.yml

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,12 @@ jobs:
103103
if: runner.os == 'Linux'
104104
uses: dtolnay/rust-toolchain@stable
105105

106+
- name: Set up Rust (Windows ARM64)
107+
if: runner.os == 'Windows' && runner.arch == 'ARM64'
108+
uses: dtolnay/rust-toolchain@stable
109+
with:
110+
targets: aarch64-pc-windows-msvc
111+
106112
- name: Install Linux dependencies
107113
if: runner.os == 'Linux'
108114
run: |
@@ -114,21 +120,20 @@ jobs:
114120
uses: actions/cache@v4
115121
with:
116122
path: C:/vcpkg/installed
117-
key: vcpkg-arm64-windows-openssl-lua-${{ hashFiles('.github/workflows/test-pywry.yml') }}
123+
key: vcpkg-arm64-windows-openssl-static-md-lua-${{ hashFiles('.github/workflows/test-pywry.yml') }}
118124
restore-keys: |
119-
vcpkg-arm64-windows-openssl-lua-
125+
vcpkg-arm64-windows-openssl-static-md-lua-
120126
121127
- name: Install OpenSSL via vcpkg (Windows ARM)
122128
if: runner.os == 'Windows' && runner.arch == 'ARM64'
123129
shell: pwsh
124130
run: |
125131
$env:VCPKG_ROOT = "$env:VCPKG_INSTALLATION_ROOT"
126-
& "$env:VCPKG_ROOT\vcpkg" install openssl:arm64-windows
127-
$opensslDir = "$env:VCPKG_ROOT\installed\arm64-windows"
132+
& "$env:VCPKG_ROOT\vcpkg" install openssl:arm64-windows-static-md
133+
$opensslDir = "$env:VCPKG_ROOT\installed\arm64-windows-static-md"
128134
echo "OPENSSL_DIR=$opensslDir" >> $env:GITHUB_ENV
129135
echo "OPENSSL_LIB_DIR=$opensslDir\lib" >> $env:GITHUB_ENV
130136
echo "OPENSSL_INCLUDE_DIR=$opensslDir\include" >> $env:GITHUB_ENV
131-
echo "$opensslDir\bin" >> $env:GITHUB_PATH
132137
133138
- name: Setup build environment (Windows ARM)
134139
if: runner.os == 'Windows' && runner.arch == 'ARM64'
@@ -145,6 +150,13 @@ jobs:
145150
echo "LUA_LIB=$luaDir\lib" >> $env:GITHUB_ENV
146151
echo "LUA_INC=$luaDir\include" >> $env:GITHUB_ENV
147152
153+
- name: Build cryptography from source (Windows ARM)
154+
if: runner.os == 'Windows' && runner.arch == 'ARM64'
155+
shell: pwsh
156+
run: |
157+
pip install --no-cache-dir --no-binary=cryptography cryptography
158+
python -c "from cryptography.hazmat.bindings._rust import openssl; print('cryptography OK')"
159+
148160
- name: Install dependencies
149161
run: |
150162
python -m pip install --upgrade pip
@@ -178,6 +190,7 @@ jobs:
178190
env:
179191
PYWRY_HEADLESS: "1"
180192
PYWRY_DEPLOY__STATE_BACKEND: "memory"
193+
PYTHONUTF8: "1"
181194
run: |
182195
python -m pytest -c pytest.ini tests/ -v --tb=short --ignore=tests/test_state_redis_integration.py --ignore=tests/test_auth_rbac_integration.py --ignore=tests/test_deploy_mode_integration.py --ignore=tests/test_e2e_deploy_mode.py --ignore=tests/test_e2e_rbac_widgets.py -m "not redis and not container"
183196
@@ -186,6 +199,7 @@ jobs:
186199
env:
187200
PYWRY_HEADLESS: "1"
188201
PYWRY_DEPLOY__STATE_BACKEND: "memory"
202+
PYTHONUTF8: "1"
189203
run: |
190204
python -m pytest -c pytest.ini tests/ -v --tb=short --ignore=tests/test_state_redis_integration.py --ignore=tests/test_auth_rbac_integration.py --ignore=tests/test_deploy_mode_integration.py --ignore=tests/test_e2e_deploy_mode.py --ignore=tests/test_e2e_rbac_widgets.py --ignore=tests/test_inline_ssl.py -m "not redis and not container"
191205

pywry/docs/docs/guides/tauri-plugins.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,16 @@ sequenceDiagram
1111
participant Sub as __main__.py (subprocess)
1212
participant Tauri as Tauri Engine
1313
14-
App->>RT: set_tauri_plugins(["dialog", "fs", "notification"])
15-
RT->>Sub: env PYWRY_TAURI_PLUGINS="dialog,fs,notification"
16-
Sub->>Sub: _load_plugins() — check flags, import modules
17-
Sub->>Tauri: builder.build(plugins=[dialog.init(), fs.init(), notification.init()])
18-
Tauri->>Tauri: Register plugins + grant capabilities
14+
App->>RT: set_tauri_plugins([...])
15+
Note over App,RT: ["dialog", "fs", "notification"]
16+
RT->>Sub: PYWRY_TAURI_PLUGINS env var
17+
Note over RT,Sub: "dialog,fs,notification"
18+
Sub->>Sub: _load_plugins()
19+
Note right of Sub: Check flags & import modules
20+
Sub->>Tauri: builder.build(plugins=[...])
21+
Note over Sub,Tauri: dialog.init(), fs.init(), notification.init()
22+
Tauri->>Tauri: Register plugins
23+
Note right of Tauri: Grant capabilities
1924
```
2025

2126
1. You list the plugins you want in config (Python, TOML, or env var).

pywry/docs/docs/mcp/examples.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,86 @@ Toolbar events that don't have a `callbacks` entry are still captured and queued
417417

418418
---
419419

420+
## Autonomous Building
421+
422+
The agentic tools use LLM sampling to generate complete widget apps from plain-English descriptions.
423+
They require a sampling-capable client (Claude, etc.) and produce structured `WidgetPlan` output.
424+
425+
### Quick build — one call
426+
427+
**Prompt:** *Build a task tracker with add/remove buttons and a progress bar.*
428+
429+
```json title="build_app"
430+
{
431+
"description": "A task tracker with add/remove buttons and a progress bar",
432+
"open_window": false
433+
}
434+
```
435+
436+
Returns a `widget_id`, complete `python_code`, and a `files` map ready to save to disk.
437+
438+
### Plan first, then inspect
439+
440+
**Step 1 — plan:**
441+
442+
```json title="plan_widget"
443+
{
444+
"description": "A crypto price dashboard showing BTC, ETH, and SOL with 24h change indicators"
445+
}
446+
```
447+
448+
Returns a `WidgetPlan` JSON the agent can inspect or modify before building.
449+
450+
**Step 2 — build from the plan:**
451+
452+
```json title="build_app"
453+
{
454+
"description": "A crypto price dashboard showing BTC, ETH, and SOL with 24h change indicators"
455+
}
456+
```
457+
458+
### Export an existing widget as a project
459+
460+
After working interactively with a widget:
461+
462+
```json title="export_project"
463+
{
464+
"widget_ids": ["w-abc123"],
465+
"project_name": "my_dashboard",
466+
"output_dir": "/Users/me/projects/my_dashboard"
467+
}
468+
```
469+
470+
Writes `main.py`, `requirements.txt`, `README.md`, and `widgets/w_abc123.py` to disk.
471+
472+
### Interactive scaffolding
473+
474+
Use `scaffold_app` to guide the user through requirements step-by-step via prompts:
475+
476+
```
477+
scaffold_app()
478+
```
479+
480+
The agent will elicit:
481+
482+
1. App title
483+
2. Description
484+
3. Display mode (native / inline)
485+
4. Libraries needed (Plotly, AG-Grid, neither)
486+
5. Toolbar placement
487+
488+
Then automatically delegates to `plan_widget` with the gathered requirements.
489+
490+
!!! tip "Which tool to use"
491+
| Situation | Tool |
492+
|:---|:---|
493+
| Clear description in hand | `build_app` |
494+
| Want to inspect the plan first | `plan_widget``build_app` |
495+
| Want to save to disk | `export_project` |
496+
| User isn't sure what they want | `scaffold_app` |
497+
498+
---
499+
420500
## Tips for Effective Prompts
421501

422502
| Do | Don't |

pywry/docs/docs/mcp/index.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,9 @@ The MCP server bridges AI agents and PyWry's rendering engine:
7474
```mermaid
7575
flowchart LR
7676
A["AI Agent<br>(Claude, etc.)"] <-->|"MCP Protocol<br>(stdio / SSE)"| B["PyWry MCP Server"]
77-
B --> C["Tools<br>25 widget operations"]
77+
B --> C["Tools<br>29 widget operations"]
7878
B --> D["Resources<br>Docs, source, exports"]
79-
B --> E["Skills<br>11 guidance prompts"]
79+
B --> E["Skills<br>12 guidance prompts"]
8080
C --> F["PyWry Widgets<br>(native or browser)"]
8181
D --> B
8282
E --> B
@@ -104,9 +104,9 @@ In headless mode, `list_widgets` returns URLs like `http://127.0.0.1:PORT/widget
104104

105105
The MCP server exposes three types of capabilities:
106106

107-
### Tools (25)
107+
### Tools (29)
108108

109-
Operations the agent can call — creating widgets, updating content, managing state. Organized into five groups:
109+
Operations the agent can call — creating widgets, updating content, managing state. Organized into six groups:
110110

111111
| Group | Tools | Purpose |
112112
|:---|:---|:---|
@@ -115,6 +115,7 @@ Operations the agent can call — creating widgets, updating content, managing s
115115
| **Widget manipulation** | `set_content`, `set_style`, `show_toast`, `update_theme`, `inject_css`, `remove_css`, `navigate`, `download`, `update_plotly`, `update_marquee`, `update_ticker_item`, `send_event` | Modify existing widgets |
116116
| **Widget management** | `list_widgets`, `get_events`, `destroy_widget` | Track and clean up widgets |
117117
| **Resources & export** | `get_component_docs`, `get_component_source`, `export_widget`, `list_resources` | Documentation and code generation |
118+
| **Autonomous building** | `plan_widget`, `build_app`, `export_project`, `scaffold_app` | LLM-powered end-to-end app creation |
118119

119120
### Resources
120121

@@ -132,7 +133,7 @@ Read-only data the agent can access via `pywry://` URIs:
132133

133134
### Prompts (Skills)
134135

135-
11 guidance prompts that teach the agent how to use PyWry effectively:
136+
12 guidance prompts that teach the agent how to use PyWry effectively:
136137

137138
| Skill | What it teaches |
138139
|:---|:---|
@@ -147,6 +148,7 @@ Read-only data the agent can access via `pywry://` URIs:
147148
| `data_visualization` | Plotly charts, AG Grid tables, live data patterns |
148149
| `forms_and_inputs` | Form building with validation and event collection |
149150
| `modals` | Modal dialogs — schema, sizes, open/close/reset |
151+
| `autonomous_building` | End-to-end app generation with `plan_widget`, `build_app`, `export_project`, `scaffold_app` |
150152

151153
## Next Steps
152154

pywry/docs/docs/mcp/setup.md

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,9 @@ All MCP settings can be configured via environment variables (`PYWRY_MCP__` pref
235235
| `log_tools` | `PYWRY_MCP__LOG_TOOLS` | `false` | Log tool calls |
236236
| `log_level` | `PYWRY_MCP__LOG_LEVEL` | `INFO` | Log level |
237237
| `skills_auto_load` | `PYWRY_MCP__SKILLS_AUTO_LOAD` | `true` | Auto-load skills on connect |
238-
| `include_tags` | `PYWRY_MCP__INCLUDE_TAGS` | `[]` | Only expose tools with these tags |
239-
| `exclude_tags` | `PYWRY_MCP__EXCLUDE_TAGS` | `[]` | Exclude tools with these tags |
238+
239+
!!! note "Removed in FastMCP V3"
240+
The `include_tags` and `exclude_tags` settings were removed when PyWry migrated to FastMCP V3. Tool filtering is no longer supported via config.
240241

241242
### Config File
242243

@@ -269,12 +270,37 @@ debug = true
269270
}
270271
```
271272

273+
## Installing Skills
274+
275+
PyWry ships bundled skills that can be copied into your MCP client's skills directory:
276+
277+
```bash
278+
# Preview what will be installed (dry run)
279+
pywry install-skills --dry-run
280+
281+
# Install to all detected MCP vendor directories
282+
pywry install-skills
283+
284+
# Install to a specific directory
285+
pywry install-skills --target custom --custom-dir /path/to/skills
286+
287+
# Overwrite existing skill files
288+
pywry install-skills --overwrite
289+
290+
# Or via the module
291+
python -m pywry.mcp install-skills --dry-run
292+
```
293+
294+
The command copies skill markdown files from PyWry's bundled `pywry/mcp/skills/` directory into the appropriate vendor location for your MCP client (Claude Desktop, Cursor, etc.).
295+
296+
---
297+
272298
## Verifying the Setup
273299

274300
1. Restart Claude Desktop after editing the config
275301
2. Open a new conversation
276302
3. Ask: *"What PyWry tools do you have available?"*
277-
4. Claude should list 25 tools
303+
5. Claude should list 29 tools
278304

279305
You can also test manually:
280306

pywry/docs/docs/mcp/skills.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Skills are **lazy-loaded** from markdown files on disk and cached in memory (LRU
3030
| `data_visualization` | Medium | Plotly charts, AG Grid tables, Marquee tickers, live data polling and event-driven update patterns |
3131
| `forms_and_inputs` | Medium | Form building with TextInput, Select, Toggle, etc. — validation patterns, event-based data collection |
3232
| `modals` | Medium | Modal dialog schemas — sizes, nested components, open/close events, JS API, `reset_on_close` behavior |
33+
| `autonomous_building` | Medium | End-to-end app generation — `plan_widget`, `build_app`, `export_project`, `scaffold_app` workflows and chaining patterns |
3334

3435
### The Component Reference
3536

@@ -146,13 +147,14 @@ Most of these have dedicated tools (e.g., `set_content`, `inject_css`). The `sen
146147

147148
A typical interaction pattern:
148149

149-
1. **Agent list tools** → sees 25 available tools
150+
1. **Agent list tools** → sees 29 available tools
150151
2. **Agent calls `get_skills()`** → receives the skill list with descriptions
151152
3. **Agent calls `get_skills(skill="component_reference")`** → loads the mandatory component docs
152153
4. **Agent calls `create_widget`** → builds widget JSON using component reference as guide
153154
5. **Agent calls `get_events`** → reads user interactions
154155
6. **Agent calls `set_content` / `set_style`** → updates the widget in response
155156
7. **Agent calls `export_widget`** → generates Python code the user can save
157+
8. **Agent calls `build_app`** *(optional)* → fully autonomous end-to-end build from a description
156158

157159
The skills ensure the agent knows the exact JSON structure, event naming conventions, and available properties before making tool calls.
158160

0 commit comments

Comments
 (0)