fix: critical cloud deployment fixes for MCP stability (#317)

Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Paul Hernandez
2025-09-27 21:57:39 -05:00
committed by GitHub
parent 5da97e4820
commit 2efd8f44e2
4 changed files with 50 additions and 9 deletions
+14 -3
View File
@@ -1,5 +1,5 @@
import os
from httpx import ASGITransport, AsyncClient
from httpx import ASGITransport, AsyncClient, Timeout
from loguru import logger
from basic_memory.api.app import app as fastapi_app
@@ -14,14 +14,25 @@ def create_client() -> AsyncClient:
proxy_base_url = os.getenv("BASIC_MEMORY_PROXY_URL", None)
logger.info(f"BASIC_MEMORY_PROXY_URL: {proxy_base_url}")
# Configure timeout for longer operations like write_note
# Default httpx timeout is 5 seconds which is too short for file operations
timeout = Timeout(
connect=10.0, # 10 seconds for connection
read=30.0, # 30 seconds for reading response
write=30.0, # 30 seconds for writing request
pool=30.0, # 30 seconds for connection pool
)
if proxy_base_url:
# Use HTTP transport to proxy endpoint
logger.info(f"Creating HTTP client for proxy at: {proxy_base_url}")
return AsyncClient(base_url=proxy_base_url)
return AsyncClient(base_url=proxy_base_url, timeout=timeout)
else:
# Default: use ASGI transport for local API (development mode)
logger.debug("Creating ASGI client for local Basic Memory API")
return AsyncClient(transport=ASGITransport(app=fastapi_app), base_url="http://test")
return AsyncClient(
transport=ASGITransport(app=fastapi_app), base_url="http://test", timeout=timeout
)
# Create shared async client
+9 -3
View File
@@ -26,13 +26,19 @@ def inject_auth_header(headers: HeaderTypes | None = None) -> HeaderTypes:
headers = headers.copy()
http_headers = get_http_headers()
logger.debug(f"HTTP headers: {http_headers}")
# Log only non-sensitive header keys for debugging
if logger.opt(lazy=True).debug:
sensitive_headers = {"authorization", "cookie", "x-api-key", "x-auth-token", "api-key"}
safe_headers = {k for k in http_headers.keys() if k.lower() not in sensitive_headers}
logger.debug(f"HTTP headers present: {list(safe_headers)}")
authorization = http_headers.get("Authorization") or http_headers.get("authorization")
if authorization:
headers["Authorization"] = authorization # type: ignore
logger.debug("Injected JWT token into authorization request headers")
# Log only that auth was injected, not the token value
logger.debug("Injected authorization header into request")
else:
logger.debug("No authorization found in request headers")
logger.debug("No authorization header found in request")
return headers
+4 -2
View File
@@ -130,7 +130,8 @@ async def read_note(
query=identifier, search_type="title", project=project, context=context
)
if title_results and title_results.results:
# Handle both SearchResponse object and error strings
if title_results and hasattr(title_results, "results") and title_results.results:
result = title_results.results[0] # Get the first/best match
if result.permalink:
try:
@@ -159,7 +160,8 @@ async def read_note(
)
# We didn't find a direct match, construct a helpful error message
if not text_results or not text_results.results:
# Handle both SearchResponse object and error strings
if not text_results or not hasattr(text_results, "results") or not text_results.results:
# No results at all
return format_not_found_message(active_project.name, identifier)
else: