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.
Files changed (56) hide show
  1. holoviz_mcp/__init__.py +18 -0
  2. holoviz_mcp/apps/__init__.py +1 -0
  3. holoviz_mcp/apps/configuration_viewer.py +116 -0
  4. holoviz_mcp/apps/holoviz_get_best_practices.py +173 -0
  5. holoviz_mcp/apps/holoviz_search.py +319 -0
  6. holoviz_mcp/apps/hvplot_get_docstring.py +255 -0
  7. holoviz_mcp/apps/hvplot_get_signature.py +252 -0
  8. holoviz_mcp/apps/hvplot_list_plot_types.py +83 -0
  9. holoviz_mcp/apps/panel_get_component.py +496 -0
  10. holoviz_mcp/apps/panel_get_component_parameters.py +467 -0
  11. holoviz_mcp/apps/panel_list_components.py +311 -0
  12. holoviz_mcp/apps/panel_list_packages.py +71 -0
  13. holoviz_mcp/apps/panel_search_components.py +312 -0
  14. holoviz_mcp/cli.py +75 -0
  15. holoviz_mcp/client.py +94 -0
  16. holoviz_mcp/config/__init__.py +29 -0
  17. holoviz_mcp/config/config.yaml +178 -0
  18. holoviz_mcp/config/loader.py +316 -0
  19. holoviz_mcp/config/models.py +208 -0
  20. holoviz_mcp/config/resources/best-practices/holoviews.md +423 -0
  21. holoviz_mcp/config/resources/best-practices/hvplot.md +465 -0
  22. holoviz_mcp/config/resources/best-practices/panel-material-ui.md +318 -0
  23. holoviz_mcp/config/resources/best-practices/panel.md +562 -0
  24. holoviz_mcp/config/schema.json +228 -0
  25. holoviz_mcp/holoviz_mcp/__init__.py +1 -0
  26. holoviz_mcp/holoviz_mcp/data.py +970 -0
  27. holoviz_mcp/holoviz_mcp/models.py +21 -0
  28. holoviz_mcp/holoviz_mcp/pages_design.md +407 -0
  29. holoviz_mcp/holoviz_mcp/server.py +220 -0
  30. holoviz_mcp/hvplot_mcp/__init__.py +1 -0
  31. holoviz_mcp/hvplot_mcp/server.py +146 -0
  32. holoviz_mcp/panel_mcp/__init__.py +17 -0
  33. holoviz_mcp/panel_mcp/data.py +319 -0
  34. holoviz_mcp/panel_mcp/models.py +124 -0
  35. holoviz_mcp/panel_mcp/server.py +443 -0
  36. holoviz_mcp/py.typed +0 -0
  37. holoviz_mcp/serve.py +36 -0
  38. holoviz_mcp/server.py +86 -0
  39. holoviz_mcp/shared/__init__.py +1 -0
  40. holoviz_mcp/shared/extract_tools.py +74 -0
  41. holoviz_mcp/thumbnails/configuration_viewer.png +0 -0
  42. holoviz_mcp/thumbnails/holoviz_get_best_practices.png +0 -0
  43. holoviz_mcp/thumbnails/holoviz_search.png +0 -0
  44. holoviz_mcp/thumbnails/hvplot_get_docstring.png +0 -0
  45. holoviz_mcp/thumbnails/hvplot_get_signature.png +0 -0
  46. holoviz_mcp/thumbnails/hvplot_list_plot_types.png +0 -0
  47. holoviz_mcp/thumbnails/panel_get_component.png +0 -0
  48. holoviz_mcp/thumbnails/panel_get_component_parameters.png +0 -0
  49. holoviz_mcp/thumbnails/panel_list_components.png +0 -0
  50. holoviz_mcp/thumbnails/panel_list_packages.png +0 -0
  51. holoviz_mcp/thumbnails/panel_search_components.png +0 -0
  52. holoviz_mcp-0.4.0.dist-info/METADATA +216 -0
  53. holoviz_mcp-0.4.0.dist-info/RECORD +56 -0
  54. holoviz_mcp-0.4.0.dist-info/WHEEL +4 -0
  55. holoviz_mcp-0.4.0.dist-info/entry_points.txt +2 -0
  56. holoviz_mcp-0.4.0.dist-info/licenses/LICENSE.txt +30 -0
