Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/dispatch_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ on:
description: "reflex-release"
type: boolean
default: false
reflex_webmcp:
description: "reflex-webmcp"
type: boolean
default: false

permissions:
contents: read
Expand Down Expand Up @@ -191,6 +195,7 @@ jobs:
${{ inputs.reflex_docgen && 'reflex-docgen' || '' }}
${{ inputs.reflex_hosting_cli && 'reflex-hosting-cli' || '' }}
${{ inputs.reflex_release && 'reflex-release' || '' }}
${{ inputs.reflex_webmcp && 'reflex-webmcp' || '' }}
shell: bash
run: uv run --frozen --package reflex-release reflex-release plan
- name: Materialize changelogs
Expand Down
56 changes: 56 additions & 0 deletions docs/api-reference/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,62 @@ The sitemap configuration supports the following options:
- `changefreq`: How frequently the page changes (`"always"`, `"hourly"`, `"daily"`, `"weekly"`, `"monthly"`, `"yearly"`, `"never"`)
- `priority`: Priority of this URL relative to other URLs (0.0 to 1.0)

### WebMCPPlugin

The `WebMCPPlugin`, shipped in the separate `reflex-webmcp` package
(`pip install reflex-webmcp`), automatically exposes backend Reflex events already bound to
components through the browser's imperative WebMCP API. Compatible agents can
discover the tools while the page is open and invoke the same event pipeline as
the normal interface, using the same signed-in browser session.

No separate tool definitions or JavaScript handlers are required:

```python
import reflex as rx
from reflex_webmcp import WebMCPPlugin


class CatalogState(rx.State):
results: list[str] = []

def search_catalog(self, query: str, limit: int = 10):
"""Search the product catalog visible on this page."""
self.results = search_products(query, limit=limit)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The example code block calls search_products(query, limit=limit) but never defines or imports it, so a user who copies this snippet (and others in the PR) gets a NameError on first run. Every other plugin example in this file is self-contained. Either define a small search_products helper in the block or note that it is a stand-in for the app's own lookup, so the "no separate tool definitions" example is actually runnable.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/api-reference/plugins.md, line 88:

<comment>The example code block calls `search_products(query, limit=limit)` but never defines or imports it, so a user who copies this snippet (and others in the PR) gets a `NameError` on first run. Every other plugin example in this file is self-contained. Either define a small `search_products` helper in the block or note that it is a stand-in for the app's own lookup, so the "no separate tool definitions" example is actually runnable.</comment>

<file context>
@@ -65,6 +65,62 @@ The sitemap configuration supports the following options:
+
+    def search_catalog(self, query: str, limit: int = 10):
+        """Search the product catalog visible on this page."""
+        self.results = search_products(query, limit=limit)
+
+
</file context>



def catalog():
return rx.input(on_change=CatalogState.search_catalog)


config = rx.Config(
app_name="my_app",
plugins=[WebMCPPlugin()],
)
```

During compilation, the plugin finds backend `EventSpec` objects in component
event chains, including rows rendered by `rx.foreach`. Each unique state
handler becomes one tool named `reflex_<StateClass>_<handler>`, the handler's
docstring supplies its description, and its Python parameter annotations and
defaults become the JSON input schema. Annotate form handlers with a `TypedDict`
rather than `dict` to give agents a field-level schema. Tool execution queues the same `ReflexEvent` through `addEvents`,
so existing state management, authentication, event processing, and input
conversion remain in effect.

Arguments already fixed by the component, such as
`on_click=CatalogState.select_item("sku-42")`, remain fixed in the generated
tool and are removed from its input schema. Distinct fixed event instances get
distinct tool names. Arguments that are runtime `Var`s, such as `task.id` inside
an `rx.foreach` row, stay in the schema for the agent to supply. Existing event actions such as debounce, throttle, and
temporal behavior are preserved.

Frontend-only events, lifecycle triggers, upload handlers, dynamic event vars,
and variadic handlers are not exposed because they cannot be represented as a
narrow backend object payload. The plugin prevents duplicate registration and
checks for browser support. Browsers without WebMCP continue to use the normal
interface. See the [OpenAI Site Tools documentation](https://learn.chatgpt.com/docs/webmcp)
for availability, security guidance, and current browser limitations.

### TailwindV4Plugin

The `TailwindV4Plugin` provides support for Tailwind CSS v4, which is the recommended version for new projects and includes performance improvements and new features.
Expand Down
1 change: 1 addition & 0 deletions packages/reflex-base/news/7007.misc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Route `reflex_webmcp` log records into the reflex logger hierarchy.
1 change: 1 addition & 0 deletions packages/reflex-base/src/reflex_base/utils/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"reflex_components_lucide",
"reflex_components_plotly",
"reflex_components_react_player",
"reflex_webmcp",
)

# The single logger the reflex sinks attach to; parent of every package logger.
Expand Down
15 changes: 15 additions & 0 deletions packages/reflex-webmcp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# reflex-webmcp

Reflex plugin that automatically exposes backend events already bound to
components as [WebMCP](https://learn.chatgpt.com/docs/webmcp) site tools.

```python
import reflex as rx
from reflex_webmcp import WebMCPPlugin

config = rx.Config(app_name="my_app", plugins=[WebMCPPlugin()])
```

Each unique state handler bound to a component becomes one tool named
`reflex_<StateClass>_<handler>`; the docstring supplies its description and
the parameter annotations become the JSON input schema.
1 change: 1 addition & 0 deletions packages/reflex-webmcp/news/7007.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add a `WebMCPPlugin` that automatically turns backend Reflex events already bound to components into guarded site tools with schemas derived from handler annotations.
19 changes: 19 additions & 0 deletions packages/reflex-webmcp/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[project]
name = "reflex-webmcp"
dynamic = ["version"]
description = "Reflex plugin that exposes bound backend events as WebMCP site tools."
license.text = "Apache-2.0"
readme = "README.md"
requires-python = ">=3.10"
dependencies = ["reflex-base >= 0.9.9", "typing_extensions >= 4.13.0"]

[tool.hatch.version]
source = "uv-dynamic-versioning"

[tool.uv-dynamic-versioning]
pattern-prefix = "reflex-webmcp-"
fallback-version = "0.0.0dev0"

[build-system]
requires = ["hatchling", "uv-dynamic-versioning"]
build-backend = "hatchling.build"
Loading
Loading