MentisDB supports multiple integration paths depending on your setup. The recommended path is Model Context Protocol (MCP) — a stdio-based protocol that works with Claude Desktop, OpenCode, Cursor, and any other MCP-compatible tool. For non-MCP clients, the REST API works with any HTTP library.
| Integration | Protocol | Best For | Setup Effort |
|---|---|---|---|
| Claude Desktop | MCP via mcp-remote | Claude Desktop users on macOS/Linux | ~2 minutes with wizard |
| OpenCode | MCP | Terminal-based coding agents | ~2 minutes with wizard |
| pymentisdb + LangChain | REST | Python agents, chatbots | pip install + 10 lines of code |
| Custom MCP client | MCP stdio | Building MCP-native integrations | Depends on complexity |
| REST API | HTTP/JSON | Any language, any platform | Read the OpenAPI spec |
The fastest path to any integration is mentisdbd setup:
# Interactive wizard — picks the right config for your platform
mentisdbd setup claude-desktop
# Dry run to see what would be written
mentisdbd setup claude-desktop --dry-run
# Auto-accept everything (CI/CD friendly)
mentisdbd setup claude-desktop --assume-yes
The wizard detects your platform, checks for prerequisites (Node.js, mcp-remote, etc.), writes the config file, and tells you what to do next. Run it and follow the prompts.
Homebrew-installed mcp-remote: If you installed mcp-remote via
brew install mcp-remote, the wizard detects this and writes the correct
mcp-remote as the command directly (no Node.js wrapper needed). The
generated config looks like:
{
"mcpServers": {
"mentisdb": {
"command": "/opt/homebrew/bin/mcp-remote",
"args": ["https://my.mentisdb.com:9473"],
"env": { "NODE_TLS_REJECT_UNAUTHORIZED": "0" }
}
}
}
Claude Desktop uses MCP over stdio. Since MCP servers run locally and can't handle
self-signed HTTPS certificates, MentisDB ships an mcp-remote bridge that
tunnels MCP stdio over HTTPS to your mentisdbd instance.
--https --port 9473)mcp-remote installed (via Homebrew or npm)Homebrew users: brew install mcp-remote installs mcp-remote
with a proper shebang pointing to the correct Node version. The wizard detects this
automatically and writes the minimal config.
Edit ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"mentisdb": {
"command": "/opt/homebrew/bin/mcp-remote",
"args": ["https://my.mentisdb.com:9473"],
"env": {
"NODE_TLS_REJECT_UNAUTHORIZED": "0"
}
}
}
}
The NODE_TLS_REJECT_UNAUTHORIZED=0 flag tells mcp-remote to accept
self-signed certificates. This is required for private servers with self-signed certs.
Omit it when using a public HTTPS endpoint with valid certificates.
After saving the config, restart Claude Desktop. The mentisdb MCP server will be available — verify by asking Claude directly:
What tools do you have from mentisdb?
Or use the MCP info command in any MCP client to confirm the mentisdb server is connected and responding.
| Tool | Description |
|---|---|
mentisdb_append | Append a structured memory thought |
mentisdb_ranked_search | Semantic search with multi-signal ranking |
mentisdb_context_bundles | Get seed thoughts with supporting evidence |
mentisdb_list_chains | List all memory chains |
mentisdb_register_webhook | Register HTTP callbacks on thought appends |
OpenCode is a terminal-based coding agent. It uses MCP for tool integration. The wizard handles the setup:
mentisdbd setup opencode
This writes the OpenCode MCP config to the standard location. OpenCode's MCP block uses a slightly different format:
{
"mcp": {
"mentisdb": {
"type": "remote",
"url": "https://my.mentisdb.com:9473",
"enabled": true
}
}
}
If there's no MCP integration available for your tool, the REST API works from any language with HTTP support. mentisdbd exposes a full REST API on port 9472 (HTTP) or 9473 (HTTPS).
# Append a thought to a specific chain (bash + curl)
curl -X POST http://127.0.0.1:9472/v1/thoughts \
-H "Content-Type: application/json" \
-d '{
"chain_key": "my-project",
"thought_type": "Insight",
"content": "The cache invalidation strategy needs rethinking.",
"role": "Memory",
"importance": 0.8,
"tags": ["architecture", "cache"],
"concepts": ["invalidation", "consistency"]
}'
# Ranked search on a specific chain
curl -X POST http://127.0.0.1:9472/v1/ranked-search \
-H "Content-Type: application/json" \
-d '{
"chain_key": "my-project",
"text": "cache invalidation strategy",
"limit": 5,
"min_importance": 0.5
}'
# Search across a branch chain AND its ancestor chains (federated)
curl -X POST http://127.0.0.1:9472/v1/ranked-search \
-H "Content-Type: application/json" \
-d '{
"chain_key": "project-x",
"text": "what is our company's position on cache consistency?",
"limit": 5
}'
# List chains
curl http://127.0.0.1:9472/v1/chains
import requests
base = "http://127.0.0.1:9472"
# Append to a specific chain
r = requests.post(f"{base}/v1/thoughts", json={
"chain_key": "my-project",
"thought_type": "Finding",
"content": "Query latency spiked after the last deploy.",
"role": "Memory",
"importance": 0.8,
"tags": ["performance", "database"],
})
thought = r.json()["thought"]
print(f"Appended to {thought['chain_key']}: {thought['id']}")
# Search within a specific chain
r = requests.post(f"{base}/v1/ranked-search", json={
"chain_key": "my-project",
"text": "database performance",
"limit": 5,
"thought_types": ["Finding", "Insight"],
})
results = r.json()
for hit in results["results"]:
print(f"[{hit['score']['total']:.3f}] {hit['thought']['content']}")
import requests
base = "http://127.0.0.1:9472"
# ==================== THOUGHTS ====================
# Append a thought to a specific chain
r = requests.post(f"{base}/v1/thoughts", json={
"chain_key": "my-project", # specify chain — omit to use default chain
"thought_type": "Insight",
"content": "The cache invalidation strategy needs rethinking.",
"role": "Memory",
"importance": 0.8,
"confidence": 0.9,
"tags": ["architecture", "cache"],
"concepts": ["invalidation", "consistency"],
})
thought = r.json()["thought"]
print(f"Appended: {thought['id']}")
# Get a thought by ID
r = requests.get(f"{base}/v1/thoughts/{thought['id']}")
print(r.json())
# Get genesis (first) thought
r = requests.post(f"{base}/v1/thoughts/genesis")
print(r.json())
# Traverse thoughts from anchor
r = requests.post(f"{base}/v1/thoughts/traverse", json={
"anchor_index": 1,
"direction": "forward",
"chunk_size": 10
})
print(r.json())
# ==================== SEARCH ====================
# Ranked search within a specific chain
r = requests.post(f"{base}/v1/ranked-search", json={
"chain_key": "my-project",
"text": "cache invalidation",
"limit": 5,
"min_importance": 0.5
})
print(r.json())
# Lexical search
r = requests.post(f"{base}/v1/lexical-search", json={
"chain_key": "my-project",
"text": "database performance",
"limit": 10
})
print(r.json())
# Semantic search
r = requests.post(f"{base}/v1/search", json={
"chain_key": "my-project",
"text": "architecture decisions",
"thought_types": ["Insight", "Finding"]
})
print(r.json())
# Context bundles
r = requests.post(f"{base}/v1/context-bundles", json={
"chain_key": "my-project",
"text": "cache consistency",
"limit": 3,
"graph": {"max_depth": 2, "include_seeds": True}
})
print(r.json())
# ==================== CHAINS ====================
# List all chains
r = requests.get(f"{base}/v1/chains")
print(r.json())
# Branch from a thought
r = requests.post(f"{base}/v1/chains/branch", json={
"source_chain_key": "default",
"branch_chain_key": "experiment-1",
"branch_thought_id": thought["id"]
})
print(r.json())
# Merge chains
r = requests.post(f"{base}/v1/chains/merge", json={
"source_chain_key": "experiment-1",
"target_chain_key": "default"
})
print(r.json())
# ==================== AGENTS ====================
# Upsert agent
r = requests.post(f"{base}/v1/agents/upsert", json={
"agent_id": "my-agent-001",
"display_name": "My Agent",
"description": "Primary coding agent"
})
print(r.json())
# List agents
r = requests.get(f"{base}/v1/agents")
print(r.json())
# ==================== WEBHOOKS ====================
# Register webhook
r = requests.post(f"{base}/v1/webhooks", json={
"url": "https://myapp.com/webhook/mentisdb",
"chain_key_filter": "default",
"thought_type_filter": ["Insight", "Finding"]
})
webhook = r.json()["webhook"]
print(f"Registered: {webhook['id']}")
# List webhooks
r = requests.get(f"{base}/v1/webhooks")
print(r.json())
# Delete webhook
r = requests.delete(f"{base}/v1/webhooks/{webhook['id']}")
print(r.json())
# ==================== HEAD & HEALTH ====================
# Get head metadata
r = requests.post(f"{base}/v1/head")
print(r.json())
# Health check
r = requests.get(f"{base}/health")
print(r.json())
# ==================== SKILLS ====================
# List skills
r = requests.get(f"{base}/v1/skills")
print(r.json())
# Upload skill
r = requests.post(f"{base}/v1/skills/upload", json={
"agent_id": "my-agent-001",
"skill_id": "my-skill",
"content": "# My Skill\n\nA custom skill for...",
"format": "markdown"
})
print(r.json())
# Search skills
r = requests.post(f"{base}/v1/skills/search", json={
"text": "coding assistance"
})
print(r.json())
# Read skill
r = requests.post(f"{base}/v1/skills/read", json={
"skill_id": "my-skill"
})
print(r.json())
# List skill versions
r = requests.post(f"{base}/v1/skills/versions", json={
"skill_id": "my-skill"
})
print(r.json())
# Deprecate skill
r = requests.post(f"{base}/v1/skills/deprecate", json={
"skill_id": "my-skill",
"reason": "Superseded by new version"
})
print(r.json())
# Revoke skill
r = requests.post(f"{base}/v1/skills/revoke", json={
"skill_id": "my-skill",
"reason": "Security concern"
})
print(r.json())
# ==================== MEMORY MARKDOWN ====================
# Export chain as MEMORY.md
r = requests.post(f"{base}/v1/memory-markdown", json={
"chain_key": "my-project", // optional — omit to export the default chain
"limit": 100
})
print(r.json()) # Returns { "markdown": "# MEMORY.md\n\n..." }
# Import from MEMORY.md into a specific chain
r = requests.post(f"{base}/v1/import-markdown", json={
"markdown": "# MEMORY.md\n\n## Thought\nContent here...",
"chain_key": "my-project"
})
print(r.json())
# ==================== RECENT CONTEXT & BOOTSTRAP ====================
# Get recent context for prompts from a specific chain
r = requests.post(f"{base}/v1/recent-context", json={
"chain_key": "my-project", // optional — omit to use the default chain
"last_n": 10
})
print(r.json())
# Bootstrap a chain if empty
r = requests.post(f"{base}/v1/bootstrap", json={
"chain_key": "my-project", // optional — omit to bootstrap the default chain
"agent_id": "my-agent-001",
"content": "Initial bootstrap thought"
})
print(r.json())
# ==================== RETROSPECTIVES ====================
# Append retrospective to a specific chain
r = requests.post(f"{base}/v1/retrospectives", json={
"chain_key": "my-project", // optional — omit to use the default chain
"content": "Lesson learned: always verify TLS certs in development.",
"agent_id": "my-agent-001",
"refs": [thought["id"]]
})
print(r.json())
Generate a Swagger client for any language at
Swagger Editor using the OpenAPI spec, or check the
interactive docs at http://127.0.0.1:9472/docs (if running with
--enable-http-docs).
| Method | Path | Description |
|---|---|---|
POST | /v1/thoughts | Append a thought |
GET | /v1/thoughts/{id} | Get a thought by ID, index, or hash |
POST | /v1/thoughts/genesis | Get the first thought in the chain |
POST | /v1/thoughts/traverse | Traverse chain forward/backward from anchor |
POST | /v1/ranked-search | Ranked semantic search |
POST | /v1/context-bundles | Seed + supporting context bundles |
POST | /v1/lexical-search | Flat ranked lexical search |
POST | /v1/search | Semantic search with filters |
GET | /v1/chains | List all chains |
POST | /v1/chains/branch | Create a new chain from a thought |
POST | /v1/chains/merge | Merge source chain into target |
POST | /v1/agents/upsert | Create or update an agent registry record |
GET | /v1/agents | List all registered agents |
POST | /v1/webhooks | Register a webhook |
GET | /v1/webhooks | List all registered webhooks |
DELETE | /v1/webhooks/{id} | Delete a webhook by UUID |
POST | /v1/head | Get head metadata and latest thought |
GET | /health | Health check |
POST | /v1/skills/upload | Upload a new skill version |
POST | /v1/skills/read | Read a stored skill |
GET | /v1/skills | List skill summaries |
POST | /v1/skills/search | Search skills by metadata |
POST | /v1/skills/versions | List versions for one skill |
POST | /v1/skills/deprecate | Mark a skill as deprecated |
POST | /v1/skills/revoke | Mark a skill as revoked |
POST | /v1/memory-markdown | Export chain as MEMORY.md |
POST | /v1/import-markdown | Import MEMORY.md into chain |
POST | /v1/recent-context | Get recent context for prompts |
POST | /v1/bootstrap | Bootstrap a chain if empty |
POST | /v1/retrospectives | Append a retrospective thought |
Request:
{
"chain_key": "my-project", // optional — defaults to server's default chain
"thought_type": "Insight",
"content": "The cache invalidation strategy needs rethinking.",
"role": "Memory",
"importance": 0.8,
"confidence": 0.9,
"tags": ["architecture", "cache"],
"concepts": ["invalidation", "consistency"],
"agent_id": "my-agent-001"
}
Response:
{
"thought": {
"id": "01AR3WZFV3Q5MZ8...",
"thought_type": "Insight",
"content": "The cache invalidation strategy needs rethinking.",
"role": "Memory",
"importance": 0.8,
"confidence": 0.9,
"tags": ["architecture", "cache"],
"concepts": ["invalidation", "consistency"],
"created_at": "2026-04-14T10:30:00Z",
"chain_index": 42,
"content_hash": "sha256:abc123..."
}
}
Request:
{
"chain_key": "my-project", // optional — omit to search the default chain
"text": "cache invalidation strategy",
"limit": 5,
"min_importance": 0.5,
"thought_types": ["Insight", "Finding"],
"tags_any": ["architecture"],
"concepts_any": ["consistency"],
"since": "2026-01-01T00:00:00Z"
}
Response:
{
"results": [
{
"score": {
"total": 0.94,
"lexical": 0.42,
"semantic": 0.52
},
"thought": {
"id": "01AR3WZFV3Q5MZ8...",
"thought_type": "Insight",
"content": "The cache invalidation strategy needs rethinking.",
"importance": 0.8,
"tags": ["architecture", "cache"]
}
}
],
"total_hits": 1,
"query_time_ms": 12
}
Request:
{
"chain_key": "my-project", // optional — omit to use the default chain
"text": "cache consistency",
"limit": 3,
"thought_types": ["Insight", "Finding", "LessonLearned"],
"graph": {
"max_depth": 2,
"max_visited": 20,
"include_seeds": true
}
}
Response:
{
"bundles": [
{
"seed": {
"id": "01AR3WZFV3Q5MZ8...",
"thought_type": "Insight",
"content": "The cache invalidation strategy needs rethinking."
},
"supporting": [
{
"id": "01AR3WZFV3Q5MZ9...",
"thought_type": "Finding",
"content": "Cache coherence protocol was causing 200ms delays.",
"relation": "related"
}
]
}
]
}
| Problem | Solution |
|---|---|
| "Certificate verify failed" errors | Set NODE_TLS_REJECT_UNAUTHORIZED=0 in your mcp-remote config env, or use a valid TLS certificate. |
| "Connection refused" on localhost:9473 | Ensure mentisdbd is running with --https --port 9473. Check with curl https://127.0.0.1:9473/health. |
| mcp-remote not found | Install via Homebrew: brew install mcp-remote, or npm: npm install -g mcp-remote. Requires Node.js >= 20 for npm. |
| Claude Desktop doesn't see mentisdb tools | Restart Claude Desktop after config changes. Verify config at ~/Library/Application Support/Claude/claude_desktop_config.json. |
| Wrong mcp-remote path in config | Use which mcp-remote or brew --prefix mcp-remote to find the correct absolute path. |
| MCP server starts but tools return errors | Check mentisdbd logs. Ensure the chain exists and is accessible. Try mentisdbd status. |
| Problem | Solution |
|---|---|
| 404 on all endpoints | Ensure mentisdbd is running with --enable-http (default port 9472 for HTTP, 9473 for HTTPS). |
| 400 Bad Request on POST | Verify Content-Type is application/json and body is valid JSON. |
| 401/403 auth errors | MentisDB currently does not require auth for local access. If using a reverse proxy, check its auth config. |
mentisdbd ships a built-in MCP server — any MCP-compatible client can connect directly via stdio. To use the MCP server, start mentisdbd and point your MCP client at it:
mentisdbd exposes an MCP server on HTTP port 9471 (and HTTPS on 9473 when TLS is
enabled). MCP clients like Claude Desktop connect over HTTP/HTTPS — not stdio.
The mcp-remote bridge is used when your MCP client speaks stdio but
mentisdbd speaks HTTP/HTTPS, or when you need TLS.
# mentisdbd starts with MCP on port 9471 (HTTP) and 9473 (HTTPS)
mentisdbd
# Your MCP client connects over HTTP/HTTPS to port 9471 or 9473
mcp-remote vs direct HTTP: If your MCP client (Claude Desktop, etc.)
uses stdio-based MCP, use mcp-remote to bridge to mentisdbd's HTTP MCP
endpoint. If you have a client that speaks HTTP MCP directly, you can connect to
port 9471 (HTTP) or 9473 (HTTPS/TLS) without mcp-remote.
MCP communicates over JSON-RPC 2.0 over stdio. Each request has this shape:
{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {
"name": "mentisdb_append",
"arguments": {
"chain_key": "my-project", // omit to use the default chain
"thought_type": "Insight",
"content": "...",
"importance": 0.8
}
}}
Responses come back on stdout as JSON-RPC results. Check the MCP specification for full details.
| Tool | Description |
|---|---|
mentisdb_bootstrap | Create a chain if needed and write one bootstrap checkpoint when it is empty. |
mentisdb_append | Append a durable semantic thought with optional tags, concepts, refs, scope, and signature metadata. |
mentisdb_append_retrospective | Append a retrospective memory intended to prevent future agents from repeating a hard failure. |
mentisdb_search | Search thoughts by semantic filters, identity filters, time bounds, and scoring thresholds. |
mentisdb_lexical_search | Return flat ranked lexical matches with explainable term and field provenance. |
mentisdb_ranked_search | Return flat ranked lexical, graph-aware, or heuristic results with additive score breakdowns. Supports as_of for point-in-time queries and scope for memory scope filtering. |
mentisdb_context_bundles | Return seed-anchored grouped support context beneath the best lexical seeds. |
mentisdb_list_chains | List known chains with version, storage adapter, counts, and storage location. |
mentisdb_merge_chains | Merge all thoughts from a source chain into a target chain, then permanently delete the source. |
mentisdb_branch_from | Create a new chain that diverges from a thought on an existing chain. |
mentisdb_list_agents | List the distinct agent identities participating in one chain. |
mentisdb_get_agent | Return one full agent registry record. |
mentisdb_list_agent_registry | Return the full per-chain agent registry. |
mentisdb_upsert_agent | Create or update a registry record before or after an agent writes thoughts. |
mentisdb_set_agent_description | Set or clear the description stored for one registered agent. |
mentisdb_add_agent_alias | Add a historical or alternate alias to a registered agent. |
mentisdb_add_agent_key | Add or replace one public verification key on a registered agent. |
mentisdb_revoke_agent_key | Revoke one previously registered public key. |
mentisdb_disable_agent | Disable one agent by marking its registry status as revoked. |
mentisdb_recent_context | Render recent thoughts into a prompt snippet for session resumption. |
mentisdb_memory_markdown | Export a MEMORY.md-style Markdown view of the full chain or a filtered subset. |
mentisdb_import_memory_markdown | Import a MEMORY.md-formatted Markdown document into a target chain. |
mentisdb_get_thought | Return one stored thought by stable id, chain index, or content hash. |
mentisdb_get_genesis_thought | Return the first thought ever recorded in the chain. |
mentisdb_traverse_thoughts | Traverse the chain forward or backward in append order from a chosen anchor. |
mentisdb_skill_md | Return the official embedded MENTISDB_SKILL.md Markdown file. |
mentisdb_list_skills | List versioned skill summaries from the skill registry. |
mentisdb_skill_manifest | Return the versioned skill-registry manifest. |
mentisdb_upload_skill | Upload a new immutable skill version from Markdown or JSON. |
mentisdb_search_skill | Search skills by indexed metadata. |
mentisdb_read_skill | Read one stored skill as Markdown or JSON. |
mentisdb_skill_versions | List immutable uploaded versions for one skill. |
mentisdb_deprecate_skill | Mark a skill as deprecated. |
mentisdb_revoke_skill | Mark a skill as revoked. |
mentisdb_head | Return head metadata, latest thought, and integrity state. |
mentisdb_register_webhook | Register a webhook to receive HTTP POST notifications. |
mentisdb_list_webhooks | List all registered webhooks. |
mentisdb_delete_webhook | Remove a webhook registration by UUID. |