@@ -0,0 +1,21 @@
1
+ """Data models for the HoloViz Documentation MCP server."""
2
+
3
+ from typing import Optional
4
+
5
+ from pydantic import BaseModel
6
+ from pydantic import Field
7
+ from pydantic import HttpUrl
8
+
9
+
10
+ class Document(BaseModel):
11
+ """Represents a document."""
12
+
13
+ title: str = Field(..., description="The title of the document.")
14
+ url: HttpUrl = Field(..., description="The URL of the rendered, target document.")
15
+ project: str = Field(..., description="The project to which the document belongs.")
16
+ source_path: str = Field(..., description="The path to the document within the project.")
17
+ source_url: HttpUrl = Field(..., description="The URL to the source document.")
18
+ is_reference: bool = Field(..., description="Indicates if the document is a reference guide.")
19
+ description: Optional[str] = Field(default=None, description="A brief description of the document.")
20
+ content: Optional[str] = Field(default=None, description="The content of the documentation, if available. In Markdown format if possible.")
21
+ relevance_score: Optional[float] = Field(default=None, description="Relevance score of the document, where 1 is the highest score indicating an exact match.")
@@ -0,0 +1,407 @@
1
+ # Pages Tool Design Document
2
+
3
+ ## Overview
4
+
5
+ The `pages` tool is designed to search HoloViz documentation and return relevant pages based on a user query. This document outlines the architecture, implementation approach, and technical decisions for building this functionality.
6
+
7
+ ## Current State Analysis
8
+
9
+ The current `pages` tool in `server.py` is a stub that needs:
10
+ 1. A `Page` model definition
11
+ 2. Data preparation pipeline
12
+
13
+ ### Search Implementation Details
14
+ ```python
15
+ import re
16
+ from pathlib import Path
17
+ from fastmcp import Context
18
+
19
+ class DocumentationIndexer:
20
+ def search_pages(
21
+ self,
22
+ name: str | None = None,
23
+ path: str | None = None,
24
+ query: str | None = None,
25
+ package: str | None = None,
26
+ content: bool = True,
27
+ max_results: int | None = 5,
28
+ ) -> List[Page]:
29
+ """Enhanced search with multiple filtering options and regex support."""
30
+
31
+ # Build ChromaDB where clause for metadata filtering
32
+ where_clause = {}
33
+ if package:
34
+ where_clause["package"] = package
35
+
36
+ # Add filtering to where clause
37
+ if path:
38
+ where_clause["source_path"] = {"$regex": path}
39
+
40
+ if name:
41
+ # Exact filename matching, not regex
42
+ where_clause["name"] = name
43
+
44
+ # Perform search
45
+ if query:
46
+ # Vector similarity search
47
+ results = self.collection.query(
48
+ query_texts=[query],
49
+ n_results=max_results,
50
+ where=where_clause if where_clause else None
51
+ )
52
+ else:
53
+ # Metadata-only search
54
+ results = self.collection.get(
55
+ where=where_clause if where_clause else None,
56
+ limit=max_results
57
+ )
58
+
59
+ # Convert to Page objects
60
+ pages = []
61
+ if results['ids'] and results['ids'][0]:
62
+ for i, doc_id in enumerate(results['ids'][0]):
63
+ metadata = results['metadatas'][0][i]
64
+
65
+ # Include full content if requested
66
+ if content:
67
+ content_text = results['documents'][0][i] if results['documents'] else ""
68
+ else:
69
+ # Just metadata - no content
70
+ content_text = None
71
+
72
+ page = Page(
73
+ title=metadata.get('title', ''),
74
+ url=metadata.get('url', ''),
75
+ package=metadata.get('package', ''),
76
+ path=metadata.get('path', ''),
77
+ description=metadata.get('description', ''),
78
+ content_preview=content_text,
79
+ relevance_score=1.0 - results['distances'][0][i] if results.get('distances') else None
80
+ )
81
+ pages.append(page)
82
+
83
+ return pages
84
+ ```
85
+
86
+ ### Notebook to Markdown Path Mapping
87
+ ```python
88
+ def process_notebook_file(self, file_path: Path, package: str, folder_type: str) -> Optional[Dict]:
89
+ """Process a notebook file and map it to documentation structure."""
90
+ try:
91
+ # Convert notebook to markdown
92
+ markdown_content = self.convert_notebook_to_markdown(file_path)
93
+ if not markdown_content:
94
+ return None
95
+
96
+ # Map notebook path to documentation path
97
+ # examples/reference/widgets/Button.ipynb -> reference/widgets/Button.md
98
+ relative_path = file_path.relative_to(self.repos_dir / package)
99
+
100
+ if str(relative_path).startswith('examples/reference/'):
101
+ # Transform examples/reference/widgets/Button.ipynb to reference/widgets/Button.md
102
+ doc_path = str(relative_path).replace('examples/reference/', 'reference/')
103
+ doc_path = doc_path.replace('.ipynb', '.md')
104
+ else:
105
+ # Keep original path but change extension
106
+ doc_path = str(relative_path).replace('.ipynb', '.md')
107
+
108
+ # Extract title and content
109
+ title = file_path.stem.replace('_', ' ').title()
110
+ # ... rest of processing
111
+
112
+ return {
113
+ 'title': title,
114
+ 'url': self._generate_doc_url(package, Path(doc_path), folder_type),
115
+ 'package': package,
116
+ 'path': doc_path, # This is the key - mapped path
117
+ 'description': description,
118
+ 'content': text_content,
119
+ 'folder_type': folder_type,
120
+ 'id': f"{package}_{doc_path}".replace('/', '_').replace('.', '_')
121
+ }
122
+
123
+ except Exception as e:
124
+ logger.error(f"Failed to process notebook file {file_path}: {e}")
125
+ return None
126
+ ```
127
+
128
+ ### Configuration Managementor documentation indexing
129
+ 3. Vector search implementation
130
+ 4. Integration with the FastMCP framework
131
+
132
+ ## Architecture
133
+
134
+ ### 1. Data Models (`src/holoviz_mcp/docs_mcp/models.py`)
135
+
136
+ ```python
137
+ from pydantic import BaseModel, HttpUrl
138
+ from typing import Optional
139
+
140
+ class Page(BaseModel):
141
+ """Represents a documentation page in the HoloViz ecosystem."""
142
+
143
+ title: str
144
+ url: HttpUrl
145
+ package: str
146
+ path: str
147
+ description: Optional[str] = None
148
+ content_preview: Optional[str] = None
149
+ relevance_score: Optional[float] = None
150
+ ```
151
+
152
+ ### 2. Data Preparation Pipeline (`src/holoviz_mcp/docs_mcp/data.py`)
153
+
154
+ The data preparation will involve:
155
+
156
+ #### HoloViz Packages to Index
157
+ - `panel` - Main Panel library
158
+ - `param` - Parameter handling
159
+ - `datashader` - Large data visualization
160
+ - `holoviews` - Declarative data visualization
161
+ - `geoviews` - Geographic visualization
162
+ - `hvplot` - High-level plotting interface
163
+ - `colorcet` - Color palettes
164
+ - `lumen` - Dashboard building
165
+ - `panel-material-ui` - Material UI components
166
+
167
+ #### Implementation Strategy
168
+
169
+ ```python
170
+ import asyncio
171
+ import git
172
+ from pathlib import Path
173
+ from typing import List, Dict, Optional
174
+ import chromadb
175
+ from sentence_transformers import SentenceTransformer
176
+ import markdown
177
+ import yaml
178
+ from nbconvert import MarkdownExporter
179
+
180
+ class DocumentationIndexer:
181
+ """Handles cloning, processing, and indexing of HoloViz documentation."""
182
+
183
+ def __init__(self, data_dir: Path = Path("~/holoviz_mcp/data").expanduser()):
184
+ self.data_dir = data_dir
185
+ self.chroma_client = chromadb.PersistentClient(path=str(data_dir / "chroma"))
186
+ self.collection = self.chroma_client.get_or_create_collection("holoviz_docs")
187
+ self.model = SentenceTransformer('all-MiniLM-L6-v2')
188
+ self.nb_exporter = MarkdownExporter()
189
+ self.config = self.load_config()
190
+
191
+ def load_config(self) -> Dict:
192
+ """Load configuration from file specified by environment variable."""
193
+
194
+ def clone_or_update_repos(self, ctx: Context = None):
195
+ """Clone or update all HoloViz repositories with progress reporting."""
196
+
197
+ def extract_docs_metadata(self, repo_path: Path, package: str) -> List[Dict]:
198
+ """Extract documentation files and metadata from a repository."""
199
+
200
+ def process_markdown_file(self, file_path: Path, package: str) -> Dict:
201
+ """Process a single markdown file and extract relevant information."""
202
+
203
+ def process_notebook_file(self, file_path: Path, package: str) -> Dict:
204
+ """Process a single notebook file and convert to markdown."""
205
+
206
+ def create_embeddings(self, docs: List[Dict], ctx: Context = None) -> List[List[float]]:
207
+ """Create embeddings for documentation content with progress reporting."""
208
+
209
+ def index_documentation(self, ctx: Context = None):
210
+ """Main method to index all documentation with progress reporting."""
211
+
212
+ def search_pages(
213
+ self,
214
+ name: str | None = None,
215
+ path: str | None = None,
216
+ query: str | None = None,
217
+ package: str | None = None,
218
+ content: bool = True,
219
+ max_results: int | None = 5,
220
+ ) -> List[Page]:
221
+ """Search indexed documentation and return relevant pages.
222
+
223
+ Args:
224
+ name: Optional exact filename to filter by (e.g., "Audio.md", "Button.md")
225
+ path: Optional path pattern (regex) to filter by (e.g., "reference/.*\.md$")
226
+ query: Optional semantic search query
227
+ package: Optional package name to filter by
228
+ content: Whether to include full content in results (default: True)
229
+ max_results: Maximum number of results to return
230
+
231
+ Returns:
232
+ List of Page objects matching the search criteria
233
+ """
234
+ ```
235
+
236
+ ### 3. Documentation Processing Strategy
237
+
238
+ #### Git Repository Cloning
239
+ - Clone HoloViz repositories using GitPython
240
+ - Process both `docs/` and `examples/reference/` folders (configurable)
241
+ - Handle both markdown (.md) and notebook (.ipynb) files
242
+ - Support additional repositories via configuration file
243
+
244
+ #### Configuration File Support
245
+ - External configuration file for additional repositories
246
+ - Environment variable `HOLOVIZ_CONFIG_FILE` to specify config location
247
+ - Configurable folder names for documentation and reference guides
248
+ - Default folders: `docs/` and `examples/reference/`
249
+
250
+ #### Notebook Processing
251
+ - Use nbconvert to convert Jupyter notebooks to markdown
252
+ - Extract metadata and content from converted markdown
253
+ - Preserve code examples and outputs in searchable format
254
+ - Process notebooks in configurable reference folders
255
+ - **Important**: Reference notebooks in `examples/reference/**/*.ipynb` are indexed as `.md` files with paths like `reference/widgets/Button.md` to match the published documentation structure
256
+
257
+ #### Content Indexing
258
+ - Use ChromaDB for persistent vector storage
259
+ - Sentence transformers for embedding generation
260
+ - Direct markdown parsing without HTML conversion
261
+ - Metadata extraction including:
262
+ - Package name
263
+ - File path
264
+ - Title (from frontmatter or headings)
265
+ - Description (from frontmatter or first paragraph)
266
+ - Content preview
267
+
268
+ ### 4. Search Implementation
269
+
270
+ #### Enhanced Search Strategy
271
+ The search implementation now supports multiple search modes:
272
+
273
+ 1. **Name-based Search**: Filter by exact filename matching (e.g., "Audio.md", "Button.md")
274
+ 2. **Path-based Search**: Filter by file path using regex patterns (e.g., "reference/.*\.md$")
275
+ 3. **Semantic Search**: Vector similarity search using queries
276
+ 4. **Package Filtering**: Limit results to specific packages
277
+ 5. **Content Control**: Option to include full content (default: True) vs. metadata only
278
+
279
+ #### Regex Pattern Support
280
+ The `path` parameter supports regex patterns for flexible path matching:
281
+
282
+ - `reference/.*\.md$` - All markdown files in reference folder and subfolders
283
+ - `reference/panes/.*\.md$` - All markdown files in reference/panes folder
284
+ - `.*/Audio.*` - All files containing "Audio" anywhere in the tree
285
+ - `docs/how_to/.*` - All files in docs/how_to folder and subfolders
286
+
287
+ #### Search Process
288
+ 1. Apply exact filename filtering if `name` parameter provided
289
+ 2. Apply path filtering using regex patterns if `path` parameter provided
290
+ 3. Apply package filtering if `package` parameter provided
291
+ 4. If `query` provided, perform vector similarity search
292
+ 5. Combine and rank results by relevance
293
+ 6. Return top N results as Page objects
294
+ 7. Include full content if `content` is True (default), otherwise metadata only
295
+
296
+ ### 5. Integration Points
297
+
298
+ #### Configuration (`src/holoviz_mcp/shared/config.py`)
299
+ ```python
300
+ # Add documentation-specific configuration
301
+ DATA_DIR = Path(os.getenv("HOLOVIZ_DATA_DIR", "~/holoviz_mcp/data")).expanduser()
302
+ CONFIG_FILE = os.getenv("HOLOVIZ_CONFIG_FILE", "")
303
+ DOCS_UPDATE_INTERVAL = int(os.getenv("HOLOVIZ_DOCS_UPDATE_INTERVAL", "86400")) # 24 hours
304
+ ```
305
+
306
+ #### Configuration File Format
307
+ ```yaml
308
+ # holoviz_config.yaml
309
+ repositories:
310
+ # Core HoloViz packages (built-in)
311
+ panel:
312
+ url: "https://github.com/holoviz/panel.git"
313
+ docs_folder: "docs"
314
+ reference_folder: "examples/reference"
315
+
316
+ # Additional user-defined repositories
317
+ my_custom_panel_extension:
318
+ url: "https://github.com/user/my-panel-extension.git"
319
+ docs_folder: "documentation"
320
+ reference_folder: "examples"
321
+
322
+ another_project:
323
+ url: "https://github.com/org/another-project.git"
324
+ docs_folder: "docs"
325
+ reference_folder: "reference"
326
+
327
+ # Default folder configuration
328
+ default_docs_folder: "docs"
329
+ default_reference_folder: "examples/reference"
330
+ ```
331
+
332
+ ## Index Update Strategy
333
+
334
+ ### **First Time Initialization**
335
+ The documentation index will be created **automatically on first use** of either the `get_reference_guide` or `pages` tools:
336
+
337
+ ```python
338
+ # In both tools, there will be logic like:
339
+ if not indexer.is_indexed():
340
+ logger.info("Documentation index not found. Creating initial index...")
341
+ indexer.index_documentation()
342
+ ```
343
+
344
+ **Benefits of lazy initialization:**
345
+ - **No upfront setup required** - the index is created when first needed
346
+ - **Better UX** - users can start using tools immediately without waiting for setup
347
+ - **Resource efficiency** - only creates index when actually needed
348
+ - **Simpler deployment** - no background processes or startup delays
349
+
350
+ ### **Subsequent Updates**
351
+ After the initial creation, the index will be updated **only when manually triggered** using the `update_index` tool:
352
+
353
+ ```python
354
+ # Manual updates only
355
+ update_index() # User must explicitly call this
356
+ ```
357
+
358
+ **Benefits of manual updates:**
359
+ - **Predictable behavior** - index content doesn't change unexpectedly
360
+ - **Resource control** - users control when expensive re-indexing happens
361
+ - **Reliability** - no background processes that could fail silently
362
+ - **Explicit control** - users decide when to refresh documentation
363
+
364
+ ### **Implementation Details**
365
+
366
+ ```python
367
+ class DocumentationIndexer:
368
+ def is_indexed(self) -> bool:
369
+ """Check if documentation index exists and is valid."""
370
+ try:
371
+ # Check if ChromaDB collection exists and has documents
372
+ collection = self.chroma_client.get_collection("holoviz_docs")
373
+ count = collection.count()
374
+ return count > 0
375
+ except Exception:
376
+ return False
377
+
378
+ def ensure_indexed(self, ctx: Context = None):
379
+ """Ensure documentation is indexed, creating if necessary."""
380
+ if not self.is_indexed():
381
+ await self.log_info("Documentation index not found. Creating initial index...")
382
+ self.index_documentation(ctx)
383
+ ```
384
+
385
+ ### **Recommended Usage Pattern**
386
+
387
+ ```python
388
+ # Day 1: First use (automatic index creation)
389
+ get_reference_guide("Button", "panel") # Index created automatically during first call
390
+
391
+ # Day 7: Manual refresh to get latest documentation
392
+ update_index() # User explicitly updates when needed
393
+
394
+ # Day 14: Another manual refresh
395
+ update_index() # User controls update frequency
396
+ ```
397
+
398
+ ### **Alternative Strategies (Future Enhancements)**
399
+
400
+ The following could be added in future versions but are not currently implemented:
401
+
402
+ 1. **Time-based updates**: Check for updates every 24 hours (via `DOCS_UPDATE_INTERVAL`)
403
+ 2. **Webhook updates**: Update when GitHub repositories change
404
+ 3. **Startup checks**: Check for stale index on server startup
405
+ 4. **Background updates**: Periodic updates without blocking user requests
406
+
407
+ This strategy balances **ease of use** (automatic first-time setup) with **control** (manual updates when needed), making it suitable for an MCP server where users want predictable, on-demand documentation access.
@@ -0,0 +1,220 @@
1
+ """[HoloViz](https://holoviz.org/) Documentation MCP Server.
2
+
3
+ This server provides tools, resources and prompts for accessing documentation related to the HoloViz ecosystems.
4
+
5
+ Use this server to search and access documentation for HoloViz libraries, including Panel and hvPlot.
6
+ """
7
+
8
+ import logging
9
+
10
+ from fastmcp import Context
11
+ from fastmcp import FastMCP
12
+
13
+ from holoviz_mcp.config.loader import get_config
14
+ from holoviz_mcp.holoviz_mcp.data import DocumentationIndexer
15
+ from holoviz_mcp.holoviz_mcp.data import get_best_practices as _get_best_practices
16
+ from holoviz_mcp.holoviz_mcp.data import list_best_practices as _list_best_practices
17
+ from holoviz_mcp.holoviz_mcp.models import Document
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+ # Global indexer instance
22
+ _indexer = None
23
+
24
+
25
+ def get_indexer() -> DocumentationIndexer:
26
+ """Get or create the global DocumentationIndexer instance."""
27
+ global _indexer
28
+ if _indexer is None:
29
+ _indexer = DocumentationIndexer()
30
+ return _indexer
31
+
32
+
33
+ # The HoloViz MCP server instance
34
+ mcp: FastMCP = FastMCP(
35
+ name="documentation",
36
+ instructions="""
37
+ [HoloViz](https://holoviz.org/) Documentation MCP Server.
38
+
39
+ This server provides tools, resources and prompts for accessing documentation related to the HoloViz ecosystems.
40
+
41
+ Use this server to search and access documentation for HoloViz libraries, including Panel and hvPlot.
42
+ """,
43
+ )
44
+
45
+
46
+ @mcp.tool
47
+ def get_best_practices(project: str) -> str:
48
+ """Get best practices for using a project with LLMs.
49
+
50
+ DO Always use this tool to get best practices for using a project with LLMs before using it!
51
+
52
+ Args:
53
+ project (str): The name of the project to get best practices for. For example, "panel", "panel-material-ui", etc.
54
+
55
+ Returns
56
+ -------
57
+ str: A string containing the best practices for the project in Markdown format.
58
+ """
59
+ return _get_best_practices(project)
60
+
61
+
62
+ @mcp.tool
63
+ def list_best_practices() -> list[str]:
64
+ """List all available best practices projects.
65
+
66
+ This tool discovers available best practices from both user and default directories,
67
+ with user resources taking precedence over default ones.
68
+
69
+ Returns
70
+ -------
71
+ list[str]: A list of project names that have best practices available.
72
+ Names are returned in hyphenated format (e.g., "panel-material-ui").
73
+ """
74
+ return _list_best_practices()
75
+
76
+
77
+ @mcp.tool
78
+ async def get_reference_guide(component: str, project: str | None = None, content: bool = True, ctx: Context | None = None) -> list[Document]:
79
+ """Find reference guides for specific HoloViz components.
80
+
81
+ Reference guides are a subset of all documents that focus on specific UI components
82
+ or plot types, such as:
83
+
84
+ - `panel`: "Button", "TextInput", ...
85
+ - `hvplot`: "bar", "scatter", ...
86
+ - ...
87
+
88
+ DO use this tool to easily find reference guides for specific components in HoloViz libraries.
89
+
90
+ Args:
91
+ component (str): Name of the component (e.g., "Button", "TextInput", "bar", "scatter")
92
+ project (str, optional): Project name. Defaults to None (searches all projects).
93
+ Options: "panel", "panel-material-ui", "hvplot", "param", "holoviews"
94
+ content (bool, optional): Whether to include full content. Defaults to True.
95
+ Set to False to only return metadata for faster responses.
96
+
97
+ Returns
98
+ -------
99
+ list[Document]: A list of reference guides for the component.
100
+
101
+ Examples
102
+ --------
103
+ >>> get_reference_guide("Button") # Find Button component guide across all projects
104
+ >>> get_reference_guide("Button", "panel") # Find Panel Button component guide specifically
105
+ >>> get_reference_guide("TextInput", "panel-material-ui") # Find Material UI TextInput guide
106
+ >>> get_reference_guide("bar", "hvplot") # Find hvplot bar chart reference
107
+ >>> get_reference_guide("scatter", "hvplot") # Find hvplot scatter plot reference
108
+ >>> get_reference_guide("Audio", content=False) # Don't include Markdown content for faster response
109
+ """
110
+ indexer = get_indexer()
111
+ return await indexer.search_get_reference_guide(component, project, content, ctx=ctx)
112
+
113
+
114
+ @mcp.tool
115
+ async def list_projects() -> list[str]:
116
+ """List all available projects with documentation.
117
+
118
+ This tool discovers all projects that have documentation available in the index,
119
+ including both core HoloViz libraries and any additional user-defined projects.
120
+
121
+ Returns
122
+ -------
123
+ list[str]: A list of project names that have documentation available.
124
+ Names are returned in hyphenated format (e.g., "panel-material-ui").
125
+ """
126
+ indexer = get_indexer()
127
+ return await indexer.list_projects()
128
+
129
+
130
+ @mcp.tool
131
+ async def get_document(path: str, project: str, ctx: Context) -> Document:
132
+ """Retrieve a specific document by path and project.
133
+
134
+ Use this tool to look up a specific document within a project.
135
+
136
+ Args:
137
+ path: The relative path to the source document (e.g., "index.md", "how_to/customize.md")
138
+ project: the name of the project (e.g., "panel", "panel-material-ui", "hvplot")
139
+
140
+ Returns
141
+ -------
142
+ The markdown content of the specified document.
143
+ """
144
+ indexer = get_indexer()
145
+ return await indexer.get_document(path, project, ctx=ctx)
146
+
147
+
148
+ @mcp.tool
149
+ async def search(
150
+ query: str,
151
+ project: str | None = None,
152
+ content: bool = True,
153
+ max_results: int = 5,
154
+ ctx: Context | None = None,
155
+ ) -> list[Document]:
156
+ """Search HoloViz documentation using semantic similarity.
157
+
158
+ Optimized for finding relevant documentation based on natural language queries.
159
+
160
+ DO use this tool to find answers to questions about HoloViz libraries, such as Panel and hvPlot.
161
+
162
+ Args:
163
+ query (str): Search query using natural language.
164
+ For example "How to style Material UI components?" or "interactive plotting with widgets"
165
+ project (str, optional): Optional project filter. Defaults to None.
166
+ Options: "panel", "panel-material-ui", "hvplot", "param", "holoviews"
167
+ content (bool, optional): Whether to include full content. Defaults to True.
168
+ Set to False to only return metadata for faster responses.
169
+ max_results (int, optional): Maximum number of results to return. Defaults to 5.
170
+
171
+ Returns
172
+ -------
173
+ list[Document]: A list of relevant documents ordered by relevance.
174
+
175
+ Examples
176
+ --------
177
+ >>> search("How to style Material UI components?", "panel-material-ui") # Semantic search in specific project
178
+ >>> search("interactive plotting with widgets", "hvplot") # Find hvplot interactive guides
179
+ >>> search("dashboard layout best practices") # Search across all projects
180
+ >>> search("custom widgets", project="panel", max_results=3) # Limit results
181
+ >>> search("parameter handling", content=False) # Get metadata only for overview
182
+ """
183
+ indexer = get_indexer()
184
+ return await indexer.search(query, project, content, max_results, ctx=ctx)
185
+
186
+
187
+ @mcp.tool(enabled=False)
188
+ async def update_index(ctx: Context) -> str:
189
+ """Update the documentation index by re-cloning repositories and re-indexing content.
190
+
191
+ DO use this tool periodically (weekly) to ensure the documentation index is up-to-date
192
+ with the latest changes in the HoloViz ecosystem.
193
+
194
+ Warning: This operation can take a long time (up to 5 minutes) depending on the number of
195
+ repositories and their size!
196
+
197
+ Returns
198
+ -------
199
+ str: Status message indicating the result of the update operation.
200
+
201
+ Examples
202
+ --------
203
+ >>> update_index() # Updates all documentation repositories and rebuilds index
204
+ """
205
+ try:
206
+ indexer = get_indexer()
207
+
208
+ # Use True as ctx to enable print statements for user feedback
209
+ await indexer.index_documentation(ctx=ctx)
210
+
211
+ return "Documentation index updated successfully."
212
+ except Exception as e:
213
+ logger.error(f"Failed to update documentation index: {e}")
214
+ error_msg = f"Failed to update documentation index: {str(e)}"
215
+ return error_msg
216
+
217
+
218
+ if __name__ == "__main__":
219
+ config = get_config()
220
+ mcp.run(transport=config.server.transport)
@@ -0,0 +1 @@
1
+ """hvPlot MCP Server."""