holoviz-mcp 0.4.0__py3-none-any.whl
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.
- holoviz_mcp/__init__.py +18 -0
- holoviz_mcp/apps/__init__.py +1 -0
- holoviz_mcp/apps/configuration_viewer.py +116 -0
- holoviz_mcp/apps/holoviz_get_best_practices.py +173 -0
- holoviz_mcp/apps/holoviz_search.py +319 -0
- holoviz_mcp/apps/hvplot_get_docstring.py +255 -0
- holoviz_mcp/apps/hvplot_get_signature.py +252 -0
- holoviz_mcp/apps/hvplot_list_plot_types.py +83 -0
- holoviz_mcp/apps/panel_get_component.py +496 -0
- holoviz_mcp/apps/panel_get_component_parameters.py +467 -0
- holoviz_mcp/apps/panel_list_components.py +311 -0
- holoviz_mcp/apps/panel_list_packages.py +71 -0
- holoviz_mcp/apps/panel_search_components.py +312 -0
- holoviz_mcp/cli.py +75 -0
- holoviz_mcp/client.py +94 -0
- holoviz_mcp/config/__init__.py +29 -0
- holoviz_mcp/config/config.yaml +178 -0
- holoviz_mcp/config/loader.py +316 -0
- holoviz_mcp/config/models.py +208 -0
- holoviz_mcp/config/resources/best-practices/holoviews.md +423 -0
- holoviz_mcp/config/resources/best-practices/hvplot.md +465 -0
- holoviz_mcp/config/resources/best-practices/panel-material-ui.md +318 -0
- holoviz_mcp/config/resources/best-practices/panel.md +562 -0
- holoviz_mcp/config/schema.json +228 -0
- holoviz_mcp/holoviz_mcp/__init__.py +1 -0
- holoviz_mcp/holoviz_mcp/data.py +970 -0
- holoviz_mcp/holoviz_mcp/models.py +21 -0
- holoviz_mcp/holoviz_mcp/pages_design.md +407 -0
- holoviz_mcp/holoviz_mcp/server.py +220 -0
- holoviz_mcp/hvplot_mcp/__init__.py +1 -0
- holoviz_mcp/hvplot_mcp/server.py +146 -0
- holoviz_mcp/panel_mcp/__init__.py +17 -0
- holoviz_mcp/panel_mcp/data.py +319 -0
- holoviz_mcp/panel_mcp/models.py +124 -0
- holoviz_mcp/panel_mcp/server.py +443 -0
- holoviz_mcp/py.typed +0 -0
- holoviz_mcp/serve.py +36 -0
- holoviz_mcp/server.py +86 -0
- holoviz_mcp/shared/__init__.py +1 -0
- holoviz_mcp/shared/extract_tools.py +74 -0
- holoviz_mcp/thumbnails/configuration_viewer.png +0 -0
- holoviz_mcp/thumbnails/holoviz_get_best_practices.png +0 -0
- holoviz_mcp/thumbnails/holoviz_search.png +0 -0
- holoviz_mcp/thumbnails/hvplot_get_docstring.png +0 -0
- holoviz_mcp/thumbnails/hvplot_get_signature.png +0 -0
- holoviz_mcp/thumbnails/hvplot_list_plot_types.png +0 -0
- holoviz_mcp/thumbnails/panel_get_component.png +0 -0
- holoviz_mcp/thumbnails/panel_get_component_parameters.png +0 -0
- holoviz_mcp/thumbnails/panel_list_components.png +0 -0
- holoviz_mcp/thumbnails/panel_list_packages.png +0 -0
- holoviz_mcp/thumbnails/panel_search_components.png +0 -0
- holoviz_mcp-0.4.0.dist-info/METADATA +216 -0
- holoviz_mcp-0.4.0.dist-info/RECORD +56 -0
- holoviz_mcp-0.4.0.dist-info/WHEEL +4 -0
- holoviz_mcp-0.4.0.dist-info/entry_points.txt +2 -0
- holoviz_mcp-0.4.0.dist-info/licenses/LICENSE.txt +30 -0
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
"""[Panel](https://panel.holoviz.org/) MCP Server.
|
|
2
|
+
|
|
3
|
+
This MCP server provides tools, resources and prompts for using Panel to develop quick, interactive
|
|
4
|
+
applications, tools and dashboards in Python using best practices.
|
|
5
|
+
|
|
6
|
+
Use this server to access:
|
|
7
|
+
|
|
8
|
+
- Panel Best Practices: Learn how to use Panel effectively.
|
|
9
|
+
- Panel Components: Get information about specific Panel components like widgets (input), panes (output) and layouts.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from importlib.metadata import distributions
|
|
13
|
+
|
|
14
|
+
from fastmcp import Context
|
|
15
|
+
from fastmcp import FastMCP
|
|
16
|
+
|
|
17
|
+
from holoviz_mcp.config.loader import get_config
|
|
18
|
+
from holoviz_mcp.panel_mcp.data import get_components as _get_components_org
|
|
19
|
+
from holoviz_mcp.panel_mcp.models import ComponentDetails
|
|
20
|
+
from holoviz_mcp.panel_mcp.models import ComponentSummary
|
|
21
|
+
from holoviz_mcp.panel_mcp.models import ComponentSummarySearchResult
|
|
22
|
+
from holoviz_mcp.panel_mcp.models import ParameterInfo
|
|
23
|
+
|
|
24
|
+
# Create the FastMCP server
|
|
25
|
+
mcp = FastMCP(
|
|
26
|
+
name="panel",
|
|
27
|
+
instructions="""
|
|
28
|
+
[Panel](https://panel.holoviz.org/) MCP Server.
|
|
29
|
+
|
|
30
|
+
This MCP server provides tools, resources and prompts for using Panel to develop quick, interactive
|
|
31
|
+
applications, tools and dashboards in Python using best practices.
|
|
32
|
+
|
|
33
|
+
DO use this server to search for specific Panel components and access detailed information including docstrings and parameter information.
|
|
34
|
+
""",
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
_config = get_config()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
async def _list_packages_depending_on(target_package: str, ctx: Context) -> list[str]:
|
|
41
|
+
"""
|
|
42
|
+
Find all installed packages that depend on a given package.
|
|
43
|
+
|
|
44
|
+
This is a helper function that searches through installed packages to find
|
|
45
|
+
those that have the target package as a dependency. Used to discover
|
|
46
|
+
Panel-related packages in the environment.
|
|
47
|
+
|
|
48
|
+
Parameters
|
|
49
|
+
----------
|
|
50
|
+
target_package : str
|
|
51
|
+
The name of the package to search for dependencies on (e.g., 'panel').
|
|
52
|
+
ctx : Context
|
|
53
|
+
FastMCP context for logging and debugging.
|
|
54
|
+
|
|
55
|
+
Returns
|
|
56
|
+
-------
|
|
57
|
+
list[str]
|
|
58
|
+
Sorted list of package names that depend on the target package.
|
|
59
|
+
"""
|
|
60
|
+
dependent_packages = []
|
|
61
|
+
|
|
62
|
+
for dist in distributions():
|
|
63
|
+
if dist.requires:
|
|
64
|
+
dist_name = dist.metadata["Name"]
|
|
65
|
+
await ctx.debug(f"Checking package: {dist_name} for dependencies on {target_package}")
|
|
66
|
+
for requirement_str in dist.requires:
|
|
67
|
+
if "extra ==" in requirement_str:
|
|
68
|
+
continue
|
|
69
|
+
package_name = requirement_str.split()[0].split(";")[0].split(">=")[0].split("==")[0].split("!=")[0].split("<")[0].split(">")[0].split("~")[0]
|
|
70
|
+
if package_name.lower() == target_package.lower():
|
|
71
|
+
dependent_packages.append(dist_name.replace("-", "_"))
|
|
72
|
+
break
|
|
73
|
+
|
|
74
|
+
return sorted(set(dependent_packages))
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
COMPONENTS: list[ComponentDetails] = []
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
async def _get_all_components(ctx: Context) -> list[ComponentDetails]:
|
|
81
|
+
"""
|
|
82
|
+
Get all available Panel components from discovered packages.
|
|
83
|
+
|
|
84
|
+
This function initializes and caches the global COMPONENTS list by:
|
|
85
|
+
1. Discovering all packages that depend on Panel
|
|
86
|
+
2. Importing those packages to register their components
|
|
87
|
+
3. Collecting detailed information about all Panel components
|
|
88
|
+
|
|
89
|
+
This is called lazily to populate the component cache when needed.
|
|
90
|
+
|
|
91
|
+
Parameters
|
|
92
|
+
----------
|
|
93
|
+
ctx : Context
|
|
94
|
+
FastMCP context for logging and debugging.
|
|
95
|
+
|
|
96
|
+
Returns
|
|
97
|
+
-------
|
|
98
|
+
list[ComponentDetails]
|
|
99
|
+
Complete list of all discovered Panel components with detailed metadata.
|
|
100
|
+
"""
|
|
101
|
+
global COMPONENTS
|
|
102
|
+
if not COMPONENTS:
|
|
103
|
+
packages_depending_on_panel = await _list_packages_depending_on("panel", ctx=ctx)
|
|
104
|
+
|
|
105
|
+
await ctx.info(f"Discovered {len(packages_depending_on_panel)} packages depending on Panel: {packages_depending_on_panel}")
|
|
106
|
+
|
|
107
|
+
for package in packages_depending_on_panel:
|
|
108
|
+
try:
|
|
109
|
+
__import__(package)
|
|
110
|
+
except ImportError as e:
|
|
111
|
+
await ctx.warning(f"Discovered but failed to import {package}: {e}")
|
|
112
|
+
|
|
113
|
+
COMPONENTS = _get_components_org()
|
|
114
|
+
|
|
115
|
+
return COMPONENTS
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@mcp.tool
|
|
119
|
+
async def list_packages(ctx: Context) -> list[str]:
|
|
120
|
+
"""
|
|
121
|
+
List all installed packages that provide Panel UI components.
|
|
122
|
+
|
|
123
|
+
Use this tool to discover what Panel-related packages are available in your environment.
|
|
124
|
+
This helps you understand which packages you can use in the 'package' parameter of other tools.
|
|
125
|
+
|
|
126
|
+
Parameters
|
|
127
|
+
----------
|
|
128
|
+
ctx : Context
|
|
129
|
+
FastMCP context (automatically provided by the MCP framework).
|
|
130
|
+
|
|
131
|
+
Returns
|
|
132
|
+
-------
|
|
133
|
+
list[str]
|
|
134
|
+
List of package names that provide Panel components, sorted alphabetically.
|
|
135
|
+
Examples: ["panel"] or ["panel", "panel_material_ui"]
|
|
136
|
+
|
|
137
|
+
Examples
|
|
138
|
+
--------
|
|
139
|
+
Use this tool to see available packages:
|
|
140
|
+
>>> list_packages()
|
|
141
|
+
["panel", "panel_material_ui"]
|
|
142
|
+
|
|
143
|
+
Then use those package names in other tools:
|
|
144
|
+
>>> list_components(package="panel_material_ui")
|
|
145
|
+
>>> search("button", package="panel")
|
|
146
|
+
"""
|
|
147
|
+
return sorted(set(component.package for component in await _get_all_components(ctx)))
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@mcp.tool
|
|
151
|
+
async def search_components(ctx: Context, query: str, package: str | None = None, limit: int = 10) -> list[ComponentSummarySearchResult]:
|
|
152
|
+
"""
|
|
153
|
+
Search for Panel components by search query and optional package filter.
|
|
154
|
+
|
|
155
|
+
Use this tool to find components when you don't know the exact name but have keywords.
|
|
156
|
+
The search looks through component names, module paths, and documentation to find matches.
|
|
157
|
+
|
|
158
|
+
Parameters
|
|
159
|
+
----------
|
|
160
|
+
ctx : Context
|
|
161
|
+
FastMCP context (automatically provided by the MCP framework).
|
|
162
|
+
query : str
|
|
163
|
+
Search term to look for. Can be component names, functionality keywords, or descriptions.
|
|
164
|
+
Examples: "button", "input", "text", "chart", "plot", "slider", "select"
|
|
165
|
+
package : str, optional
|
|
166
|
+
Package name to filter results. If None, searches all packages.
|
|
167
|
+
Examples: "hvplot", "panel", or "panel_material_ui"
|
|
168
|
+
limit : int, optional
|
|
169
|
+
Maximum number of results to return. Default is 10.
|
|
170
|
+
|
|
171
|
+
Returns
|
|
172
|
+
-------
|
|
173
|
+
list[ComponentSummarySearchResult]
|
|
174
|
+
List of matching components with relevance scores (0-100, where 100 is exact match).
|
|
175
|
+
Results are sorted by relevance score in descending order.
|
|
176
|
+
|
|
177
|
+
Examples
|
|
178
|
+
--------
|
|
179
|
+
Search for button components:
|
|
180
|
+
>>> search_components("button")
|
|
181
|
+
[ComponentSummarySearchResult(name="Button", package="panel", relevance_score=80, ...)]
|
|
182
|
+
|
|
183
|
+
Search within a specific package:
|
|
184
|
+
>>> search_components("input", package="panel_material_ui")
|
|
185
|
+
[ComponentSummarySearchResult(name="TextInput", package="panel_material_ui", ...)]
|
|
186
|
+
|
|
187
|
+
Find chart components with limited results:
|
|
188
|
+
>>> search_components("chart", limit=5)
|
|
189
|
+
[ComponentSummarySearchResult(name="Bokeh", package="panel", ...)]
|
|
190
|
+
"""
|
|
191
|
+
query_lower = query.lower()
|
|
192
|
+
|
|
193
|
+
matches = []
|
|
194
|
+
for component in await _get_all_components(ctx=ctx):
|
|
195
|
+
score = 0
|
|
196
|
+
if package and component.package.lower() != package.lower():
|
|
197
|
+
continue
|
|
198
|
+
|
|
199
|
+
if component.name.lower() == query_lower or component.module_path.lower() == query_lower:
|
|
200
|
+
score = 100
|
|
201
|
+
elif query_lower in component.name.lower():
|
|
202
|
+
score = 80
|
|
203
|
+
elif query_lower in component.module_path.lower():
|
|
204
|
+
score = 60
|
|
205
|
+
elif query_lower in component.docstring.lower():
|
|
206
|
+
score = 40
|
|
207
|
+
elif any(word in component.docstring.lower() for word in query_lower.split()):
|
|
208
|
+
score = 20
|
|
209
|
+
|
|
210
|
+
if score > 0:
|
|
211
|
+
matches.append(ComponentSummarySearchResult.from_component(component=component, relevance_score=score))
|
|
212
|
+
|
|
213
|
+
matches.sort(key=lambda x: x.relevance_score, reverse=True)
|
|
214
|
+
if len(matches) > limit:
|
|
215
|
+
matches = matches[:limit]
|
|
216
|
+
|
|
217
|
+
return matches
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
async def _get_component(ctx: Context, name: str | None = None, module_path: str | None = None, package: str | None = None) -> list[ComponentDetails]:
|
|
221
|
+
"""
|
|
222
|
+
Get component details based on filtering criteria.
|
|
223
|
+
|
|
224
|
+
This is an internal function used by the public component tools to filter
|
|
225
|
+
and retrieve components based on name, module path, and package criteria.
|
|
226
|
+
|
|
227
|
+
Parameters
|
|
228
|
+
----------
|
|
229
|
+
ctx : Context
|
|
230
|
+
FastMCP context for logging and debugging.
|
|
231
|
+
name : str, optional
|
|
232
|
+
Component name to filter by (case-insensitive). If None, all components match.
|
|
233
|
+
module_path : str, optional
|
|
234
|
+
Module path prefix to filter by. If None, all components match.
|
|
235
|
+
package : str, optional
|
|
236
|
+
Package name to filter by. For example "panel" or "panel_material_ui". If None, all components match.
|
|
237
|
+
|
|
238
|
+
Returns
|
|
239
|
+
-------
|
|
240
|
+
list[ComponentDetails]
|
|
241
|
+
List of components matching the specified criteria.
|
|
242
|
+
"""
|
|
243
|
+
components_list = []
|
|
244
|
+
|
|
245
|
+
for component in await _get_all_components(ctx=ctx):
|
|
246
|
+
if name and component.name.lower() != name.lower():
|
|
247
|
+
continue
|
|
248
|
+
if package and component.package != package:
|
|
249
|
+
continue
|
|
250
|
+
if module_path and not component.module_path.startswith(module_path):
|
|
251
|
+
continue
|
|
252
|
+
components_list.append(component)
|
|
253
|
+
|
|
254
|
+
return components_list
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
@mcp.tool
|
|
258
|
+
async def list_components(ctx: Context, name: str | None = None, module_path: str | None = None, package: str | None = None) -> list[ComponentSummary]:
|
|
259
|
+
"""
|
|
260
|
+
Get a summary list of Panel components without detailed docstring and parameter information.
|
|
261
|
+
|
|
262
|
+
Use this tool to get an overview of available Panel components when you want to browse
|
|
263
|
+
or discover components without needing full parameter details. This is faster than
|
|
264
|
+
get_component and provides just the essential information.
|
|
265
|
+
|
|
266
|
+
Parameters
|
|
267
|
+
----------
|
|
268
|
+
ctx : Context
|
|
269
|
+
FastMCP context (automatically provided by the MCP framework).
|
|
270
|
+
name : str, optional
|
|
271
|
+
Component name to filter by (case-insensitive). If None, returns all components.
|
|
272
|
+
Examples: "Button", "TextInput", "Slider"
|
|
273
|
+
module_path : str, optional
|
|
274
|
+
Module path prefix to filter by. If None, returns all components.
|
|
275
|
+
Examples: "panel.widgets" to get all widgets, "panel.pane" to get all panes
|
|
276
|
+
package : str, optional
|
|
277
|
+
Package name to filter by. If None, returns all components.
|
|
278
|
+
Examples: "hvplot", "panel" or "panel_material_ui"
|
|
279
|
+
|
|
280
|
+
Returns
|
|
281
|
+
-------
|
|
282
|
+
list[ComponentSummary]
|
|
283
|
+
List of component summaries containing name, package, description, and module path.
|
|
284
|
+
No parameter details are included for faster responses.
|
|
285
|
+
|
|
286
|
+
Examples
|
|
287
|
+
--------
|
|
288
|
+
Get all available components:
|
|
289
|
+
>>> list_components()
|
|
290
|
+
[ComponentSummary(name="Button", package="panel", description="A clickable button widget", ...)]
|
|
291
|
+
|
|
292
|
+
Get all Material UI components:
|
|
293
|
+
>>> list_components(package="panel_material_ui")
|
|
294
|
+
[ComponentSummary(name="Button", package="panel_material_ui", ...)]
|
|
295
|
+
|
|
296
|
+
Get all Button components from all packages:
|
|
297
|
+
>>> list_components(name="Button")
|
|
298
|
+
[ComponentSummary(name="Button", package="panel", ...), ComponentSummary(name="Button", package="panel_material_ui", ...)]
|
|
299
|
+
"""
|
|
300
|
+
components_list = []
|
|
301
|
+
|
|
302
|
+
for component in await _get_all_components(ctx=ctx):
|
|
303
|
+
if name and component.name.lower() != name.lower():
|
|
304
|
+
continue
|
|
305
|
+
if package and component.package != package:
|
|
306
|
+
continue
|
|
307
|
+
if module_path and not component.module_path.startswith(module_path):
|
|
308
|
+
continue
|
|
309
|
+
components_list.append(component.to_base())
|
|
310
|
+
|
|
311
|
+
return components_list
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
@mcp.tool
|
|
315
|
+
async def get_component(ctx: Context, name: str | None = None, module_path: str | None = None, package: str | None = None) -> ComponentDetails:
|
|
316
|
+
"""
|
|
317
|
+
Get complete details about a single Panel component including docstring and parameters.
|
|
318
|
+
|
|
319
|
+
Use this tool when you need full information about a specific Panel component, including
|
|
320
|
+
its docstring, parameter specifications, and initialization signature. This is the most
|
|
321
|
+
comprehensive tool for component information.
|
|
322
|
+
|
|
323
|
+
IMPORTANT: This tool returns exactly one component. If your criteria match multiple components,
|
|
324
|
+
you'll get an error asking you to be more specific.
|
|
325
|
+
|
|
326
|
+
Parameters
|
|
327
|
+
----------
|
|
328
|
+
ctx : Context
|
|
329
|
+
FastMCP context (automatically provided by the MCP framework).
|
|
330
|
+
name : str, optional
|
|
331
|
+
Component name to match (case-insensitive). If None, must specify other criteria.
|
|
332
|
+
Examples: "Button", "TextInput", "Slider"
|
|
333
|
+
module_path : str, optional
|
|
334
|
+
Full module path to match. If None, uses name and package to find component.
|
|
335
|
+
Examples: "panel.widgets.Button", "panel_material_ui.Button"
|
|
336
|
+
package : str, optional
|
|
337
|
+
Package name to filter by. If None, searches all packages.
|
|
338
|
+
Examples: "panel" or "panel_material_ui"
|
|
339
|
+
|
|
340
|
+
Returns
|
|
341
|
+
-------
|
|
342
|
+
ComponentDetails
|
|
343
|
+
Complete component information including docstring, parameters, and initialization signature.
|
|
344
|
+
|
|
345
|
+
Raises
|
|
346
|
+
------
|
|
347
|
+
ValueError
|
|
348
|
+
If no components match the criteria or if multiple components match (be more specific).
|
|
349
|
+
|
|
350
|
+
Examples
|
|
351
|
+
--------
|
|
352
|
+
Get Panel's Button component:
|
|
353
|
+
>>> get_component(name="Button", package="panel")
|
|
354
|
+
ComponentDetails(name="Button", package="panel", docstring="A clickable button...", parameters={...})
|
|
355
|
+
|
|
356
|
+
Get Material UI Button component:
|
|
357
|
+
>>> get_component(name="Button", package="panel_material_ui")
|
|
358
|
+
ComponentDetails(name="Button", package="panel_material_ui", ...)
|
|
359
|
+
|
|
360
|
+
Get component by exact module path:
|
|
361
|
+
>>> get_component(module_path="panel.widgets.button.Button")
|
|
362
|
+
ComponentDetails(name="Button", module_path="panel.widgets.button.Button", ...)
|
|
363
|
+
"""
|
|
364
|
+
components_list = await _get_component(ctx, name, module_path, package)
|
|
365
|
+
|
|
366
|
+
if not components_list:
|
|
367
|
+
raise ValueError(f"No components found matching criteria: '{name}', '{module_path}', '{package}'. Please check your inputs.")
|
|
368
|
+
if len(components_list) > 1:
|
|
369
|
+
module_paths = "'" + "','".join([component.module_path for component in components_list]) + "'"
|
|
370
|
+
raise ValueError(f"Multiple components found matching criteria: {module_paths}. Please refine your search.")
|
|
371
|
+
component = components_list[0]
|
|
372
|
+
return component
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
@mcp.tool
|
|
376
|
+
async def get_component_parameters(ctx: Context, name: str | None = None, module_path: str | None = None, package: str | None = None) -> dict[str, ParameterInfo]:
|
|
377
|
+
"""
|
|
378
|
+
Get detailed parameter information for a single Panel component.
|
|
379
|
+
|
|
380
|
+
Use this tool when you need to understand the parameters of a specific Panel component,
|
|
381
|
+
including their types, default values, documentation, and constraints. This is useful
|
|
382
|
+
for understanding how to properly initialize and configure a component.
|
|
383
|
+
|
|
384
|
+
IMPORTANT: This tool returns parameters for exactly one component. If your criteria
|
|
385
|
+
match multiple components, you'll get an error asking you to be more specific.
|
|
386
|
+
|
|
387
|
+
Parameters
|
|
388
|
+
----------
|
|
389
|
+
ctx : Context
|
|
390
|
+
FastMCP context (automatically provided by the MCP framework).
|
|
391
|
+
name : str, optional
|
|
392
|
+
Component name to match (case-insensitive). If None, must specify other criteria.
|
|
393
|
+
Examples: "Button", "TextInput", "Slider"
|
|
394
|
+
module_path : str, optional
|
|
395
|
+
Full module path to match. If None, uses name and package to find component.
|
|
396
|
+
Examples: "panel.widgets.Button", "panel_material_ui.Button"
|
|
397
|
+
package : str, optional
|
|
398
|
+
Package name to filter by. If None, searches all packages.
|
|
399
|
+
Examples: "hvplot", "panel" or "panel_material_ui"
|
|
400
|
+
|
|
401
|
+
Returns
|
|
402
|
+
-------
|
|
403
|
+
dict[str, ParameterInfo]
|
|
404
|
+
Dictionary mapping parameter names to their detailed information, including:
|
|
405
|
+
- type: Parameter type (e.g., 'String', 'Number', 'Boolean')
|
|
406
|
+
- default: Default value
|
|
407
|
+
- doc: Parameter documentation
|
|
408
|
+
- bounds: Value constraints for numeric parameters
|
|
409
|
+
- objects: Available options for selector parameters
|
|
410
|
+
|
|
411
|
+
Raises
|
|
412
|
+
------
|
|
413
|
+
ValueError
|
|
414
|
+
If no components match the criteria or if multiple components match (be more specific).
|
|
415
|
+
|
|
416
|
+
Examples
|
|
417
|
+
--------
|
|
418
|
+
Get Button parameters:
|
|
419
|
+
>>> get_component_parameters(name="Button", package="panel")
|
|
420
|
+
{"name": ParameterInfo(type="String", default="Button", doc="The text displayed on the button"), ...}
|
|
421
|
+
|
|
422
|
+
Get TextInput parameters:
|
|
423
|
+
>>> get_component_parameters(name="TextInput", package="panel")
|
|
424
|
+
{"value": ParameterInfo(type="String", default="", doc="The current text value"), ...}
|
|
425
|
+
|
|
426
|
+
Get parameters by exact module path:
|
|
427
|
+
>>> get_component_parameters(module_path="panel.widgets.Slider")
|
|
428
|
+
{"start": ParameterInfo(type="Number", default=0, bounds=(0, 100)), ...}
|
|
429
|
+
"""
|
|
430
|
+
components_list = await _get_component(ctx, name, module_path, package)
|
|
431
|
+
|
|
432
|
+
if not components_list:
|
|
433
|
+
raise ValueError(f"No components found matching criteria: '{name}', '{module_path}', '{package}'. Please check your inputs.")
|
|
434
|
+
if len(components_list) > 1:
|
|
435
|
+
module_paths = "'" + "','".join([component.module_path for component in components_list]) + "'"
|
|
436
|
+
raise ValueError(f"Multiple components found matching criteria: {module_paths}. Please refine your search.")
|
|
437
|
+
|
|
438
|
+
component = components_list[0]
|
|
439
|
+
return component.parameters
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
if __name__ == "__main__":
|
|
443
|
+
mcp.run(transport=_config.server.transport)
|
holoviz_mcp/py.typed
ADDED
|
File without changes
|
holoviz_mcp/serve.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Serve Panel apps with configurable arguments."""
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
# Configure logging
|
|
10
|
+
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
THUMBNAILS_DIR = Path(__file__).parent / "thumbnails"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def main() -> None:
|
|
17
|
+
"""Serve all Panel apps in the apps directory."""
|
|
18
|
+
apps_dir = Path(__file__).parent / "apps"
|
|
19
|
+
app_files = [str(f) for f in apps_dir.glob("*.py") if f.name != "__init__.py"]
|
|
20
|
+
|
|
21
|
+
if not app_files:
|
|
22
|
+
logger.warning("No Panel apps found in apps directory")
|
|
23
|
+
return
|
|
24
|
+
|
|
25
|
+
# Use python -m panel to ensure we use the same Python environment
|
|
26
|
+
cmd = [sys.executable, "-m", "panel", "serve", *app_files, "--static-dirs", f"thumbnails={THUMBNAILS_DIR}"]
|
|
27
|
+
logger.info(f"Running: {' '.join(cmd)}")
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
subprocess.run(cmd, check=True)
|
|
31
|
+
except KeyboardInterrupt:
|
|
32
|
+
logger.info("Server stopped")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
if __name__ == "__main__":
|
|
36
|
+
main()
|
holoviz_mcp/server.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""HoloViz MCP Server.
|
|
2
|
+
|
|
3
|
+
This MCP server provides comprehensive tools, resources and prompts for working with the HoloViz ecosystem,
|
|
4
|
+
including Panel and hvPlot following best practices.
|
|
5
|
+
|
|
6
|
+
The server is composed of multiple sub-servers that provide various functionalities:
|
|
7
|
+
|
|
8
|
+
- Documentation: Search and access HoloViz documentation as context
|
|
9
|
+
- hvPlot: Tools, resources and prompts for using hvPlot to develop quick, interactive plots in Python
|
|
10
|
+
- Panel: Tools, resources and prompts for using Panel Material UI
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import asyncio
|
|
14
|
+
import logging
|
|
15
|
+
import os
|
|
16
|
+
|
|
17
|
+
from fastmcp import FastMCP
|
|
18
|
+
|
|
19
|
+
from holoviz_mcp.config.loader import get_config
|
|
20
|
+
from holoviz_mcp.holoviz_mcp.server import mcp as holoviz_mcp
|
|
21
|
+
from holoviz_mcp.hvplot_mcp.server import mcp as hvplot_mcp
|
|
22
|
+
from holoviz_mcp.panel_mcp.server import mcp as panel_mcp
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
mcp: FastMCP = FastMCP(
|
|
27
|
+
name="holoviz",
|
|
28
|
+
instructions="""
|
|
29
|
+
[his MCP server provides comprehensive tools, resources and prompts for working with the HoloViz ecosystem following best practices.
|
|
30
|
+
|
|
31
|
+
HoloViz provides a set of core Python packages that make visualization easier, more accurate, and more powerful:
|
|
32
|
+
|
|
33
|
+
- [Panel](https://panel.holoviz.org): for making apps and dashboards for your plots from any supported plotting library.
|
|
34
|
+
- [hvPlot](https://hvplot.holoviz.org): to quickly generate interactive plots from your data.
|
|
35
|
+
- [HoloViews](https://holoviews.org): to help you make all of your data instantly visualizable.
|
|
36
|
+
- [GeoViews](https://geoviews.org): to extend HoloViews for geographic data.
|
|
37
|
+
- [Datashader](https://datashader.org): for rendering even the largest datasets.
|
|
38
|
+
- [Lumen](https://lumen.holoviz.org): to build data-driven dashboards from a simple YAML specification that's well suited to modern AI tools like LLMs.
|
|
39
|
+
- [Param](https://param.holoviz.org): to create declarative user-configurable objects.
|
|
40
|
+
- [Colorcet](https://colorcet.holoviz.org): for perceptually uniform colormaps.
|
|
41
|
+
|
|
42
|
+
The server is composed of multiple sub-servers that provide various functionalities:
|
|
43
|
+
|
|
44
|
+
- Documentation: Search and access HoloViz documentation and reference guides
|
|
45
|
+
- Panel: Tools, resources and prompts for using Panel and Panel Extension packages
|
|
46
|
+
- hvPlot: Tools, resources and prompts for using hvPlot to develop quick, interactive plots in Python
|
|
47
|
+
""",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
async def setup_composed_server() -> None:
|
|
52
|
+
"""Set up the composed server by importing all sub-servers with prefixes.
|
|
53
|
+
|
|
54
|
+
This uses static composition (import_server), which copies components
|
|
55
|
+
from sub-servers into the main server with appropriate prefixes.
|
|
56
|
+
"""
|
|
57
|
+
await mcp.import_server(holoviz_mcp, prefix="holoviz")
|
|
58
|
+
await mcp.import_server(hvplot_mcp, prefix="hvplot")
|
|
59
|
+
await mcp.import_server(panel_mcp, prefix="panel")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def main() -> None:
|
|
63
|
+
"""Set up and run the composed MCP server."""
|
|
64
|
+
pid = f"Process ID: {os.getpid()}"
|
|
65
|
+
print(pid) # noqa: T201
|
|
66
|
+
|
|
67
|
+
async def setup_and_run() -> None:
|
|
68
|
+
await setup_composed_server()
|
|
69
|
+
config = get_config()
|
|
70
|
+
|
|
71
|
+
# Pass host and port for HTTP transport
|
|
72
|
+
if config.server.transport == "http":
|
|
73
|
+
await mcp.run_async(
|
|
74
|
+
transport=config.server.transport,
|
|
75
|
+
host=config.server.host,
|
|
76
|
+
port=config.server.port,
|
|
77
|
+
)
|
|
78
|
+
else:
|
|
79
|
+
await mcp.run_async(transport=config.server.transport)
|
|
80
|
+
|
|
81
|
+
asyncio.run(setup_and_run())
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
if __name__ == "__main__":
|
|
85
|
+
# Run the composed MCP server
|
|
86
|
+
main()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Shared features for the holoviz-mcp project."""
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Extract tools from the MCP server for documentation."""
|
|
3
|
+
|
|
4
|
+
import asyncio
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
from holoviz_mcp.server import mcp
|
|
8
|
+
from holoviz_mcp.server import setup_composed_server
|
|
9
|
+
|
|
10
|
+
# Configure logging
|
|
11
|
+
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
async def extract_tools():
|
|
16
|
+
"""Extract available tools from the HoloViz MCP server and return as structured data."""
|
|
17
|
+
await setup_composed_server()
|
|
18
|
+
tools_dict = await mcp.get_tools()
|
|
19
|
+
|
|
20
|
+
# Group tools by category
|
|
21
|
+
holoviz_tools = []
|
|
22
|
+
panel_tools = []
|
|
23
|
+
utility_tools = []
|
|
24
|
+
|
|
25
|
+
for tool_name, tool_info in tools_dict.items():
|
|
26
|
+
tool_data = {"name": tool_name, "description": getattr(tool_info, "description", "No description available"), "parameters": []}
|
|
27
|
+
|
|
28
|
+
# Get input schema
|
|
29
|
+
input_schema = getattr(tool_info, "inputSchema", None)
|
|
30
|
+
if input_schema and hasattr(input_schema, "get") and "properties" in input_schema:
|
|
31
|
+
for param_name, param_info in input_schema["properties"].items():
|
|
32
|
+
required = param_name in input_schema.get("required", [])
|
|
33
|
+
param_type = param_info.get("type", "unknown")
|
|
34
|
+
desc = param_info.get("description", "No description")
|
|
35
|
+
tool_data["parameters"].append({"name": param_name, "type": param_type, "required": required, "description": desc})
|
|
36
|
+
|
|
37
|
+
# Categorize tools
|
|
38
|
+
if any(x in tool_name for x in ["docs", "get_best_practices", "get_reference_guide", "get_document", "update_docs"]) or (
|
|
39
|
+
tool_name == "search" and "component" not in str(tool_info)
|
|
40
|
+
):
|
|
41
|
+
holoviz_tools.append(tool_data)
|
|
42
|
+
elif any(x in tool_name for x in ["component", "packages"]) or "search" in tool_name:
|
|
43
|
+
panel_tools.append(tool_data)
|
|
44
|
+
else:
|
|
45
|
+
utility_tools.append(tool_data)
|
|
46
|
+
|
|
47
|
+
return {"panel_tools": panel_tools, "holoviz_tools": holoviz_tools, "utility_tools": utility_tools}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
async def main():
|
|
51
|
+
"""Extract and print available tools from the HoloViz MCP server."""
|
|
52
|
+
tools_data = await extract_tools()
|
|
53
|
+
logger.info("## 🛠️ Available Tools")
|
|
54
|
+
logger.info("")
|
|
55
|
+
|
|
56
|
+
def print_tools(tools_list, category_name):
|
|
57
|
+
if not tools_list:
|
|
58
|
+
return
|
|
59
|
+
logger.info("<details>")
|
|
60
|
+
logger.info(f"<summary><b>{category_name}</b></summary>")
|
|
61
|
+
logger.info("")
|
|
62
|
+
for tool_data in tools_list:
|
|
63
|
+
logger.info(f"- **{tool_data['name']}**: {tool_data['description']}")
|
|
64
|
+
logger.info("")
|
|
65
|
+
logger.info("</details>")
|
|
66
|
+
logger.info("")
|
|
67
|
+
|
|
68
|
+
print_tools(tools_data["panel_tools"], "Panel Components")
|
|
69
|
+
print_tools(tools_data["holoviz_tools"], "Documentation")
|
|
70
|
+
print_tools(tools_data["utility_tools"], "Utilities")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
if __name__ == "__main__":
|
|
74
|
+
asyncio.run(main())
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|