2828 HINDSIGHT_API_LLM_PROVIDER: Optional. LLM provider (default: "openai").
2929 HINDSIGHT_API_LLM_MODEL: Optional. LLM model (default: "gpt-4o-mini").
3030 HINDSIGHT_API_MCP_LOCAL_BANK_ID: Optional. Memory bank ID (default: "mcp").
31- HINDSIGHT_API_LOG_LEVEL: Optional. Log level (default: "info").
31+ HINDSIGHT_API_LOG_LEVEL: Optional. Log level (default: "warning").
32+ HINDSIGHT_API_MCP_INSTRUCTIONS: Optional. Additional instructions appended to both retain and recall tools.
33+
34+ Example custom instructions (these are ADDED to the default behavior):
35+ To also store assistant actions:
36+ HINDSIGHT_API_MCP_INSTRUCTIONS="Also store every action you take, including tool calls, code written, and decisions made."
37+
38+ To also store conversation summaries:
39+ HINDSIGHT_API_MCP_INSTRUCTIONS="Also store summaries of important conversations and their outcomes."
3240"""
3341
3442import logging
3543import os
3644import sys
3745
3846from mcp .server .fastmcp import FastMCP
47+ from mcp .types import Icon
3948
4049from hindsight_api .config import (
4150 DEFAULT_MCP_LOCAL_BANK_ID ,
51+ DEFAULT_MCP_RECALL_DESCRIPTION ,
52+ DEFAULT_MCP_RETAIN_DESCRIPTION ,
53+ ENV_MCP_INSTRUCTIONS ,
4254 ENV_MCP_LOCAL_BANK_ID ,
4355)
4456
45- # Configure logging - default to info
46- _log_level_str = os .environ .get ("HINDSIGHT_API_LOG_LEVEL" , "info" ).lower ()
57+ # Configure logging - default to warning to avoid polluting stderr during MCP init
58+ # MCP clients interpret stderr output as errors, so we suppress INFO logs by default
59+ _log_level_str = os .environ .get ("HINDSIGHT_API_LOG_LEVEL" , "warning" ).lower ()
4760_log_level_map = {
4861 "critical" : logging .CRITICAL ,
4962 "error" : logging .ERROR ,
@@ -79,22 +92,21 @@ def create_local_mcp_server(bank_id: str, memory=None) -> FastMCP:
7992 if memory is None :
8093 memory = MemoryEngine (db_url = "pg0://hindsight-mcp" )
8194
95+ # Get custom instructions from environment variable (appended to both tools)
96+ extra_instructions = os .environ .get (ENV_MCP_INSTRUCTIONS , "" )
97+
98+ retain_description = DEFAULT_MCP_RETAIN_DESCRIPTION
99+ recall_description = DEFAULT_MCP_RECALL_DESCRIPTION
100+
101+ if extra_instructions :
102+ retain_description = f"{ DEFAULT_MCP_RETAIN_DESCRIPTION } \n \n Additional instructions: { extra_instructions } "
103+ recall_description = f"{ DEFAULT_MCP_RECALL_DESCRIPTION } \n \n Additional instructions: { extra_instructions } "
104+
82105 mcp = FastMCP ("hindsight" )
83106
84- @mcp .tool ()
107+ @mcp .tool (description = retain_description )
85108 async def retain (content : str , context : str = "general" ) -> dict :
86109 """
87- Store important information to long-term memory.
88-
89- Use this tool PROACTIVELY whenever the user shares:
90- - Personal facts, preferences, or interests
91- - Important events or milestones
92- - User history, experiences, or background
93- - Decisions, opinions, or stated preferences
94- - Goals, plans, or future intentions
95- - Relationships or people mentioned
96- - Work context, projects, or responsibilities
97-
98110 Args:
99111 content: The fact/memory to store (be specific and include relevant details)
100112 context: Category for the memory (e.g., 'preferences', 'work', 'hobbies', 'family'). Default: 'general'
@@ -111,17 +123,9 @@ async def _retain():
111123 asyncio .create_task (_retain ())
112124 return {"status" : "accepted" , "message" : "Memory storage initiated" }
113125
114- @mcp .tool ()
126+ @mcp .tool (description = recall_description )
115127 async def recall (query : str , max_tokens : int = 4096 , budget : str = "low" ) -> dict :
116128 """
117- Search memories to provide personalized, context-aware responses.
118-
119- Use this tool PROACTIVELY to:
120- - Check user's preferences before making suggestions
121- - Recall user's history to provide continuity
122- - Remember user's goals and context
123- - Personalize responses based on past interactions
124-
125129 Args:
126130 query: Natural language search query (e.g., "user's food preferences", "what projects is user working on")
127131 max_tokens: Maximum tokens to return in results (default: 4096)
@@ -153,10 +157,9 @@ async def _initialize_and_run(bank_id: str):
153157 from hindsight_api import MemoryEngine
154158
155159 # Create and initialize memory engine with pg0 embedded database
156- print ( "Initializing memory engine..." , file = sys . stderr )
160+ # Note: We avoid printing to stderr during init as MCP clients show it as "errors"
157161 memory = MemoryEngine (db_url = "pg0://hindsight-mcp" )
158162 await memory .initialize ()
159- print ("Memory engine initialized." , file = sys .stderr )
160163
161164 # Create and run the server
162165 mcp = create_local_mcp_server (bank_id , memory = memory )
@@ -179,8 +182,8 @@ def main():
179182 # Get bank ID from environment, default to "mcp"
180183 bank_id = os .environ .get (ENV_MCP_LOCAL_BANK_ID , DEFAULT_MCP_LOCAL_BANK_ID )
181184
182- # Print startup message to stderr (stdout is reserved for MCP protocol)
183- print ( f"Hindsight MCP server starting (bank_id= { bank_id } )..." , file = sys . stderr )
185+ # Note: We don't print to stderr as MCP clients display it as "error output"
186+ # Use HINDSIGHT_API_LOG_LEVEL=debug for verbose startup logging
184187
185188 # Run the async initialization and server
186189 asyncio .run (_initialize_and_run (bank_id ))
0 commit comments