flet-mcp 0.1.0__tar.gz

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.
@@ -0,0 +1,114 @@
1
+ Metadata-Version: 2.4
2
+ Name: flet-mcp
3
+ Version: 0.1.0
4
+ Summary: Flet MCP server for LLM agents
5
+ Author-email: "Appveyor Systems Inc." <hello@flet.dev>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://flet.dev
8
+ Project-URL: Repository, https://github.com/flet-dev/flet
9
+ Project-URL: Documentation, https://flet.dev/docs
10
+ Requires-Python: >=3.10
11
+ Description-Content-Type: text/markdown
12
+ Requires-Dist: fastmcp>=2.0.0
13
+ Requires-Dist: pyyaml>=6.0
14
+ Provides-Extra: build
15
+ Requires-Dist: markdownify>=0.14.1; extra == "build"
16
+ Requires-Dist: griffe>=1.6.2; extra == "build"
17
+ Requires-Dist: flet; extra == "build"
18
+ Requires-Dist: flet-cli; extra == "build"
19
+
20
+ # flet-mcp
21
+
22
+ MCP (Model Context Protocol) server that gives LLM agents access to Flet examples, documentation, and API reference.
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ pip install flet-mcp
28
+ ```
29
+
30
+ ## Usage
31
+
32
+ ### Start the MCP server
33
+
34
+ ```bash
35
+ # stdio transport (default, for use with Claude Desktop, Cursor, etc.)
36
+ flet mcp
37
+
38
+ # HTTP transport
39
+ flet mcp --transport streamable-http --port 8000
40
+ ```
41
+
42
+ ### Configure in Claude Desktop
43
+
44
+ Add to your `claude_desktop_config.json`:
45
+
46
+ ```json
47
+ {
48
+ "mcpServers": {
49
+ "flet": {
50
+ "command": "flet",
51
+ "args": ["mcp"]
52
+ }
53
+ }
54
+ }
55
+ ```
56
+
57
+ ### List available tools
58
+
59
+ ```bash
60
+ fastmcp list packages/flet-mcp/src/flet_mcp/server.py
61
+ ```
62
+
63
+ ### Call tools from the command line
64
+
65
+ ```bash
66
+ # Search examples
67
+ fastmcp call packages/flet-mcp/src/flet_mcp/server.py search_examples '{"query": "dropdown"}'
68
+
69
+ # Get full example code
70
+ fastmcp call packages/flet-mcp/src/flet_mcp/server.py get_example '{"example_id": "controls_dropdown_styled"}'
71
+
72
+ # Search documentation
73
+ fastmcp call packages/flet-mcp/src/flet_mcp/server.py search_docs '{"query": "TextField validation"}'
74
+
75
+ # Get control API reference
76
+ fastmcp call packages/flet-mcp/src/flet_mcp/server.py get_control_api '{"name": "TextField"}'
77
+
78
+ # Find an icon
79
+ fastmcp call packages/flet-mcp/src/flet_mcp/server.py find_icon '{"query": "settings"}'
80
+
81
+ # Search large enums
82
+ fastmcp call packages/flet-mcp/src/flet_mcp/server.py search_enum_members '{"name": "Icons", "query": "arrow"}'
83
+
84
+ # Get CLI help
85
+ fastmcp call packages/flet-mcp/src/flet_mcp/server.py get_cli_help '{"command": "run"}'
86
+ ```
87
+
88
+ ### Use with Pydantic AI
89
+
90
+ ```python
91
+ from pydantic_ai import Agent
92
+ from pydantic_ai.toolsets import MCPToolset
93
+ from flet_mcp import mcp
94
+
95
+ agent = Agent("claude-sonnet-4-20250514", toolsets=[MCPToolset(mcp)])
96
+ result = agent.run_sync("Create a Flet app with a login form")
97
+ ```
98
+
99
+ ## Tools
100
+
101
+ | Tool | Description |
102
+ |------|-------------|
103
+ | `search_examples` | Search example projects by keyword |
104
+ | `get_example` | Get full source code for an example |
105
+ | `search_docs` | Search documentation by keyword |
106
+ | `get_doc` | Get full content of a doc section |
107
+ | `list_controls` | List controls and services, with optional filtering |
108
+ | `get_control_api` | Get properties, events, and methods for a control |
109
+ | `get_type_api` | Get fields and methods for a type (ButtonStyle, Padding, etc.) |
110
+ | `get_enum` | Get enum members |
111
+ | `search_enum_members` | Search large enums (Icons, CupertinoIcons) |
112
+ | `enum_has_member` | Check if an enum value exists |
113
+ | `find_icon` | Search Material and Cupertino icons by keyword |
114
+ | `get_cli_help` | Get structured CLI command options |
@@ -0,0 +1,95 @@
1
+ # flet-mcp
2
+
3
+ MCP (Model Context Protocol) server that gives LLM agents access to Flet examples, documentation, and API reference.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install flet-mcp
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Start the MCP server
14
+
15
+ ```bash
16
+ # stdio transport (default, for use with Claude Desktop, Cursor, etc.)
17
+ flet mcp
18
+
19
+ # HTTP transport
20
+ flet mcp --transport streamable-http --port 8000
21
+ ```
22
+
23
+ ### Configure in Claude Desktop
24
+
25
+ Add to your `claude_desktop_config.json`:
26
+
27
+ ```json
28
+ {
29
+ "mcpServers": {
30
+ "flet": {
31
+ "command": "flet",
32
+ "args": ["mcp"]
33
+ }
34
+ }
35
+ }
36
+ ```
37
+
38
+ ### List available tools
39
+
40
+ ```bash
41
+ fastmcp list packages/flet-mcp/src/flet_mcp/server.py
42
+ ```
43
+
44
+ ### Call tools from the command line
45
+
46
+ ```bash
47
+ # Search examples
48
+ fastmcp call packages/flet-mcp/src/flet_mcp/server.py search_examples '{"query": "dropdown"}'
49
+
50
+ # Get full example code
51
+ fastmcp call packages/flet-mcp/src/flet_mcp/server.py get_example '{"example_id": "controls_dropdown_styled"}'
52
+
53
+ # Search documentation
54
+ fastmcp call packages/flet-mcp/src/flet_mcp/server.py search_docs '{"query": "TextField validation"}'
55
+
56
+ # Get control API reference
57
+ fastmcp call packages/flet-mcp/src/flet_mcp/server.py get_control_api '{"name": "TextField"}'
58
+
59
+ # Find an icon
60
+ fastmcp call packages/flet-mcp/src/flet_mcp/server.py find_icon '{"query": "settings"}'
61
+
62
+ # Search large enums
63
+ fastmcp call packages/flet-mcp/src/flet_mcp/server.py search_enum_members '{"name": "Icons", "query": "arrow"}'
64
+
65
+ # Get CLI help
66
+ fastmcp call packages/flet-mcp/src/flet_mcp/server.py get_cli_help '{"command": "run"}'
67
+ ```
68
+
69
+ ### Use with Pydantic AI
70
+
71
+ ```python
72
+ from pydantic_ai import Agent
73
+ from pydantic_ai.toolsets import MCPToolset
74
+ from flet_mcp import mcp
75
+
76
+ agent = Agent("claude-sonnet-4-20250514", toolsets=[MCPToolset(mcp)])
77
+ result = agent.run_sync("Create a Flet app with a login form")
78
+ ```
79
+
80
+ ## Tools
81
+
82
+ | Tool | Description |
83
+ |------|-------------|
84
+ | `search_examples` | Search example projects by keyword |
85
+ | `get_example` | Get full source code for an example |
86
+ | `search_docs` | Search documentation by keyword |
87
+ | `get_doc` | Get full content of a doc section |
88
+ | `list_controls` | List controls and services, with optional filtering |
89
+ | `get_control_api` | Get properties, events, and methods for a control |
90
+ | `get_type_api` | Get fields and methods for a type (ButtonStyle, Padding, etc.) |
91
+ | `get_enum` | Get enum members |
92
+ | `search_enum_members` | Search large enums (Icons, CupertinoIcons) |
93
+ | `enum_has_member` | Check if an enum value exists |
94
+ | `find_icon` | Search Material and Cupertino icons by keyword |
95
+ | `get_cli_help` | Get structured CLI command options |
@@ -0,0 +1,32 @@
1
+ [project]
2
+ name = "flet-mcp"
3
+ version = "0.1.0"
4
+ description = "Flet MCP server for LLM agents"
5
+ authors = [{ name = "Appveyor Systems Inc.", email = "hello@flet.dev" }]
6
+ license = "Apache-2.0"
7
+ readme = "README.md"
8
+ requires-python = ">=3.10"
9
+ dependencies = [
10
+ "fastmcp >=2.0.0",
11
+ "pyyaml >=6.0",
12
+ ]
13
+
14
+ [project.optional-dependencies]
15
+ build = [
16
+ "markdownify >=0.14.1",
17
+ "griffe >=1.6.2",
18
+ "flet",
19
+ "flet-cli",
20
+ ]
21
+
22
+ [project.urls]
23
+ Homepage = "https://flet.dev"
24
+ Repository = "https://github.com/flet-dev/flet"
25
+ Documentation = "https://flet.dev/docs"
26
+
27
+ [build-system]
28
+ requires = ["setuptools"]
29
+ build-backend = "setuptools.build_meta"
30
+
31
+ [tool.setuptools.package-data]
32
+ flet_mcp = ["data/*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ from flet_mcp.server import mcp
2
+
3
+ __all__ = ["mcp"]
@@ -0,0 +1,147 @@
1
+ """Load and query the Griffe-generated API reference (api.json)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import importlib.resources
6
+ import json
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+
11
+ class ApiStore:
12
+ """Lazy-loading store for the bundled api.json data.
13
+
14
+ The api.json file uses lists for controls/events/types/enums,
15
+ each entry having a "name" key. This store builds name-keyed dicts
16
+ for fast lookup.
17
+ """
18
+
19
+ def __init__(self) -> None:
20
+ self._raw: dict[str, Any] | None = None
21
+ self._controls: dict[str, dict] | None = None
22
+ self._events: dict[str, dict] | None = None
23
+ self._types: dict[str, dict] | None = None
24
+ self._enums: dict[str, dict] | None = None
25
+
26
+ def _load(self) -> dict[str, Any]:
27
+ if self._raw is None:
28
+ ref = importlib.resources.files("flet_mcp").joinpath("data/api.json")
29
+ self._raw = json.loads(Path(str(ref)).read_text(encoding="utf-8"))
30
+ # Build name-keyed dicts from lists
31
+ self._controls = {c["name"]: c for c in self._raw.get("controls", [])}
32
+ self._events = {e["name"]: e for e in self._raw.get("events", [])}
33
+ self._types = {t["name"]: t for t in self._raw.get("types", [])}
34
+ self._enums = {e["name"]: e for e in self._raw.get("enums", [])}
35
+ return self._raw
36
+
37
+ # ------------------------------------------------------------------
38
+ # Controls
39
+ # ------------------------------------------------------------------
40
+
41
+ def list_controls(
42
+ self,
43
+ category: str | None = None,
44
+ kind: str | None = None,
45
+ limit: int = 50,
46
+ ) -> list[dict[str, Any]]:
47
+ """List controls, optionally filtered by category or kind."""
48
+ self._load()
49
+ results: list[dict[str, Any]] = []
50
+ for name, ctrl in self._controls.items():
51
+ if kind and ctrl.get("kind") != kind:
52
+ continue
53
+ if category and category not in ctrl.get("categories", []):
54
+ continue
55
+ results.append(
56
+ {
57
+ "name": name,
58
+ "kind": ctrl.get("kind"),
59
+ "summary": ctrl.get("summary", ""),
60
+ "categories": ctrl.get("categories", []),
61
+ }
62
+ )
63
+ if len(results) >= limit:
64
+ break
65
+ return results
66
+
67
+ def get_control(self, name: str) -> dict[str, Any] | None:
68
+ """Return full control dict by name, or None."""
69
+ self._load()
70
+ return self._controls.get(name)
71
+
72
+ # ------------------------------------------------------------------
73
+ # Types & Events
74
+ # ------------------------------------------------------------------
75
+
76
+ def get_type(self, name: str) -> dict[str, Any] | None:
77
+ """Return full type dict by name, or None."""
78
+ self._load()
79
+ return self._types.get(name) or self._events.get(name)
80
+
81
+ # ------------------------------------------------------------------
82
+ # Enums
83
+ # ------------------------------------------------------------------
84
+
85
+ def get_enum(self, name: str) -> dict[str, Any] | None:
86
+ """Return enum data. Large enums are truncated with a hint to search."""
87
+ self._load()
88
+ enum = self._enums.get(name)
89
+ if enum is None:
90
+ return None
91
+
92
+ if enum.get("kind") == "large_enum":
93
+ members = enum.get("members", [])
94
+ return {
95
+ "name": name,
96
+ "kind": "large_enum",
97
+ "total_members": len(members),
98
+ "sample_members": members[:10],
99
+ "note": (
100
+ f"This enum has {len(members)} members. "
101
+ "Use search_enum_members() to find specific values."
102
+ ),
103
+ }
104
+
105
+ return enum
106
+
107
+ def search_enum_members(self, name: str, query: str, limit: int = 10) -> list[str]:
108
+ """Search enum members by case-insensitive substring match."""
109
+ self._load()
110
+ enum = self._enums.get(name)
111
+ if enum is None:
112
+ return []
113
+
114
+ query_lower = query.lower()
115
+ results: list[str] = []
116
+ for member in enum.get("members", []):
117
+ member_name = member["name"] if isinstance(member, dict) else str(member)
118
+ if query_lower in member_name.lower():
119
+ results.append(member_name)
120
+ if len(results) >= limit:
121
+ break
122
+ return results
123
+
124
+ def enum_has_member(self, name: str, member: str) -> bool:
125
+ """Check whether an enum contains a specific member."""
126
+ self._load()
127
+ enum = self._enums.get(name)
128
+ if enum is None:
129
+ return False
130
+ members = enum.get("members", [])
131
+ member_lower = member.lower()
132
+ return any(
133
+ (m["name"] if isinstance(m, dict) else str(m)).lower() == member_lower
134
+ for m in members
135
+ )
136
+
137
+ # ------------------------------------------------------------------
138
+ # CLI
139
+ # ------------------------------------------------------------------
140
+
141
+ def get_cli_help(self, command: str | None = None) -> Any:
142
+ """Return CLI help. None -> list commands; otherwise command help text."""
143
+ self._load()
144
+ cli = self._raw.get("cli", {})
145
+ if command is None:
146
+ return list(cli.keys())
147
+ return cli.get(command)
File without changes