Skip to content

Commit a2a9614

Browse files
fix
1 parent 762c577 commit a2a9614

6 files changed

Lines changed: 307 additions & 35 deletions

File tree

src/holoviz_mcp/holoviews_mcp/server.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
)
3131

3232

33-
@mcp.tool
33+
@mcp.tool()
3434
async def list_elements(ctx: Context) -> list[str]:
3535
"""
3636
List all available HoloViews visualization elements.
@@ -50,7 +50,7 @@ async def list_elements(ctx: Context) -> list[str]:
5050
return sorted(elements_list)
5151

5252

53-
@mcp.tool
53+
@mcp.tool()
5454
async def get_docstring(ctx: Context, element: str, backend: Literal["matplotlib", "bokeh", "plotly"] = "bokeh") -> str:
5555
"""
5656
Get the hvPlot docstring for a specific plot type, including available options and usage details.

src/holoviz_mcp/holoviz_mcp/data.py

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1258,25 +1258,43 @@ def _clone_or_update_repo_sync(self, repo_name: str, repo_config: "GitRepository
12581258

12591259
try:
12601260
if repo_path.exists():
1261-
logger.info(f"Updating {repo_name} repository at {repo_path}...")
12621261
repo = git.Repo(repo_path)
1263-
repo.remotes.origin.pull()
1264-
else:
1265-
logger.info(f"Cloning {repo_name} repository to {repo_path}...")
1266-
clone_kwargs: dict[str, Any] = {"depth": 1}
1267-
1268-
if repo_config.branch:
1269-
clone_kwargs["branch"] = repo_config.branch
1270-
elif repo_config.tag:
1271-
clone_kwargs["branch"] = repo_config.tag
1272-
elif repo_config.commit:
1273-
git.Repo.clone_from(str(repo_config.url), repo_path, **clone_kwargs)
1274-
repo = git.Repo(repo_path)
1275-
repo.git.checkout(repo_config.commit)
1262+
expected_ref = repo_config.branch or repo_config.tag
1263+
1264+
if expected_ref:
1265+
try:
1266+
current_ref = repo.active_branch.name
1267+
except TypeError:
1268+
current_ref = None # Detached HEAD (e.g. tag/commit checkout)
1269+
1270+
if current_ref != expected_ref:
1271+
logger.info(f"Stale checkout for {repo_name}: on '{current_ref}', expected '{expected_ref}'. " "Deleting and re-cloning...")
1272+
shutil.rmtree(repo_path)
1273+
# Fall through to clone below
1274+
else:
1275+
logger.info(f"Updating {repo_name} repository at {repo_path}...")
1276+
repo.remotes.origin.pull()
1277+
return repo_path
1278+
else:
1279+
logger.info(f"Updating {repo_name} repository at {repo_path}...")
1280+
repo.remotes.origin.pull()
12761281
return repo_path
12771282

1283+
# Repo does not exist (fresh or was just deleted above)
1284+
logger.info(f"Cloning {repo_name} repository to {repo_path}...")
1285+
clone_kwargs: dict[str, Any] = {"depth": 1}
1286+
1287+
if repo_config.branch:
1288+
clone_kwargs["branch"] = repo_config.branch
1289+
elif repo_config.tag:
1290+
clone_kwargs["branch"] = repo_config.tag
1291+
elif repo_config.commit:
12781292
git.Repo.clone_from(str(repo_config.url), repo_path, **clone_kwargs)
1293+
repo = git.Repo(repo_path)
1294+
repo.git.checkout(repo_config.commit)
1295+
return repo_path
12791296

1297+
git.Repo.clone_from(str(repo_config.url), repo_path, **clone_kwargs)
12801298
return repo_path
12811299
except Exception as e:
12821300
logger.warning(f"Failed to clone/update {repo_name}: {e}")
@@ -1343,7 +1361,7 @@ def _extract_docs_from_repo_sync(self, repo_path: Path, project: str, old_hashes
13431361
reference_count = sum(1 for doc in docs if doc["is_reference"])
13441362
regular_count = len(docs) - reference_count
13451363
if skipped_count:
1346-
logger.info(f" {project}: {len(docs)} changed + {skipped_count} unchanged " f"({regular_count} regular, {reference_count} reference guides)")
1364+
logger.info(f" {project}: {len(docs)} changed + {skipped_count} unchanged ({regular_count} regular, {reference_count} reference guides)")
13471365
else:
13481366
logger.info(f" {project}: {len(docs)} total documents ({regular_count} regular, {reference_count} reference guides)")
13491367
return {"docs": docs, "skipped_hashes": skipped_hashes}
@@ -1676,7 +1694,7 @@ async def index_documentation(
16761694

16771695
clone_extract_elapsed = time.monotonic() - overall_t0
16781696
await log_info(
1679-
f"Clone + extract completed in {clone_extract_elapsed:.1f}s " f"({len(all_docs)} to index, {len(all_skipped_hashes)} unchanged)",
1697+
f"Clone + extract completed in {clone_extract_elapsed:.1f}s ({len(all_docs)} to index, {len(all_skipped_hashes)} unchanged)",
16801698
ctx,
16811699
)
16821700

@@ -1702,7 +1720,7 @@ async def index_documentation(
17021720

17031721
docs_to_index = all_docs # all are new or changed (unchanged were skipped by workers)
17041722
await log_info(
1705-
f"Incremental: {len(new_docs)} new, {len(changed_docs)} changed, " f"{len(all_skipped_hashes)} unchanged, {len(deleted_doc_ids)} deleted",
1723+
f"Incremental: {len(new_docs)} new, {len(changed_docs)} changed, {len(all_skipped_hashes)} unchanged, {len(deleted_doc_ids)} deleted",
17061724
ctx,
17071725
)
17081726

src/holoviz_mcp/holoviz_mcp/server.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ async def app_lifespan(server: FastMCP):
156156
)
157157

158158

159-
@mcp.tool
159+
@mcp.tool()
160160
def get_skill(name: str) -> str:
161161
"""Get the specified skill for usage with LLMs.
162162
@@ -181,7 +181,7 @@ def get_skill(name: str) -> str:
181181
return _get_skill(name)
182182

183183

184-
@mcp.tool
184+
@mcp.tool()
185185
def list_skills() -> list[str]:
186186
"""List all available skills.
187187
@@ -195,7 +195,7 @@ def list_skills() -> list[str]:
195195
return _list_skills()
196196

197197

198-
@mcp.tool
198+
@mcp.tool()
199199
async def get_reference_guide(
200200
component: str,
201201
project: str | None = None,
@@ -237,7 +237,7 @@ async def get_reference_guide(
237237
return await indexer.search_get_reference_guide(component, project, content, ctx=ctx)
238238

239239

240-
@mcp.tool
240+
@mcp.tool()
241241
async def list_projects() -> list[str]:
242242
"""List all HoloViz and user-defined projects with indexed documentation.
243243
@@ -253,7 +253,7 @@ async def list_projects() -> list[str]:
253253
return await indexer.list_projects()
254254

255255

256-
@mcp.tool
256+
@mcp.tool()
257257
async def get_document(path: str, project: str, ctx: Context) -> Document:
258258
"""Retrieve a specific document by path and project.
259259
@@ -271,7 +271,7 @@ async def get_document(path: str, project: str, ctx: Context) -> Document:
271271
return await indexer.get_document(path, project, ctx=ctx)
272272

273273

274-
@mcp.tool
274+
@mcp.tool()
275275
async def search(
276276
query: str,
277277
project: str | None = None,
@@ -349,7 +349,7 @@ async def search(
349349
return await indexer.search(query, project, content, max_results, max_content_chars, ctx=ctx)
350350

351351

352-
@mcp.tool(enabled=False)
352+
# @mcp.tool(enabled=False)
353353
async def update_index(ctx: Context) -> str:
354354
"""Update the documentation index by re-cloning repositories and re-indexing content.
355355
@@ -380,7 +380,7 @@ async def update_index(ctx: Context) -> str:
380380
return error_msg
381381

382382

383-
@mcp.tool
383+
@mcp.tool()
384384
async def display(
385385
code: str,
386386
name: str = "",

src/holoviz_mcp/hvplot_mcp/server.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def _help(
4646
return doc, sig
4747

4848

49-
@mcp.tool
49+
@mcp.tool()
5050
async def list_plot_types(ctx: Context) -> list[str]:
5151
"""
5252
List all available hvPlot plot types supported in the current environment.
@@ -75,7 +75,7 @@ async def list_plot_types(ctx: Context) -> list[str]:
7575
return sorted(HoloViewsConverter._kind_mapping)
7676

7777

78-
@mcp.tool
78+
@mcp.tool()
7979
async def get_docstring(
8080
ctx: Context, plot_type: str, docstring: bool = True, generic: bool = True, style: Union[Literal["matplotlib", "bokeh", "plotly"], bool] = True
8181
) -> str:
@@ -111,7 +111,7 @@ async def get_docstring(
111111
return doc
112112

113113

114-
@mcp.tool
114+
@mcp.tool()
115115
async def get_signature(ctx: Context, plot_type: str, style: Union[Literal["matplotlib", "bokeh", "plotly"], bool] = True) -> str:
116116
"""
117117
Get the function signature for a specific hvPlot plot type.

src/holoviz_mcp/panel_mcp/server.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ async def _get_all_components(ctx: Context) -> list[ComponentDetails]:
126126
return COMPONENTS
127127

128128

129-
@mcp.tool
129+
@mcp.tool()
130130
async def list_packages(ctx: Context) -> list[str]:
131131
"""
132132
List all installed packages that provide Panel UI components.
@@ -158,7 +158,7 @@ async def list_packages(ctx: Context) -> list[str]:
158158
return sorted(set(component.package for component in await _get_all_components(ctx)))
159159

160160

161-
@mcp.tool
161+
@mcp.tool()
162162
async def search_components(ctx: Context, query: str, package: str | None = None, limit: int = 10) -> list[ComponentSummarySearchResult]:
163163
"""
164164
Search for Panel components by search query and optional package filter.
@@ -265,7 +265,7 @@ async def _get_component(ctx: Context, name: str | None = None, module_path: str
265265
return components_list
266266

267267

268-
@mcp.tool
268+
@mcp.tool()
269269
async def list_components(ctx: Context, name: str | None = None, module_path: str | None = None, package: str | None = None) -> list[ComponentSummary]:
270270
"""
271271
Get a summary list of Panel components without detailed docstring and parameter information.
@@ -322,7 +322,7 @@ async def list_components(ctx: Context, name: str | None = None, module_path: st
322322
return components_list
323323

324324

325-
@mcp.tool
325+
@mcp.tool()
326326
async def get_component(ctx: Context, name: str | None = None, module_path: str | None = None, package: str | None = None) -> ComponentDetails:
327327
"""
328328
Get complete details about a single Panel component including docstring and parameters.
@@ -383,7 +383,7 @@ async def get_component(ctx: Context, name: str | None = None, module_path: str
383383
return component
384384

385385

386-
@mcp.tool
386+
@mcp.tool()
387387
async def get_component_parameters(ctx: Context, name: str | None = None, module_path: str | None = None, package: str | None = None) -> dict[str, ParameterInfo]:
388388
"""
389389
Get detailed parameter information for a single Panel component.

0 commit comments

Comments
 (0)