Skip to content

Commit 8e1f137

Browse files
Peterclaude
andcommitted
fix: resolve variable shadowing bug causing all crates to return 'unwind' as name
The get_crate_summary function had a critical variable shadowing issue where the crate name was being overwritten by module names in a loop. The last module processed (often 'unwind') would replace the actual crate name. Changes: - Rename loop variable from 'name' to 'module_name' to prevent shadowing - Remove unnecessary WHERE crate_id = 1 clauses (each DB has single crate) - Update mcp_server.py tool names to snake_case for consistency - Document bug and solution in UsefulInformation.json The bug only affected MCP mode and was causing incorrect crate names in API responses despite correct data in the database. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent f4138e9 commit 8e1f137

File tree

3 files changed

+63
-15
lines changed

3 files changed

+63
-15
lines changed

UsefulInformation.json

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"projectName": "docsrs-mcp",
3-
"lastUpdated": "2025-08-16",
3+
"lastUpdated": "2025-08-18",
44
"purpose": "Track errors, solutions, and lessons learned during development",
55
"categories": {
66
"errorSolutions": {
@@ -542,6 +542,30 @@
542542
"relatedFiles": ["src/docsrs_mcp/mcp_tools.py"],
543543
"codeExample": "# Standard pattern for all tools:\nasync def tool_function(crate_name: str, ...):\n # Always attempt ingestion first\n db_path = await ingest_crate(crate_name, session)\n if not db_path:\n raise ValueError(f'Failed to ingest crate: {crate_name}')\n \n # Proceed with tool logic\n return process_data(db_path, ...)",
544544
"debuggingTechnique": "Review all tool implementations to verify consistent auto-ingestion pattern usage"
545+
},
546+
{
547+
"error": "Variable Shadowing Bug - All Crates Returning 'unwind' as Name",
548+
"rootCause": "Variable shadowing in get_crate_summary function at line 1103 of app.py. The crate name variable was being overwritten by module names in a loop processing crate modules. The last module processed ('unwind') would replace the actual crate name.",
549+
"solution": "Rename loop variable from 'name' to 'module_name' to prevent shadowing the crate name variable when unpacking tuples in the module processing loop.",
550+
"context": "All crates returning 'unwind' as their name in MCP mode, breaking crate identification and search functionality",
551+
"implementation": [
552+
"Changed loop variable from 'name' to 'module_name' in tuple unpacking",
553+
"Preserved original crate name variable throughout function execution",
554+
"Fixed variable scope isolation to prevent unintended overwrites"
555+
],
556+
"lesson": "Always check for variable shadowing in loops, especially when unpacking tuples. Loop variables can accidentally overwrite outer scope variables if named identically.",
557+
"pattern": "Use descriptive, scope-specific variable names in loops to avoid shadowing: use 'module_name' instead of 'name' when processing modules",
558+
"dateEncountered": "2025-08-18",
559+
"relatedFiles": ["src/docsrs_mcp/app.py"],
560+
"affectedLines": ["app.py:1103"],
561+
"codeExample": "# Before (variable shadowing):\nfor name, path in modules:\n # 'name' overwrites the crate name variable\n \n# After (no shadowing):\nfor module_name, path in modules:\n # 'module_name' doesn't conflict with crate name",
562+
"debuggingTechnique": "Test with multiple different crates to verify each returns its correct name rather than the last processed module name",
563+
"additionalNotes": [
564+
"The hardcoded crate_id = 1 was also fixed but was NOT the cause of this specific bug",
565+
"Old cached databases may need to be deleted and re-ingested if they contain corrupted data from previous buggy runs",
566+
"This bug only affected MCP mode because the variable shadowing occurred in the get_crate_summary function"
567+
],
568+
"warningFlags": "Variable shadowing, loop variable naming, tuple unpacking scope issues"
545569
}
546570
]
547571
},
@@ -1741,6 +1765,31 @@
17411765
"debuggingTechnique": "Check ingestion counts in database before running comparisons to identify incomplete data",
17421766
"implementationDetails": "Added MIN_ITEMS_THRESHOLD check in version_diff.py using existing validation utilities from validation.py to provide actionable error messages explaining the issue",
17431767
"impact": "Prevents misleading '0 changes' results and provides clear error messages when comparison data is insufficient"
1768+
},
1769+
{
1770+
"error": "getCrateSummary returning 'unwind' as name for all crates in MCP mode",
1771+
"context": "All crates return 'unwind' as the name field in getCrateSummary responses when using MCP mode, despite database containing correct crate names",
1772+
"investigation": {
1773+
"suspectedCause": "tools_to_fix dictionary in FastMCP schema override using camelCase operation_ids instead of snake_case tool names",
1774+
"attemptedFix": "Changed tool names from camelCase (getCrateSummary) to snake_case (get_crate_summary) in tools_to_fix dictionary",
1775+
"result": "Bug persists after schema override fix - root cause appears deeper than initially suspected"
1776+
},
1777+
"currentStatus": "Unresolved - database contains correct data, issue appears to be in response generation or serialization layer",
1778+
"debuggingFindings": [
1779+
"Database queries return correct crate names when tested directly",
1780+
"Issue specific to MCP mode - REST mode returns correct names",
1781+
"Schema override modification did not resolve the issue",
1782+
"Problem likely in FastMCP response serialization or field mapping"
1783+
],
1784+
"nextSteps": [
1785+
"Investigate FastMCP response serialization logic",
1786+
"Check field mapping between database results and MCP responses",
1787+
"Test with simplified response models to isolate the issue",
1788+
"Compare MCP vs REST response generation pathways"
1789+
],
1790+
"dateEncountered": "2025-08-16",
1791+
"relatedFiles": ["src/docsrs_mcp/mcp_server.py", "src/docsrs_mcp/mcp_tools.py", "src/docsrs_mcp/models.py"],
1792+
"impact": "All MCP clients receive incorrect crate names, affecting user experience and tool reliability"
17441793
}
17451794
]
17461795
},

