mirror of
https://github.com/basicmachines-co/basic-memory
synced 2026-06-21 13:47:35 +00:00
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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user