loading…
Search for a command to run...
loading…
Exposes the Integrated Memory System (IMS) capabilities, including session management, memory storage, and RAG-based context search, via the Model Context Proto
Exposes the Integrated Memory System (IMS) capabilities, including session management, memory storage, and RAG-based context search, via the Model Context Protocol. It allows MCP-aware clients to interact with IMS backends to maintain long-term memory and context across sessions.
MCP server that exposes the Integrated Memory System (IMS) as tools via the Model Context Protocol Python SDK.
It wraps the existing IMS HTTP backend (session-memory, memory-core, context-rag) and makes those capabilities available to MCP-aware clients (e.g. mcphub, Warp, VS Code, LibreChat).
http://localhost:8000, orhttp://ims.delongpa.comThat's it! The MCP server includes all necessary client code to communicate with the IMS backend.
From the ims-mcp directory:
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
This installs the MCP Python SDK and required dependencies (httpx).
The MCP server talks to IMS via environment variables. These can be provided in three ways (in order of increasing precedence):
.env file in the project root (or a path specified by
IMS_ENV_FILE)env block)Supported variables:
IMS_BASE_URL (optional, default https://ims.delongpa.com)http://localhost:8000).IMS_HTTP_TIMEOUT (optional, default 5.0 seconds)IMS_CLIENT_NAME (optional, default "ims-mcp")IMS_VERIFY_SSL (optional, default true)false only for local/dev environments with self-signed certs.IMS_ENV_FILE (optional, default .env).env-style file to load before reading other vars.IMS_HEALTH_PROJECT_ID (optional, default ims-mcp)ims://health resource when probing read endpoints.Create a file named .env next to server.py (only needed if you want to override defaults, e.g. local dev):
IMS_BASE_URL=http://localhost:8000
IMS_HTTP_TIMEOUT=5.0
IMS_CLIENT_NAME=ims-mcp
You can override the file name/path with IMS_ENV_FILE if needed.
Example using exported variables:
export IMS_BASE_URL="http://ims.delongpa.com"
export IMS_HTTP_TIMEOUT="5.0"
export IMS_CLIENT_NAME="ims-mcp"
export IMS_VERIFY_SSL="true"
With the venv activated (and optionally IMS_BASE_URL set):
source .venv/bin/activate
# Optional override for local dev:
# export IMS_BASE_URL="http://localhost:8000"
python server.py
The server runs over stdio, which is what MCP clients expect when they spawn it as a subprocess.
To use this server from mcphub on a host where you cloned this repo to
/opt/mcps/ims-mcp and created the venv as above, add an entry like:
"IMS-MCP": {
"type": "stdio",
"command": "/opt/mcps/ims-mcp/.venv/bin/python",
"args": [
"/opt/mcps/ims-mcp/server.py"
],
"env": {
"IMS_BASE_URL": "http://ims.delongpa.com"
}
}
Adjust paths and IMS_BASE_URL to match your environment.
The MCP server exposes the following tools for interacting with IMS capabilities:
ims.context-rag.context_searchexpand_graph (default: true) and graph_depth (default: 2) parametersexpand_graph=true, vector search results are enriched with related entities from the ontology graphdocs_index_directoryproject_docs (chunked by default)IMS_MEILI_URL / IMS_MEILI_API_KEY and stores user_id (from IMS_USER_ID or OS username)include_globs: only include files matching at least one glob (e.g. **/*-meta.xml for Salesforce metadata)exclude_globs: exclude files matching any globno_default_excludes: disable built-in excludes (e.g. .env*, lockfiles, *.min.js)ims.memory-core.store_memorykind="decision" → creates Decision node in Neo4jkind="issue" → creates Bug node in Neo4jkind="fact"/"note" → memory only (no graph node)ims.memory-core.find_memoriesims.session-memory.auto_sessionims.session-memory.resolve_sessionhook_session_id into session metadata for strict session gatingims.session-memory.get_bound_sessionhook_session_id is already bound to an open IMS session for a projectims.session-memory.continue_sessionims.session-memory.checkpoint_sessionims.session-memory.wrap_sessionims.session-memory.list_open_sessionsims.session-memory.resume_sessionhandoff_createims.graph.create_nodeims.graph.create_relationshipims.graph.impact_analysisims.graph.blocking_analysisims.graph.architectural_driftims.graph.lookup_patternsims.graph.corrections_readyims.graph.promote_correctionEach tool includes comprehensive documentation in its docstring. For the complete
IMS protocol and usage guidelines, see AGENTS.md.
The app/ directory provides client libraries for direct programmatic access to IMS:
from app.ims_client import IMSClient
ims = IMSClient()
# Session management
session = ims.session_memory.continue_session(
project_id="my-project",
agent_id="implementer",
task_id="add-feature"
)
# Long-term memory with automatic graph node creation
# kind="decision" automatically creates a Decision node in Neo4j
memory = ims.memory_core.store_memory(
project_id="my-project",
text="Use Redis for session state. Rationale: Need TTL and atomic ops.",
kind="decision", # Creates Decision node in graph
tags=["architecture", "redis"],
importance=0.9
)
# kind="issue" automatically creates a Bug node in Neo4j
ims.memory_core.store_memory(
project_id="my-project",
text="Auth timeout bug fixed by increasing session TTL to 1 hour",
kind="issue", # Creates Bug node in graph
tags=["bug", "auth"]
)
# Context search with graph expansion (default)
results = ims.context_rag.context_search(
project_id="my-project",
query="How is authentication handled?",
sources=["code", "docs", "memories"],
expand_graph=True, # Default: enrich with graph relationships
graph_depth=2 # Default: traverse 2 levels deep
)
# Vector-only search (disable graph expansion)
results = ims.context_rag.context_search(
project_id="my-project",
query="authentication patterns",
sources=["code"],
expand_graph=False # Disable graph expansion for pure vector search
)
# Graph operations (ontology)
node_id = ims.graph.create_node(
node_type="Decision",
properties={
"text": "Use Redis for session state",
"project_id": "my-project",
"rationale": "Low latency, TTL support"
}
)
# Create relationships
ims.graph.create_relationship(
from_id=decision_node_id,
rel_type="affects",
to_id=component_node_id,
properties={"impact": "high"}
)
# Impact analysis
impact = ims.graph.impact_analysis(
entity_id=decision_node_id,
entity_type="Decision"
)
The MCP server also exposes read-only resources for inspection/discovery:
ims://healthsession-memory, memory-core, and context-rag reachability checks.ims://capabilitiesims://sessions/{project_id}/openims://sessions/{project_id}/{user_id}/openims://graph/{project_id}/driftims://graph/corrections/readyims://graph/{project_id}/corrections/readyThe docs_index_directory tool (and the underlying chunking/indexing logic) now writes richer per-chunk metadata into Meilisearch documents:
snippet (short preview)path (relative path)ext (file extension without dot)tags (simple tags derived from path/extension)user_id (owner)However, the IMS backend’s context-rag service (the thing behind POST /context/search) must be updated to use these fields during docs retrieval. Concretely, to leverage the new semantics you’ll typically want to update the IMS backend to:
Return better previews for docs hits
snippet from Meilisearch instead of returning the entire content chunk as the hit snippet.content available for grounding (either return it in metadata or as a separate field), but avoid flooding the prompt/UI.Support docs filtering controls (ext/path/tags)
/context/search request to accept optional filters such as:ext: allowlist (e.g. ["md","txt"] or ["cls","trigger"] for Apex)path_prefix: e.g. "docs/"tags: e.g. ["terraform","yaml"]filter expressions, relying on path, ext, and tags being configured as filterable attributes.Handle Salesforce metadata patterns intentionally
**/*-meta.xml. The indexer can include these using include globs, but the retrieval layer may want to:*-meta.xml by default, unless the query suggests metadata is needed, orpath/tags conventions to target metadata vs source.Guard relevance when doc counts increase
path (keep best scoring chunk per file), and/orpath values to keep context diverse.Decide on ownership / multi-user scoping
user_id. If you want per-user isolation for docs retrieval, the IMS backend should optionally filter by user_id when present.If you want to keep the IMS backend interface stable, the smallest useful change is #1 (use snippet) plus a single optional ext filter for the docs retrieval portion.
If you're upgrading from a pre-ontology version of IMS, this section explains what changed and how to adapt your workflows.
New capabilities added (fully backward compatible):
context-rag with optional graph expansionmemory-coreNo breaking changes:
memory-core.store_memory calls automatically benefit from graph node creationcontext-rag.context_search calls automatically include graph expansion (can be disabled)You don't need to change anything to keep using IMS as before. However, you can opt into new capabilities:
Pre-ontology:
# Pure vector similarity search
results = ims.context_rag.context_search(
project_id="my-app",
query="authentication flow",
sources=["code", "docs", "memories"]
)
Post-ontology (default behavior, enhanced with graph relationships):
# Hybrid vector + graph search (automatically enabled)
results = ims.context_rag.context_search(
project_id="my-app",
query="authentication flow",
sources=["code", "docs", "memories"],
expand_graph=True, # Default: enriches results with related entities
graph_depth=2 # Default: traverse 2 relationship levels
)
To disable graph expansion (revert to pure vector search):
results = ims.context_rag.context_search(
project_id="my-app",
query="authentication flow",
sources=["code"],
expand_graph=False # Disable graph for pure vector similarity
)
Pre-ontology:
# Just stored in Postgres + Qdrant
memory_id = ims.memory_core.store_memory(
project_id="my-app",
text="Use Redis for session state. Rationale: Low latency, TTL support.",
kind="decision",
tags=["architecture", "redis"]
)
Post-ontology (automatic graph node, no code changes needed):
# Same call, now also creates Decision node in Neo4j graph
memory_id = ims.memory_core.store_memory(
project_id="my-app",
text="Use Redis for session state. Rationale: Low latency, TTL support.",
kind="decision", # Automatically creates Decision graph node
tags=["architecture", "redis"]
)
# Backend now creates:
# - Memory record (Postgres + Qdrant embedding) - as before
# - Decision node (Neo4j graph) - NEW, enables relationships & impact analysis
You can now explicitly create relationships and run analysis queries:
# Create explicit relationships between entities
ims.graph.create_relationship(
from_id=decision_node_id,
rel_type="affects",
to_id=component_node_id,
properties={"impact": "high"}
)
# Run impact analysis
impact = ims.graph.impact_analysis(
entity_id=decision_node_id,
entity_type="Decision"
)
print(f"This decision affects {len(impact['affected_components'])} components")
# Find blocking bugs
blocking = ims.graph.blocking_analysis(feature_id="auth-2fa")
print(f"{len(blocking['blocking_bugs'])} bugs block this feature")
For most users: No action required. Your existing code continues to work and automatically benefits from graph enhancements.
To fully leverage ontology features:
Understand auto-graph-node behavior:
kind="decision" → creates Decision nodekind="issue" → creates Bug nodekind="fact"/"note" → memory only (no graph node)Review existing memories:
Adopt graph operations incrementally:
Adjust context search if needed:
graph_depthexpand_graph=Falsecontext-rag)Vector-only search (expand_graph=False):
Hybrid vector + graph search (expand_graph=True, default):
graph_depth (each level adds ~50-200ms), graph densityRecommendations:
expand_graph=False for speedgraph_depth to 1-2 for most queries (default: 2)graph_depth=3 only for deep dependency analysismemory-core)Pre-ontology (memory only):
Post-ontology (memory + graph node):
kind="decision"/"issue" incur graph overheadkind="fact"/"note" remain at pre-ontology speed (no graph node)Recommendations:
Node creation:
Relationship creation:
Analysis queries (impact, blocking, drift):
Recommendations:
Vector stores (Qdrant):
Graph database (Neo4j):
Text search (Meilisearch):
Symptom: ims.graph.create_node() returns 500 error
Likely causes:
Solutions:
# 1. Check backend health
health = client.read_resource("ims://health")
print(health) # Look for graph_db status
# 2. Verify node type is valid
# Valid types: Decision, Bug, Feature, Component, Correction, Reflection, Pattern, Lesson
# 3. Ensure required properties exist
node_id = ims.graph.create_node(
node_type="Decision",
properties={
"text": "Required: decision description",
"project_id": "Required: project identifier",
"rationale": "Optional: why this decision"
}
)
Backend-side checks (if you control IMS backend):
docker ps | grep neo4j or check Neo4j serviceMATCH (n) RETURN labels(n) LIMIT 10 in Neo4j browserSymptom: context_search() returns only vector results, no graph expansion
Likely causes:
expand_graph=FalseSolutions:
# 1. Explicitly enable graph expansion (default is True)
results = ims.context_rag.context_search(
project_id="my-app",
query="your query",
sources=["memories"], # Memories most likely to have graph nodes
expand_graph=True, # Explicitly enabled
graph_depth=2 # Increase if needed
)
# 2. Check if graph nodes exist
from app.ims_client import IMSClient
ims = IMSClient()
try:
# Attempt to query graph
impact = ims.graph.impact_analysis(
entity_id="any-decision-id",
entity_type="Decision"
)
print("Graph backend is operational")
except Exception as e:
print(f"Graph backend issue: {e}")
# 3. Store some decisions/issues to populate graph
ims.memory_core.store_memory(
project_id="my-app",
text="Use PostgreSQL for relational data",
kind="decision", # Creates Decision node
tags=["database"]
)
Symptom: store_memory() takes 200-300ms instead of previous 100ms
Explanation: This is expected for kind="decision"/"issue" due to graph node creation. Not a bug.
Solutions:
Adjust expectations: Graph node creation adds 50-100ms overhead for decisions/issues. This is the cost of enabling relationships and impact analysis.
For high-frequency storage of non-decision data:
# Use kind="fact" or "note" to skip graph node creation
ims.memory_core.store_memory(
project_id="my-app",
text="Log entry: request took 150ms",
kind="fact", # No graph node, faster
tags=["performance"]
)
Batch operations: If storing many decisions, group related ones and create relationships afterward:
# Store decisions
decision_ids = []
for decision_text in decisions:
id = ims.memory_core.store_memory(
project_id="my-app",
text=decision_text,
kind="decision"
)
decision_ids.append(id)
# Create relationships in batch (future optimization)
# Currently, relationships are created one at a time
Symptom: httpx.ReadTimeout or TimeoutException
Likely causes:
Solutions:
Increase timeout:
export IMS_HTTP_TIMEOUT=10.0 # Default is 5.0 seconds
Reduce graph traversal depth:
results = ims.context_rag.context_search(
project_id="my-app",
query="query",
sources=["memories"],
expand_graph=True,
graph_depth=1 # Reduce from default 2
)
Check backend health:
health = client.read_resource("ims://health")
# Look for slow response times or error status
Symptom: create_relationship() fails with validation error
Valid relationship types:
implements (Feature → Decision)blocks (Bug → Feature)affects (Decision → Component)depends_on (Component → Component)supersedes (Decision → Decision)fixed_by (Bug → Decision)worked_on (Session → Feature/Bug)addresses (Correction → Bug/Issue)inspired_by (Pattern → Correction, Reflection)learned_from (Lesson → Bug, Decision, Reflection)documents (Reflection → Session/Decision)applied_to (Pattern → Component)tagged_with (any → Tag)relates_to (generic relationship)precedes (temporal ordering)contains (composition)Solution:
# Use valid relationship type
ims.graph.create_relationship(
from_id=decision_id,
rel_type="affects", # Must be from list above
to_id=component_id
)
Check resources first:
from mcp import ClientSession
import mcp.types as types
import asyncio
async def check_capabilities():
# Connect to ims-mcp server and read capabilities
# This shows all available tools and resources
capabilities = await session.read_resource(
types.ReadResourceRequest(
uri="ims://capabilities"
)
)
print(capabilities)
asyncio.run(check_capabilities())
Common issues:
IMS_BASE_URL env var, ensure backend is runningIMS_VERIFY_SSL=false for local dev (not production)IMS_BASE_URLReporting bugs:
Добавь это в claude_desktop_config.json и перезапусти Claude Desktop.
{
"mcpServers": {
"ims-mcp-server": {
"command": "npx",
"args": []
}
}
}