99import asyncio
1010import importlib .resources
1111import logging
12+ import os
1213import sqlite3
14+ import subprocess
1315import sys
1416import traceback
1517from 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
65116async 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