src/docsrs_mcp/app.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,8 +1080,7 @@ async def get_crate_summary(request: Request, params: GetCrateSummaryRequest):
10801080
"""
10811081
SELECT name, path, parent_id, depth, item_count
10821082
FROM modules
1083-
WHERE crate_id = 1
1084-
AND depth <= 3
1083+
WHERE depth <= 3
10851084
AND (item_count >= 2 OR depth = 0)
10861085
AND path NOT LIKE '%target::%'
10871086
AND path NOT LIKE '%.cargo::%'
@@ -1101,10 +1100,10 @@ async def get_crate_summary(request: Request, params: GetCrateSummaryRequest):
11011100
# Additional filtering in Python for more complex patterns
11021101
filtered_modules = []
11031102
for row in all_rows:
1104-
name, path, parent_id, depth, item_count = row
1103+
module_name, path, parent_id, depth, item_count = row
11051104

11061105
# Skip internal/private modules (often start with underscore)
1107-
if name.startswith("_") and depth > 0:
1106+
if module_name.startswith("_") and depth > 0:
11081107
continue
11091108

11101109
# Skip generated modules
@@ -1117,7 +1116,7 @@ async def get_crate_summary(request: Request, params: GetCrateSummaryRequest):
11171116
# Include the module
11181117
filtered_modules.append(
11191118
CrateModule(
1120-
name=name,
1119+
name=module_name,
11211120
path=path,
11221121
parent_id=parent_id,
11231122
depth=depth,
@@ -1132,8 +1131,7 @@ async def get_crate_summary(request: Request, params: GetCrateSummaryRequest):
11321131
"""
11331132
SELECT name, path, parent_id, depth, item_count
11341133
FROM modules
1135-
WHERE crate_id = 1
1136-
AND (depth <= 1 OR item_count >= 5)
1134+
WHERE (depth <= 1 OR item_count >= 5)
11371135
AND path NOT LIKE '%target::%'
11381136
AND path NOT LIKE '%.cargo::%'
11391137
ORDER BY depth ASC, item_count DESC

src/docsrs_mcp/mcp_server.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,32 +75,33 @@ async def override_fastmcp_schemas():
7575
# Each tool maps to parameters that need type override and their original types
7676
# We track original types for logging/debugging purposes
7777
tools_to_fix = {
78-
"searchItems": {
78+
"search_items": {
7979
"k": "integer", # Number of results
8080
"min_doc_length": "integer", # Min doc length filter
8181
"has_examples": "boolean", # Filter for examples
8282
"deprecated": "boolean", # Deprecation filter
8383
},
84-
"startPreIngestion": {
84+
"start_pre_ingestion": {
8585
"count": "integer", # Number of crates to ingest
8686
"concurrency": "integer", # Parallel workers
8787
"force": "boolean", # Force restart flag
8888
},
89-
"ingestCargoFile": {
89+
"ingest_cargo_file": {
9090
"concurrency": "integer", # Parallel workers
9191
"skip_existing": "boolean", # Skip already ingested
9292
"resolve_versions": "boolean", # Resolve version specs
9393
},
94-
"compareVersions": {
94+
"compare_versions": {
9595
"include_unchanged": "boolean", # Include unchanged items
9696
"max_results": "integer", # Max changes to return
9797
},
98-
"searchExamples": {
98+
"get_crate_summary": {}, # Only has string parameters
99+
"search_examples": {
99100
"k": "integer" # Number of examples
100101
},
101102
# These tools have no problematic parameters but are listed for completeness
102-
"listVersions": {}, # Only has string parameters
103-
"controlPreIngestion": {}, # Only has enum parameters
103+
"list_versions": {}, # Only has string parameters
104+
"control_pre_ingestion": {}, # Only has enum parameters
104105
}
105106

106107
# Iterate through tools and modify their schemas

0 commit comments

Comments
 (0)