66"""
77
88import logging
9+ import os
10+ import platform
11+ import sys
12+ from pathlib import Path
913from typing import Annotated , Any
1014
1115from pydantic import Field
1216
1317from ha_mcp import __version__
1418
15- from ..utils .usage_logger import AVG_LOG_ENTRIES_PER_TOOL , get_recent_logs
19+ from ..utils .usage_logger import AVG_LOG_ENTRIES_PER_TOOL , get_recent_logs , get_startup_logs
1620from .helpers import log_tool_usage
1721
1822logger = logging .getLogger (__name__ )
1923
2024
25+ def _detect_installation_method () -> str :
26+ """
27+ Detect how ha-mcp was installed.
28+
29+ Returns one of: pyinstaller, addon, docker, git, pypi, unknown
30+ """
31+ # 1. PyInstaller binary
32+ if getattr (sys , "frozen" , False ):
33+ return "pyinstaller"
34+
35+ # 2. Home Assistant Add-on (has supervisor token)
36+ if os .environ .get ("SUPERVISOR_TOKEN" ):
37+ return "addon"
38+
39+ # 3. Docker container (non-addon)
40+ if Path ("/.dockerenv" ).exists ():
41+ return "docker"
42+
43+ # 4. Git clone - check for .git directory relative to package
44+ try :
45+ # Go up from tools_bug_report.py -> tools -> ha_mcp -> src -> project_root
46+ project_root = Path (__file__ ).parent .parent .parent .parent
47+ if (project_root / ".git" ).exists ():
48+ return "git"
49+ except Exception :
50+ pass
51+
52+ # 5. PyPI install - marker file exists in package
53+ try :
54+ marker_path = Path (__file__ ).parent .parent / "_pypi_marker"
55+ if marker_path .exists ():
56+ return "pypi"
57+ except Exception :
58+ pass
59+
60+ # 6. Default - unknown
61+ return "unknown"
62+
63+
64+ def _detect_platform () -> dict [str , str ]:
65+ """Detect platform information."""
66+ return {
67+ "os" : platform .system (), # Windows, Darwin, Linux
68+ "os_release" : platform .release (),
69+ "os_version" : platform .version (),
70+ "architecture" : platform .machine (),
71+ "python_version" : platform .python_version (),
72+ }
73+
74+
2175def register_bug_report_tools (mcp : Any , client : Any , ** kwargs : Any ) -> None :
2276 """Register bug report tools with the MCP server."""
2377
@@ -49,31 +103,33 @@ async def ha_bug_report(
49103 """
50104 Collect diagnostic information for filing bug reports against ha-mcp.
51105
106+ **IMPORTANT FOR AI AGENTS:**
107+ When creating a bug report, you MUST only report FACTS that you directly
108+ observed during the conversation. Do NOT make assumptions or guesses.
109+ - Report exact error messages you received
110+ - Report exact tool names and parameters you used
111+ - Report exact responses from tools
112+ - Do NOT speculate about causes or solutions
113+ - Do NOT fill in template sections you cannot answer from the conversation
114+
52115 **WHEN TO USE THIS TOOL:**
53116 Use this tool when the user says something like:
54117 - "I want to file a bug for: <reason>"
55118 - "This isn't working, I need to report this"
56119 - "How do I report this issue?"
57120
58- **BEFORE CALLING THIS TOOL:**
59- If the bug details are unclear, guide the user to provide:
60- 1. What they were trying to do (the goal)
61- 2. What actually happened (the result)
62- 3. What they expected to happen instead
63- 4. Any error messages they saw
64- 5. Steps to reproduce the issue
65-
66- **PARAMETERS:**
67- - tool_call_count: Count how many ha_* tools you called since the bug started.
68- This helps include the right amount of logs. Default is 3.
69-
70121 **OUTPUT:**
71- Returns diagnostic info, recent logs, and a bug report template.
72- The template guides privacy-conscious reporting while preserving
73- enough detail to diagnose the issue.
122+ Returns diagnostic info (auto-populated), recent logs, startup logs,
123+ and a bug report template. All environment info is automatically filled.
74124 """
125+ # Detect installation method and platform
126+ install_method = _detect_installation_method ()
127+ platform_info = _detect_platform ()
128+
75129 diagnostic_info : dict [str , Any ] = {
76130 "ha_mcp_version" : __version__ ,
131+ "installation_method" : install_method ,
132+ "platform" : platform_info ,
77133 "connection_status" : "Unknown" ,
78134 "home_assistant_version" : "Unknown" ,
79135 "entity_count" : 0 ,
@@ -101,19 +157,26 @@ async def ha_bug_report(
101157 logger .warning (f"Failed to get entity count: { e } " )
102158
103159 # Calculate how many log entries to retrieve
104- # Formula: AVG_LOG_ENTRIES_PER_TOOL * 2 * tool_call_count
105- max_log_entries = AVG_LOG_ENTRIES_PER_TOOL * 2 * tool_call_count
160+ # Formula: AVG_LOG_ENTRIES_PER_TOOL * 4 * tool_call_count (doubled from 2x to 4x)
161+ max_log_entries = AVG_LOG_ENTRIES_PER_TOOL * 4 * tool_call_count
106162 recent_logs = get_recent_logs (max_entries = max_log_entries )
107163
164+ # Get startup logs (first minute of server operation)
165+ startup_logs = get_startup_logs ()
166+
108167 # Format logs for inclusion (sanitized summary)
109168 log_summary = _format_logs_for_report (recent_logs )
169+ startup_log_summary = _format_startup_logs (startup_logs )
110170
111171 # Build the formatted report
112172 report_lines = [
113173 "=== ha-mcp Bug Report Info ===" ,
114174 "" ,
115- f"Home Assistant Version: { diagnostic_info ['home_assistant_version' ]} " ,
116175 f"ha-mcp Version: { diagnostic_info ['ha_mcp_version' ]} " ,
176+ f"Installation Method: { diagnostic_info ['installation_method' ]} " ,
177+ f"Platform: { platform_info ['os' ]} { platform_info ['os_release' ]} ({ platform_info ['architecture' ]} )" ,
178+ f"Python Version: { platform_info ['python_version' ]} " ,
179+ f"Home Assistant Version: { diagnostic_info ['home_assistant_version' ]} " ,
117180 f"Connection Status: { diagnostic_info ['connection_status' ]} " ,
118181 f"Entity Count: { diagnostic_info ['entity_count' ]} " ,
119182 ]
@@ -124,6 +187,13 @@ async def ha_bug_report(
124187 if "time_zone" in diagnostic_info :
125188 report_lines .append (f"Time Zone: { diagnostic_info ['time_zone' ]} " )
126189
190+ if startup_logs :
191+ report_lines .extend ([
192+ "" ,
193+ f"=== Startup Logs ({ len (startup_logs )} entries) ===" ,
194+ startup_log_summary ,
195+ ])
196+
127197 if recent_logs :
128198 report_lines .extend ([
129199 "" ,
@@ -135,7 +205,7 @@ async def ha_bug_report(
135205
136206 # Bug report template for the AI to present to the user
137207 bug_report_template = _generate_bug_report_template (
138- diagnostic_info , log_summary
208+ diagnostic_info , log_summary , startup_log_summary
139209 )
140210
141211 # Anonymization instructions
@@ -145,14 +215,17 @@ async def ha_bug_report(
145215 "success" : True ,
146216 "diagnostic_info" : diagnostic_info ,
147217 "recent_logs" : recent_logs ,
218+ "startup_logs" : startup_logs ,
148219 "log_count" : len (recent_logs ),
220+ "startup_log_count" : len (startup_logs ),
149221 "formatted_report" : formatted_report ,
150222 "bug_report_template" : bug_report_template ,
151223 "anonymization_guide" : anonymization_guide ,
152224 "issue_url" : "https://github.com/homeassistant-ai/ha-mcp/issues/new" ,
153225 "instructions" : (
154226 "Present the bug_report_template to the user. "
155- "Ask them to review and fill in the [DESCRIBE...] sections. "
227+ "The Environment section is already filled with accurate data. "
228+ "Ask the user to describe the bug, what happened, and what they expected. "
156229 "Remind them to follow the anonymization_guide to protect their privacy. "
157230 "The user should copy the completed template and submit it at the issue_url."
158231 ),
@@ -182,56 +255,96 @@ def _format_logs_for_report(logs: list[dict[str, Any]]) -> str:
182255 return "\n " .join (lines )
183256
184257
258+ def _format_startup_logs (logs : list [dict [str , Any ]]) -> str :
259+ """Format startup log entries for inclusion in a bug report."""
260+ if not logs :
261+ return "(No startup logs available)"
262+
263+ lines = []
264+ for log in logs :
265+ elapsed = log .get ("elapsed_seconds" , 0 )
266+ level = log .get ("level" , "INFO" )
267+ logger_name = log .get ("logger" , "" )
268+ message = log .get ("message" , "" )
269+
270+ # Truncate long messages
271+ if len (message ) > 200 :
272+ message = message [:200 ] + "..."
273+
274+ line = f" +{ elapsed :05.2f} s | { level :5} | { logger_name } : { message } "
275+ lines .append (line )
276+
277+ return "\n " .join (lines )
278+
279+
185280def _generate_bug_report_template (
186- diagnostic_info : dict [str , Any ], log_summary : str
281+ diagnostic_info : dict [str , Any ],
282+ log_summary : str ,
283+ startup_log_summary : str ,
187284) -> str :
188- """Generate a bug report template for users to fill out."""
285+ """Generate a bug report template with auto-populated environment info."""
286+ platform_info = diagnostic_info .get ("platform" , {})
287+
189288 return f"""## Bug Report Template
190289
191- **Copy this template, fill in the sections, and submit at:**
290+ **Copy this template, fill in the bug description sections, and submit at:**
192291https://github.com/homeassistant-ai/ha-mcp/issues/new
193292
194293---
195294
196- ### I want to file a bug for: [DESCRIBE THE BUG IN ONE SENTENCE]
295+ ### Bug Summary
296+ <!-- Describe the bug in one sentence -->
197297
198- ### What I was trying to do
199- [DESCRIBE YOUR GOAL - What were you asking the AI to do?]
200298
201299### What happened
202- [DESCRIBE THE RESULT - What did the AI do or say?]
300+ <!-- Describe what the AI did or what error occurred -->
301+
302+
303+ ### What you expected
304+ <!-- Describe what should have happened instead -->
203305
204- ### What I expected
205- [DESCRIBE EXPECTED BEHAVIOR - What should have happened instead?]
206306
207307### Steps to reproduce
208- 1. [First step]
209- 2. [Second step]
210- 3. [...]
308+ <!-- If you can reproduce the issue, list the steps -->
309+ 1.
310+ 2.
311+ 3.
211312
212313### Error messages (if any)
213314```
214- [PASTE ANY ERROR MESSAGES HERE]
315+ <!-- Paste any error messages here -->
215316```
216317
217- ### Environment
218- - Home Assistant Version: { diagnostic_info .get ('home_assistant_version' , 'Unknown' )}
219- - ha-mcp Version: { diagnostic_info .get ('ha_mcp_version' , 'Unknown' )}
220- - Connection Status: { diagnostic_info .get ('connection_status' , 'Unknown' )}
221- - Entity Count: { diagnostic_info .get ('entity_count' , 0 )}
222- - Time Zone: { diagnostic_info .get ('time_zone' , 'Unknown' )}
318+ ### Environment (auto-populated)
319+ - **ha-mcp Version:** { diagnostic_info .get ('ha_mcp_version' , 'Unknown' )}
320+ - **Installation Method:** { diagnostic_info .get ('installation_method' , 'Unknown' )}
321+ - **Platform:** { platform_info .get ('os' , 'Unknown' )} { platform_info .get ('os_release' , '' )} ({ platform_info .get ('architecture' , 'Unknown' )} )
322+ - **Python Version:** { platform_info .get ('python_version' , 'Unknown' )}
323+ - **Home Assistant Version:** { diagnostic_info .get ('home_assistant_version' , 'Unknown' )}
324+ - **Connection Status:** { diagnostic_info .get ('connection_status' , 'Unknown' )}
325+ - **Entity Count:** { diagnostic_info .get ('entity_count' , 0 )}
326+ - **Time Zone:** { diagnostic_info .get ('time_zone' , 'Unknown' )}
327+
328+ ### Startup logs
329+ <details>
330+ <summary>Click to expand startup logs</summary>
331+
332+ ```
333+ { startup_log_summary }
334+ ```
335+ </details>
223336
224337### Recent tool calls
338+ <details>
339+ <summary>Click to expand recent tool calls</summary>
340+
225341```
226342{ log_summary }
227343```
228-
229- ### Additional context
230- [ADD ANY OTHER RELEVANT INFORMATION]
344+ </details>
231345
232346---
233347**Privacy note:** Please review and anonymize any sensitive information before submitting.
234- See the anonymization guide in the tool output for details.
235348"""
236349
237350
0 commit comments