Atlas / MCP servers / deeleeramone / PyWry

PyWryBLOCK

mcp/deeleeramone/pywry

PyWry is a cross-platform app factory, rendering engine and UI toolkit for Python that produces native desktop, web, and notebook experiences from a single API.

Verdict
BLOCK
Grade
F
Trust score
42 /100
Exposed tools
67 39r · 24w · 4d
Transport
sse · stdio · streamable-http
License
Apache-2.0
Stars
97
01

Overview

From the repository's own README, as read at the audited commit. Badges and raw HTML are left out.

PyWry is a cross-platform rendering engine and desktop UI toolkit for Python. One API, three output targets:

  • Native window — OS webview via PyTauri. Not Qt, not Electron. Use unrestricted HTML/CSS/JS.
  • Jupyter widget — anywidget + FastAPI + WebSocket, works in JupyterLab, VS Code, and Colab.
  • Browser tab — FastAPI server with Redis state backend for horizontal scaling.

Build Once, Render Anywhere: Prototype interactive data apps in a Jupyter Notebook, easily deploy them as web apps, and seamlessly compile them into secure, lightweight standalone desktop executables via pywry[freeze].

Installation

Python 3.10–3.14, virtual environment recommended.

pip install pywry

Core extras:

Chat provider extras:

Read from source at commit d870349f2c13OBSERVED · 2026-10-07
Advertisement
02

Connect

Built from this server's own package name, version and transport as found in its source — not copied from anyone's documentation, so it cannot drift against a page we do not control. Replace the environment placeholders with a token scoped to the least it needs.

claude-code
claude mcp add pywry-frontend-assets --env GEMINI_API_KEY=${GEMINI_API_KEY} --env NVIDIA_API_KEY=${NVIDIA_API_KEY} --env PYWRY_DEPLOY__AUTH_ENABLED=${PYWRY_DEPLOY__AUTH_ENABLED} --env PYWRY_OAUTH2__CLIENT_ID=${PYWRY_OAUTH2__CLIENT_ID} -- npx -y pywry-frontend-assets
claude-desktop
{
  "mcpServers": {
    "pywry-frontend-assets": {
      "command": "npx",
      "args": [
        "-y",
        "pywry-frontend-assets"
      ],
      "env": {
        "GEMINI_API_KEY": "${GEMINI_API_KEY}",
        "NVIDIA_API_KEY": "${NVIDIA_API_KEY}",
        "PYWRY_DEPLOY__AUTH_ENABLED": "${PYWRY_DEPLOY__AUTH_ENABLED}",
        "PYWRY_OAUTH2__CLIENT_ID": "${PYWRY_OAUTH2__CLIENT_ID}"
      }
    }
  }
}
03

Exposed tools (67)

39 read · 24 write · 4 destructive. Blast radius: 4 tools can delete or overwrite — an agent that can be talked into calling a tool can be talked into calling this one.

ToolRiskDescription
build_divread
build_ticker_itemread
chat_get_historyread
chat_manage_threadread
chat_register_commandread
chat_send_messagewrite
chat_set_typingwriteShow or hide the typing indicator in a chat widget.
chat_stop_generationwrite
chat_update_settingswrite
create_chat_widgetwrite
create_widgetwrite
destroy_widgetdestructiveDestroy a widget and clean up resources.
downloadwriteTrigger a file download in the browser.
export_widgetread
get_component_docsread
get_component_sourceread
get_eventsread
get_skillsread
get_widget_appread
inject_cssread
list_resourcesread
list_widgetsreadList all active widgets with their URLs.
navigatereadNavigate the widget to a new URL (client-side redirect).
remove_cssdestructiveRemove previously injected CSS by style_id.
send_eventwrite
set_contentwrite
set_stylewrite
show_dataframeread
show_plotlyread
show_toastreadDisplay a toast notification in the widget.
show_tvchartread
tvchart_add_indicatorwrite
tvchart_add_markerswrite
tvchart_add_price_linewrite
tvchart_add_serieswrite
tvchart_apply_optionswrite
tvchart_auto_scalereadToggle auto-scale on the price axis. Emits ``tvchart:auto-scale``.
tvchart_change_intervalread
tvchart_chart_typeread
tvchart_compareread
tvchart_drawing_toolread
tvchart_fit_contentreadFit all bars to the visible area. Emits ``tvchart:time-scale`` with ``{fitContent: true}``.
tvchart_fullscreenreadToggle chart fullscreen mode. Emits ``tvchart:fullscreen``.
tvchart_list_indicatorsread
tvchart_log_scalereadToggle the logarithmic price scale. Emits ``tvchart:log-scale``.
tvchart_open_layoutreadOpen the layout picker dialog. Emits ``tvchart:open-layout``.
tvchart_redoreadRedo the last undone chart action. Emits ``tvchart:redo``.
tvchart_remove_indicatordestructive
tvchart_remove_seriesdestructive
tvchart_request_stateread
tvchart_save_layoutwrite
tvchart_save_statewrite
tvchart_screenshotreadTake a screenshot of the chart. Emits ``tvchart:screenshot``.
tvchart_set_visible_rangewrite
tvchart_show_indicatorsreadOpen the indicator picker panel. Emits ``tvchart:show-indicators``.
tvchart_show_settingsreadOpen the chart settings modal. Emits ``tvchart:show-settings``.
tvchart_symbol_searchread
tvchart_time_rangeread
tvchart_time_range_pickerreadOpen the date-range picker dialog. Emits ``tvchart:time-range-picker``.
tvchart_toggle_dark_modereadToggle the chart
tvchart_undoreadUndo the last chart action. Emits ``tvchart:undo``.
tvchart_update_barwrite
tvchart_update_serieswrite
update_marqueewrite
update_plotlywriteUpdate an existing Plotly chart with new data/layout.
update_themewriteSwitch widget theme to dark/light/system.
update_ticker_itemwrite
04

