Skip to content

Commit ea8163c

Browse files
authored
fix(mcp): unify hindsight-mcp-local and server mcp (#407)
* fix(mcp): stateless param not supported anymore * fixes * fix: npx hindsight-control-plane fails
1 parent ac73948 commit ea8163c

5 files changed

Lines changed: 104 additions & 486 deletions

File tree

.github/workflows/test.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ jobs:
174174
HINDSIGHT_API_LLM_PROVIDER: groq
175175
HINDSIGHT_API_LLM_API_KEY: ${{ secrets.GROQ_API_KEY }}
176176
HINDSIGHT_API_LLM_MODEL: openai/gpt-oss-20b
177+
HINDSIGHT_API_LLM_GROQ_SERVICE_TIER: flex
177178
HINDSIGHT_API_URL: http://localhost:8888
178179
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
179180
UV_INDEX: pytorch=https://download.pytorch.org/whl/cpu
@@ -233,6 +234,7 @@ jobs:
233234
HINDSIGHT_API_LLM_PROVIDER=${{ env.HINDSIGHT_API_LLM_PROVIDER }}
234235
HINDSIGHT_API_LLM_API_KEY=${{ env.HINDSIGHT_API_LLM_API_KEY }}
235236
HINDSIGHT_API_LLM_MODEL=${{ env.HINDSIGHT_API_LLM_MODEL }}
237+
HINDSIGHT_API_LLM_GROQ_SERVICE_TIER=${{ env.HINDSIGHT_API_LLM_GROQ_SERVICE_TIER }}
236238
EOF
237239
238240
- name: Start API server
@@ -361,6 +363,7 @@ jobs:
361363
COHERE_API_KEY: ${{ secrets.COHERE_API_KEY }}
362364
HINDSIGHT_API_EMBEDDINGS_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
363365
HINDSIGHT_API_LLM_MODEL: openai/gpt-oss-20b
366+
HINDSIGHT_API_LLM_GROQ_SERVICE_TIER: flex
364367
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
365368
# Prefer CPU-only PyTorch in CI (but keep PyPI for everything else)
366369
UV_INDEX: pytorch=https://download.pytorch.org/whl/cpu
@@ -417,6 +420,7 @@ jobs:
417420
HINDSIGHT_API_LLM_PROVIDER: groq
418421
HINDSIGHT_API_LLM_API_KEY: ${{ secrets.GROQ_API_KEY }}
419422
HINDSIGHT_API_LLM_MODEL: openai/gpt-oss-20b
423+
HINDSIGHT_API_LLM_GROQ_SERVICE_TIER: flex
420424
HINDSIGHT_API_URL: http://localhost:8888
421425
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
422426
# Prefer CPU-only PyTorch in CI (but keep PyPI for everything else)
@@ -458,6 +462,7 @@ jobs:
458462
HINDSIGHT_API_LLM_PROVIDER=${{ env.HINDSIGHT_API_LLM_PROVIDER }}
459463
HINDSIGHT_API_LLM_API_KEY=${{ env.HINDSIGHT_API_LLM_API_KEY }}
460464
HINDSIGHT_API_LLM_MODEL=${{ env.HINDSIGHT_API_LLM_MODEL }}
465+
HINDSIGHT_API_LLM_GROQ_SERVICE_TIER=${{ env.HINDSIGHT_API_LLM_GROQ_SERVICE_TIER }}
461466
EOF
462467
463468
- name: Start API server
@@ -493,6 +498,7 @@ jobs:
493498
HINDSIGHT_API_LLM_PROVIDER: groq
494499
HINDSIGHT_API_LLM_API_KEY: ${{ secrets.GROQ_API_KEY }}
495500
HINDSIGHT_API_LLM_MODEL: openai/gpt-oss-20b
501+
HINDSIGHT_API_LLM_GROQ_SERVICE_TIER: flex
496502
HINDSIGHT_API_URL: http://localhost:8888
497503
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
498504
# Prefer CPU-only PyTorch in CI (but keep PyPI for everything else)
@@ -539,6 +545,7 @@ jobs:
539545
HINDSIGHT_API_LLM_PROVIDER=${{ env.HINDSIGHT_API_LLM_PROVIDER }}
540546
HINDSIGHT_API_LLM_API_KEY=${{ env.HINDSIGHT_API_LLM_API_KEY }}
541547
HINDSIGHT_API_LLM_MODEL=${{ env.HINDSIGHT_API_LLM_MODEL }}
548+
HINDSIGHT_API_LLM_GROQ_SERVICE_TIER=${{ env.HINDSIGHT_API_LLM_GROQ_SERVICE_TIER }}
542549
EOF
543550
544551
- name: Start API server
@@ -574,6 +581,7 @@ jobs:
574581
HINDSIGHT_API_LLM_PROVIDER: groq
575582
HINDSIGHT_API_LLM_API_KEY: ${{ secrets.GROQ_API_KEY }}
576583
HINDSIGHT_API_LLM_MODEL: openai/gpt-oss-20b
584+
HINDSIGHT_API_LLM_GROQ_SERVICE_TIER: flex
577585
HINDSIGHT_API_URL: http://localhost:8888
578586
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
579587
# Prefer CPU-only PyTorch in CI (but keep PyPI for everything else)
@@ -619,6 +627,7 @@ jobs:
619627
HINDSIGHT_API_LLM_PROVIDER=${{ env.HINDSIGHT_API_LLM_PROVIDER }}
620628
HINDSIGHT_API_LLM_API_KEY=${{ env.HINDSIGHT_API_LLM_API_KEY }}
621629
HINDSIGHT_API_LLM_MODEL=${{ env.HINDSIGHT_API_LLM_MODEL }}
630+
HINDSIGHT_API_LLM_GROQ_SERVICE_TIER=${{ env.HINDSIGHT_API_LLM_GROQ_SERVICE_TIER }}
622631
EOF
623632
624633
- name: Start API server
@@ -654,6 +663,7 @@ jobs:
654663
HINDSIGHT_API_LLM_PROVIDER: groq
655664
HINDSIGHT_API_LLM_API_KEY: ${{ secrets.GROQ_API_KEY }}
656665
HINDSIGHT_API_LLM_MODEL: openai/gpt-oss-20b
666+
HINDSIGHT_API_LLM_GROQ_SERVICE_TIER: flex
657667
HINDSIGHT_API_URL: http://localhost:8888
658668
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
659669
# Prefer CPU-only PyTorch in CI (but keep PyPI for everything else)
@@ -693,6 +703,7 @@ jobs:
693703
HINDSIGHT_API_LLM_PROVIDER=${{ env.HINDSIGHT_API_LLM_PROVIDER }}
694704
HINDSIGHT_API_LLM_API_KEY=${{ env.HINDSIGHT_API_LLM_API_KEY }}
695705
HINDSIGHT_API_LLM_MODEL=${{ env.HINDSIGHT_API_LLM_MODEL }}
706+
HINDSIGHT_API_LLM_GROQ_SERVICE_TIER=${{ env.HINDSIGHT_API_LLM_GROQ_SERVICE_TIER }}
696707
EOF
697708
698709
- name: Start API server
@@ -732,6 +743,7 @@ jobs:
732743
HINDSIGHT_API_LLM_PROVIDER: groq
733744
HINDSIGHT_API_LLM_API_KEY: ${{ secrets.GROQ_API_KEY }}
734745
HINDSIGHT_API_LLM_MODEL: openai/gpt-oss-20b
746+
HINDSIGHT_API_LLM_GROQ_SERVICE_TIER: flex
735747
HINDSIGHT_API_URL: http://localhost:8888
736748
HINDSIGHT_EMBED_PACKAGE_PATH: ${{ github.workspace }}/hindsight-embed
737749
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -798,6 +810,7 @@ jobs:
798810
HINDSIGHT_API_LLM_PROVIDER=${{ env.HINDSIGHT_API_LLM_PROVIDER }}
799811
HINDSIGHT_API_LLM_API_KEY=${{ env.HINDSIGHT_API_LLM_API_KEY }}
800812
HINDSIGHT_API_LLM_MODEL=${{ env.HINDSIGHT_API_LLM_MODEL }}
813+
HINDSIGHT_API_LLM_GROQ_SERVICE_TIER=${{ env.HINDSIGHT_API_LLM_GROQ_SERVICE_TIER }}
801814
EOF
802815
803816
- name: Start API server
@@ -833,6 +846,7 @@ jobs:
833846
HINDSIGHT_API_LLM_PROVIDER: groq
834847
HINDSIGHT_API_LLM_API_KEY: ${{ secrets.GROQ_API_KEY }}
835848
HINDSIGHT_API_LLM_MODEL: openai/gpt-oss-20b
849+
HINDSIGHT_API_LLM_GROQ_SERVICE_TIER: flex
836850
HINDSIGHT_API_URL: http://localhost:8888
837851
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
838852
UV_INDEX: pytorch=https://download.pytorch.org/whl/cpu
@@ -889,6 +903,7 @@ jobs:
889903
HINDSIGHT_API_LLM_PROVIDER=${{ env.HINDSIGHT_API_LLM_PROVIDER }}
890904
HINDSIGHT_API_LLM_API_KEY=${{ env.HINDSIGHT_API_LLM_API_KEY }}
891905
HINDSIGHT_API_LLM_MODEL=${{ env.HINDSIGHT_API_LLM_MODEL }}
906+
HINDSIGHT_API_LLM_GROQ_SERVICE_TIER=${{ env.HINDSIGHT_API_LLM_GROQ_SERVICE_TIER }}
892907
EOF
893908
894909
- name: Start API server
@@ -982,6 +997,7 @@ jobs:
982997
HINDSIGHT_API_LLM_PROVIDER: groq
983998
HINDSIGHT_API_LLM_API_KEY: ${{ secrets.GROQ_API_KEY }}
984999
HINDSIGHT_API_LLM_MODEL: openai/gpt-oss-20b
1000+
HINDSIGHT_API_LLM_GROQ_SERVICE_TIER: flex
9851001
# Prefer CPU-only PyTorch in CI
9861002
UV_INDEX: pytorch=https://download.pytorch.org/whl/cpu
9871003

@@ -1026,6 +1042,7 @@ jobs:
10261042
HINDSIGHT_API_LLM_PROVIDER: groq
10271043
HINDSIGHT_API_LLM_API_KEY: ${{ secrets.GROQ_API_KEY }}
10281044
HINDSIGHT_API_LLM_MODEL: openai/gpt-oss-20b
1045+
HINDSIGHT_API_LLM_GROQ_SERVICE_TIER: flex
10291046
# For test_server_integration.py compatibility
10301047
HINDSIGHT_LLM_PROVIDER: groq
10311048
HINDSIGHT_LLM_API_KEY: ${{ secrets.GROQ_API_KEY }}
@@ -1075,6 +1092,7 @@ jobs:
10751092
HINDSIGHT_API_LLM_PROVIDER: groq
10761093
HINDSIGHT_API_LLM_API_KEY: ${{ secrets.GROQ_API_KEY }}
10771094
HINDSIGHT_API_LLM_MODEL: openai/gpt-oss-20b
1095+
HINDSIGHT_API_LLM_GROQ_SERVICE_TIER: flex
10781096
HINDSIGHT_API_URL: http://localhost:8888
10791097
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
10801098
UV_INDEX: pytorch=https://download.pytorch.org/whl/cpu
@@ -1130,6 +1148,7 @@ jobs:
11301148
HINDSIGHT_API_LLM_PROVIDER=${{ env.HINDSIGHT_API_LLM_PROVIDER }}
11311149
HINDSIGHT_API_LLM_API_KEY=${{ env.HINDSIGHT_API_LLM_API_KEY }}
11321150
HINDSIGHT_API_LLM_MODEL=${{ env.HINDSIGHT_API_LLM_MODEL }}
1151+
HINDSIGHT_API_LLM_GROQ_SERVICE_TIER=${{ env.HINDSIGHT_API_LLM_GROQ_SERVICE_TIER }}
11331152
EOF
11341153
11351154
- name: Start API server
@@ -1167,6 +1186,7 @@ jobs:
11671186
HINDSIGHT_API_LLM_PROVIDER: groq
11681187
HINDSIGHT_API_LLM_API_KEY: ${{ secrets.GROQ_API_KEY }}
11691188
HINDSIGHT_API_LLM_MODEL: openai/gpt-oss-20b
1189+
HINDSIGHT_API_LLM_GROQ_SERVICE_TIER: flex
11701190
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
11711191
UV_INDEX: pytorch=https://download.pytorch.org/whl/cpu
11721192

hindsight-api/hindsight_api/config.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,8 +233,6 @@ def normalize_config_dict(config: dict[str, Any]) -> dict[str, Any]:
233233
ENV_MPFP_TOP_K_NEIGHBORS = "HINDSIGHT_API_MPFP_TOP_K_NEIGHBORS"
234234
ENV_RECALL_MAX_CONCURRENT = "HINDSIGHT_API_RECALL_MAX_CONCURRENT"
235235
ENV_RECALL_CONNECTION_BUDGET = "HINDSIGHT_API_RECALL_CONNECTION_BUDGET"
236-
ENV_MCP_LOCAL_BANK_ID = "HINDSIGHT_API_MCP_LOCAL_BANK_ID"
237-
ENV_MCP_INSTRUCTIONS = "HINDSIGHT_API_MCP_INSTRUCTIONS"
238236
ENV_MENTAL_MODEL_REFRESH_CONCURRENCY = "HINDSIGHT_API_MENTAL_MODEL_REFRESH_CONCURRENCY"
239237

240238
# OpenTelemetry tracing configuration
@@ -389,7 +387,6 @@ def normalize_config_dict(config: dict[str, Any]) -> dict[str, Any]:
389387
DEFAULT_MPFP_TOP_K_NEIGHBORS = 20 # Fan-out limit per node in MPFP graph traversal
390388
DEFAULT_RECALL_MAX_CONCURRENT = 32 # Max concurrent recall operations per worker
391389
DEFAULT_RECALL_CONNECTION_BUDGET = 4 # Max concurrent DB connections per recall operation
392-
DEFAULT_MCP_LOCAL_BANK_ID = "mcp"
393390
DEFAULT_MENTAL_MODEL_REFRESH_CONCURRENCY = 8 # Max concurrent mental model refreshes
394391

395392
# Retain settings

hindsight-api/hindsight_api/mcp_local.py

Lines changed: 16 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -1,157 +1,39 @@
11
"""
2-
Local MCP server for use with Claude Code (stdio transport).
2+
Local MCP server entry point for use with Claude Code (HTTP transport).
33
4-
This runs a fully local Hindsight instance with embedded PostgreSQL (pg0).
5-
No external database or server required.
4+
This is a thin wrapper around the main hindsight-api server that pre-configures
5+
sensible defaults for local use (embedded PostgreSQL via pg0, warning log level).
6+
7+
The full API runs on localhost:8888. Configure Claude Code's MCP settings:
8+
claude mcp add --transport http hindsight http://localhost:8888/mcp/
9+
10+
Or pinned to a specific bank (single-bank mode):
11+
claude mcp add --transport http hindsight http://localhost:8888/mcp/default/
612
713
Run with:
814
hindsight-local-mcp
915
1016
Or with uvx:
1117
uvx hindsight-api@latest hindsight-local-mcp
1218
13-
Configure in Claude Code's MCP settings:
14-
{
15-
"mcpServers": {
16-
"hindsight": {
17-
"command": "uvx",
18-
"args": ["hindsight-api@latest", "hindsight-local-mcp"],
19-
"env": {
20-
"HINDSIGHT_API_LLM_API_KEY": "your-openai-key"
21-
}
22-
}
23-
}
24-
}
25-
2619
Environment variables:
2720
HINDSIGHT_API_LLM_API_KEY: Required. API key for LLM provider.
2821
HINDSIGHT_API_LLM_PROVIDER: Optional. LLM provider (default: "openai").
2922
HINDSIGHT_API_LLM_MODEL: Optional. LLM model (default: "gpt-4o-mini").
30-
HINDSIGHT_API_MCP_LOCAL_BANK_ID: Optional. Memory bank ID (default: "mcp").
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."
23+
HINDSIGHT_API_DATABASE_URL: Optional. Override database URL (default: pg0://hindsight-mcp).
4024
"""
4125

42-
import logging
4326
import os
44-
import sys
45-
46-
from mcp.server.fastmcp import FastMCP
47-
48-
from hindsight_api.config import (
49-
DEFAULT_MCP_LOCAL_BANK_ID,
50-
DEFAULT_MCP_RECALL_DESCRIPTION,
51-
DEFAULT_MCP_RETAIN_DESCRIPTION,
52-
ENV_MCP_INSTRUCTIONS,
53-
ENV_MCP_LOCAL_BANK_ID,
54-
)
55-
from hindsight_api.mcp_tools import MCPToolsConfig, register_mcp_tools
56-
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()
60-
_log_level_map = {
61-
"critical": logging.CRITICAL,
62-
"error": logging.ERROR,
63-
"warning": logging.WARNING,
64-
"info": logging.INFO,
65-
"debug": logging.DEBUG,
66-
}
67-
logging.basicConfig(
68-
level=_log_level_map.get(_log_level_str, logging.WARNING),
69-
format="%(asctime)s - %(levelname)s - %(name)s - %(message)s",
70-
stream=sys.stderr, # MCP uses stdout for protocol, logs go to stderr
71-
)
72-
logger = logging.getLogger(__name__)
73-
74-
75-
def create_local_mcp_server(bank_id: str, memory=None) -> FastMCP:
76-
"""
77-
Create a stdio MCP server with retain/recall tools.
78-
79-
Args:
80-
bank_id: The memory bank ID to use for all operations.
81-
memory: Optional MemoryEngine instance. If not provided, creates one with pg0.
82-
83-
Returns:
84-
Configured FastMCP server instance.
85-
"""
86-
# Import here to avoid slow startup if just checking --help
87-
from hindsight_api import MemoryEngine
88-
89-
# Create memory engine with pg0 embedded database if not provided
90-
if memory is None:
91-
memory = MemoryEngine(db_url="pg0://hindsight-mcp")
92-
93-
# Get custom instructions from environment variable (appended to both tools)
94-
extra_instructions = os.environ.get(ENV_MCP_INSTRUCTIONS, "")
95-
96-
retain_description = DEFAULT_MCP_RETAIN_DESCRIPTION
97-
recall_description = DEFAULT_MCP_RECALL_DESCRIPTION
98-
99-
if extra_instructions:
100-
retain_description = f"{DEFAULT_MCP_RETAIN_DESCRIPTION}\n\nAdditional instructions: {extra_instructions}"
101-
recall_description = f"{DEFAULT_MCP_RECALL_DESCRIPTION}\n\nAdditional instructions: {extra_instructions}"
102-
103-
mcp = FastMCP("hindsight")
104-
105-
# Configure and register tools using shared module
106-
config = MCPToolsConfig(
107-
bank_id_resolver=lambda: bank_id,
108-
include_bank_id_param=False, # Local MCP uses fixed bank_id
109-
tools={"retain", "recall"}, # Local MCP only has retain and recall
110-
retain_description=retain_description,
111-
recall_description=recall_description,
112-
retain_fire_and_forget=True, # Local MCP uses fire-and-forget pattern
113-
)
114-
115-
register_mcp_tools(mcp, memory, config)
116-
117-
return mcp
118-
119-
120-
async def _initialize_and_run(bank_id: str):
121-
"""Initialize memory and run the MCP server."""
122-
from hindsight_api import MemoryEngine
123-
124-
# Create and initialize memory engine with pg0 embedded database
125-
# Note: We avoid printing to stderr during init as MCP clients show it as "errors"
126-
memory = MemoryEngine(db_url="pg0://hindsight-mcp")
127-
await memory.initialize()
128-
129-
# Create and run the server
130-
mcp = create_local_mcp_server(bank_id, memory=memory)
131-
await mcp.run_stdio_async()
132-
133-
134-
def main():
135-
"""Main entry point for the stdio MCP server."""
136-
import asyncio
137-
138-
from hindsight_api.config import ENV_LLM_API_KEY, get_config
13927

140-
# Check for required environment variables
141-
config = get_config()
142-
if not config.llm_api_key:
143-
print(f"Error: {ENV_LLM_API_KEY} environment variable is required", file=sys.stderr)
144-
print("Set it in your MCP configuration or shell environment", file=sys.stderr)
145-
sys.exit(1)
14628

147-
# Get bank ID from environment, default to "mcp"
148-
bank_id = os.environ.get(ENV_MCP_LOCAL_BANK_ID, DEFAULT_MCP_LOCAL_BANK_ID)
29+
def main() -> None:
30+
"""Start the Hindsight API server with local defaults."""
31+
# Set local defaults (only if not already configured by the user)
32+
os.environ.setdefault("HINDSIGHT_API_DATABASE_URL", "pg0://hindsight-mcp")
14933

150-
# Note: We don't print to stderr as MCP clients display it as "error output"
151-
# Use HINDSIGHT_API_LOG_LEVEL=debug for verbose startup logging
34+
from hindsight_api.main import main as api_main
15235

153-
# Run the async initialization and server
154-
asyncio.run(_initialize_and_run(bank_id))
36+
api_main()
15537

15638

15739
if __name__ == "__main__":

0 commit comments

Comments
 (0)