Skip to content

Commit 80a2545

Browse files
fix: auto-build symbol index for hosted scanners
1 parent 8dc400d commit 80a2545

1 file changed

Lines changed: 54 additions & 2 deletions

File tree

src/mcp_server_python_docs/server.py

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
import asyncio
1010
import importlib.resources
1111
import logging
12+
import os
1213
import sqlite3
14+
import subprocess
1315
import sys
1416
import traceback
1517
from collections.abc import AsyncIterator
@@ -61,6 +63,55 @@ def _assert_fts5(conn: sqlite3.Connection) -> None:
6163
assert_fts5_available(conn)
6264

6365

66+
def _auto_build_symbol_index(index_path) -> bool:
67+
"""Build a small symbol-only index when the server starts without one.
68+
69+
This keeps hosted scanners/directories such as Glama from failing MCP
70+
introspection on a fresh container while preserving the documented full
71+
corpus command for normal users. Set PYTHON_DOCS_MCP_DISABLE_AUTO_INDEX=1
72+
to require an explicitly prebuilt index.
73+
"""
74+
if os.environ.get("PYTHON_DOCS_MCP_DISABLE_AUTO_INDEX") == "1":
75+
return False
76+
77+
versions = os.environ.get("PYTHON_DOCS_MCP_AUTO_INDEX_VERSIONS", "3.13")
78+
logger.warning(
79+
"No index found at %s; auto-building symbol-only Python docs index for %s",
80+
index_path,
81+
versions,
82+
)
83+
try:
84+
result = subprocess.run(
85+
[
86+
sys.executable,
87+
"-m",
88+
"mcp_server_python_docs",
89+
"build-index",
90+
"--versions",
91+
versions,
92+
"--skip-content",
93+
],
94+
check=False,
95+
capture_output=True,
96+
text=True,
97+
timeout=180,
98+
)
99+
except (OSError, subprocess.TimeoutExpired) as e:
100+
logger.error("Auto index build failed to start or timed out: %s", e)
101+
return False
102+
103+
if result.returncode != 0:
104+
logger.error(
105+
"Auto index build failed with exit code %s:\n%s",
106+
result.returncode,
107+
(result.stderr or result.stdout)[-4000:],
108+
)
109+
return False
110+
111+
logger.info("Auto index build complete at %s", index_path)
112+
return index_path.exists()
113+
114+
64115
@asynccontextmanager
65116
async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]:
66117
"""Manage application lifecycle with typed context (SRVR-01).
@@ -72,8 +123,9 @@ async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]:
72123
cache_dir = get_cache_dir()
73124
index_path = get_index_path()
74125

75-
# Fail fast on missing index (SRVR-10)
76-
if not index_path.exists():
126+
# Fail fast on missing index unless a small symbol-only auto-index succeeds
127+
# (SRVR-10 plus hosted-directory scanner compatibility).
128+
if not index_path.exists() and not _auto_build_symbol_index(index_path):
77129
from mcp_server_python_docs.ingestion.cpython_versions import (
78130
SUPPORTED_DOC_VERSIONS_CSV,
79131
)

0 commit comments

Comments
 (0)