Trust audit

BLOCKgrade F · trust 42/100 Do not install this without reading the findings. The audit found something that could harm you or your machine.

LayerWhat it checksResult
L0Provenance & inventoryWARN
L1Static analysis of the codeFAIL
L2Instruction surface (what it tells the agent)WARN
L3Class-specific surfaceWARN
L4Behavioural (sandbox)SKIPPED

What the source does

Filesystem
declared (7 observation(s))
Network
declared (6 observation(s))
Shell
declared (9 observation(s))
Dependencies
not all pinned
Secrets in source
found

Findings (25)

HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
pywry/pywry/frontend/src/aggrid-defaults.js:174
return eval(getterExpr);
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
pywry/pywry/frontend/src/aggrid-defaults.js:196
return eval(formatterExpr);
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
pywry/pywry/frontend/src/aggrid-defaults.js:222
return eval(setterExpr);
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
pywry/pywry/frontend/src/main.js:113
eval(event.payload.script);
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
pywry/pywry/frontend/src/ws-bridge.js:532
btn.click = eval('(' + btn.click + ')');
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
MEDIUMInventory / provenance · inv.binary · CWE-1104
pywry/pywry/frontend/assets/icon.icns
icon.icns
Why it matters. a compiled or binary member cannot be reviewed from source
Fix. ship source, or explain the binary in the README
MEDIUMInventory / provenance · inv.binary · CWE-1104
pywry/pywry/frontend/assets/tauri-icons/icon.icns
icon.icns
Why it matters. a compiled or binary member cannot be reviewed from source
Fix. ship source, or explain the binary in the README
MEDIUMCode injection · code.dynamic_import · CWE-78, CWE-94, CWE-95
pywry/pywry/__main__.py:242
mod = importlib.import_module(module_path)
MEDIUMCode injection · code.dynamic_import · CWE-78, CWE-94, CWE-95
pywry/pywry/_freeze.py:93
ext_mod = importlib.import_module(mod_name)
MEDIUMCode injection · code.dynamic_import · CWE-78, CWE-94, CWE-95
pywry/pywry/chat/providers/__init__.py:209
module = importlib.import_module(module_name, package=__package__)
MEDIUMCode injection · code.dynamic_import · CWE-78, CWE-94, CWE-95
pywry/pywry/state/sqlite.py:47
module = importlib.import_module(f"{name}.dbapi2")
MEDIUMNetwork egress · net.beacon_words · CWE-200, CWE-319
pywry/pywry/inline.py:599
"beacon",
MEDIUMNetwork egress · net.beacon_words · CWE-200, CWE-319
pywry/pywry/inline.py:1018
widget_id: str, _request: Request, reason: str = "beacon"
MEDIUMHard-coded secrets · secret.db_uri · CWE-798, CWE-321
pywry/tests/conftest.py:837
return f"redis://{username}:{password}@{host}:{port}/0"
MEDIUMHard-coded secrets · secret.db_uri · CWE-798, CWE-321
pywry/tests/test_auth_rbac_integration.py:1517
bad_url = f"redis://admin:wrongpassword@{host}:{port}/0"
MEDIUMHard-coded secrets · secret.db_uri · CWE-798, CWE-321
pywry/tests/test_auth_rbac_integration.py:1535
bad_url = f"redis://fakeuser:fakepass@{host}:{port}/0"
MEDIUMHard-coded secrets · secret.private_key · CWE-798, CWE-321
pywry/tests/test_toolbar.py:3958
ssh_key = """-----BEGIN OPENSSH PRIVATE KEY-----
MEDIUMHard-coded secrets · secret.private_key · CWE-798, CWE-321
pywry/tests/test_toolbar.py:3976
"private_key": "-----BEGIN RSA PRIVATE KEY-----\\nMIIE...base64...\\n-----END RSA PRIVATE KEY-----\\n",
MEDIUMHard-coded secrets · secret.private_key · CWE-798, CWE-321
pywry/tests/test_toolbar.py:4257
ssh_key = """-----BEGIN OPENSSH PRIVATE KEY-----
MEDIUMHard-coded secrets · secret.private_key · CWE-798, CWE-321
pywry/tests/test_toolbar.py:4284
assert "-----BEGIN OPENSSH PRIVATE KEY-----" in retrieved
MEDIUMHard-coded secrets · secret.private_key · CWE-798, CWE-321
pywry/tests/test_toolbar.py:4294
"private_key": "-----BEGIN RSA PRIVATE KEY-----\\nMIIE...\\n-----END RSA PRIVATE KEY-----\\n",
MEDIUMPrompt injection · prompt.hidden_style · CWE-94, CWE-1427
pywry/docs/docs/components/htmlcontent/content-assembly.md:220
<div class="pywry-modal-overlay" id="modal-settings" style="display:none">
MEDIUMPrompt injection · prompt.hidden_style · CWE-94, CWE-1427
pywry/docs/docs/components/toolbar/radiogroup.md:74
<div class="view-list" style="display:none">List View Content</div>
MEDIUMPrompt injection · prompt.hidden_style · CWE-94, CWE-1427
pywry/docs/docs/components/toolbar/radiogroup.md:75
<div class="view-chart" style="display:none">Chart View Content</div>
MEDIUMPrompt injection · prompt.hidden_style · CWE-94, CWE-1427
pywry/docs/docs/components/toolbar/tabgroup.md:51
<div id="section-data" style="display:none">Data content...</div>

Gates applied: no_behavioural_pass.

Audited 2026-10-07 · audit v0.4.1 · source sha d870349f2c13full audit observations/trust-audit/mcp-server/deeleeramone__pywry.json · Report an issue / request a re-scan
05

Audit history

Every audit this server has had. A grade with a past is a grade somebody is still checking.

DateSourceVerdictGradeScoreChange
2026-10-07d870349f2c13BLOCKF42first audit
06

Questions

What is the PyWry MCP server?

PyWry is a cross-platform app factory, rendering engine and UI toolkit for Python that produces native desktop, web, and notebook experiences from a single API.

What tools does PyWry expose?

67 in total: 39 read-only, 24 that write, and 4 that can delete or overwrite (destroy_widget, remove_css, tvchart_remove_indicator, tvchart_remove_series). Every one is listed on this page with its risk.

Is PyWry safe to connect to an agent?

No — not without reading the findings first. The audit graded it F (42/100) and found 5 critical or high issues in the source. Each one is listed on this page with the file and line it is on. Separately from the audit: 4 of its tools can destroy data, so scope the token you give it to what you actually need.

What credentials does PyWry need?

It reads GEMINI_API_KEY, NVIDIA_API_KEY, PYWRY_DEPLOY__AUTH_ENABLED, PYWRY_OAUTH2__CLIENT_ID, PYWRY_OAUTH2__CLIENT_SECRET, PYWRY_SERVER__INTERNAL_API_TOKEN, PYWRY_SERVER__SSL_KEYFILE, PYWRY_SERVER__SSL_KEYFILE_PASSWORD, PYWRY_SERVER__STRICT_WIDGET_AUTH, PYWRY_SERVER__WEBSOCKET_REQUIRE_TOKEN and PYWRY_SQLITE_KEY from the environment. Give it a token scoped to the least it needs — an agent that can be talked into calling a tool can be talked into calling it with your credentials.

How does PyWry run?

It speaks sse, stdio and streamable-http, so it runs as a local process your client starts. It is published on npm as pywry-frontend-assets.

How current is this page?

The grade is for one exact copy of the source (d870349f2c13), read on 2026-10-07. The repository is watched and re-audited when it changes.

Advertisement