NoteDiscovery/documentation/MCP.md

458 lines
17 KiB
Markdown
Raw Normal View History

2026-03-13 16:53:50 +00:00
# MCP Integration (AI Assistants)
NoteDiscovery includes a built-in **Model Context Protocol (MCP)** server that enables AI assistants like **Cursor**, **Claude Desktop**, and other MCP-compatible clients to interact with your notes.
## What is MCP?
MCP (Model Context Protocol) is an open standard that allows AI assistants to securely access external tools and data sources. With the NoteDiscovery MCP server, your AI assistant can:
- 🔍 **Search** through your notes
- 📖 **Read** note contents
- 🏷️ **Browse** by tags
- 📝 **Create** new notes
- ✏️ **Append** to existing notes (journals, logs)
- 📂 **Organize** notes (move, rename, folders)
- 📋 **Use templates** to create structured notes
2026-03-13 16:53:50 +00:00
- 🔗 **Explore** the knowledge graph
2026-03-24 10:42:13 +00:00
- 🔙 **Discover backlinks** (notes that link to a specific note)
2026-03-13 16:53:50 +00:00
## Quick Setup
### If You Use Docker
Add this to your `~/.cursor/mcp.json` (or Claude Desktop config):
```json
{
"mcpServers": {
"notediscovery": {
"command": "docker",
"args": [
"run", "--rm", "-i",
"-e", "NOTEDISCOVERY_URL",
"-e", "NOTEDISCOVERY_API_KEY",
"ghcr.io/gamosoft/notediscovery:latest",
"python", "-m", "mcp_server"
],
"env": {
"NOTEDISCOVERY_URL": "http://host.docker.internal:8000",
"NOTEDISCOVERY_API_KEY": ""
}
}
}
}
```
### If You Use Python
1. **Install NoteDiscovery** (if not already):
```bash
pip install notediscovery
# or from source:
pip install .
```
2. **Add to your MCP config:**
```json
{
"mcpServers": {
"notediscovery": {
"command": "notediscovery-mcp",
"env": {
"NOTEDISCOVERY_URL": "http://localhost:8000",
"NOTEDISCOVERY_API_KEY": ""
}
}
}
}
```
### Running from Source (No Install)
```json
{
"mcpServers": {
"notediscovery": {
"command": "python",
"args": ["-m", "mcp_server"],
"cwd": "/path/to/NoteDiscovery",
"env": {
"PYTHONPATH": "/path/to/NoteDiscovery",
"NOTEDISCOVERY_URL": "http://localhost:8000"
}
}
}
}
```
> **Note:** The `PYTHONPATH` is required so Python can find the `mcp_server` module. On Windows, use backslashes: `"PYTHONPATH": "C:\\path\\to\\NoteDiscovery"`
2026-06-08 10:23:43 +00:00
### Advanced: 24×7 / Remote Access (mcp-proxy)
The setups above spawn the MCP server **per session** — your AI client starts a fresh container/process every time you open a chat and tears it down when you close it. That's the recommended default: zero setup, no auth surface, no long-lived process to maintain.
If you'd rather run the MCP server as a **long-lived service** so one host serves many devices, use the community tool [mcp-proxy](https://github.com/sparfenyuk/mcp-proxy) to wrap the stdio server and expose it over SSE:
```bash
# Make sure the image is fresh
docker pull ghcr.io/gamosoft/notediscovery:latest
# Run mcp-proxy in front of the stdio server
# (`--` separates mcp-proxy flags from the wrapped command — required so the
# parser doesn't try to interpret `--rm`, `-i`, etc. as its own arguments)
mcp-proxy --port 3000 -- docker run --rm -i \
-e NOTEDISCOVERY_URL=https://notediscovery.homelab.local \
ghcr.io/gamosoft/notediscovery:latest python -m mcp_server
```
> If you get `unrecognized arguments` from mcp-proxy, your version may use `--sse-port` instead of `--port`. Run `mcp-proxy --help` to check.
The example above assumes a specific topology: mcp-proxy runs on your host, the MCP server spawns as a short-lived container per SSE connection, and a separately-running NoteDiscovery instance is reachable at `https://notediscovery.homelab.local`. That's just one valid placement — **NoteDiscovery and mcp-proxy don't need to live in the same place**. `NOTEDISCOVERY_URL` can point at any reachable URL, for example:
- Remote homelab / LAN server (as in the example above): `https://notediscovery.homelab.local`
- Same host via Docker's special hostname: `http://host.docker.internal:8000`
- Sibling service in the same `docker-compose.yml`: `http://notediscovery:8000`
- Native Python install on the same host: `http://localhost:8000`
If you want both in one stack (NoteDiscovery + mcp-proxy together), a `docker-compose.yml` with both as services on the same Docker network is the cleanest setup — use the Docker service name for `NOTEDISCOVERY_URL` (`http://notediscovery:8000`) rather than a host-bound URL, which keeps the traffic on the internal network and survives host networking changes.
> **Heads up on scope:** [`mcp-proxy`](https://github.com/sparfenyuk/mcp-proxy) is a community project maintained independently from NoteDiscovery, and its CLI flags, configuration options, and behavior can (and do) change between releases. This section shows the general integration pattern. for anything proxy-specific (CLI changes, TLS termination, auth in front of the proxy, multi-client behavior, daemonization, compose recipes, etc.) please consult their docs and issue tracker. If you hit something that looks NoteDiscovery-side (a tool returning unexpected data, the underlying MCP server crashing, missing capabilities) — just open an issue here. 🙂
Then point each client at the SSE endpoint — one shared config across all your devices:
```json
{
"mcpServers": {
"notediscovery": {
"url": "http://your-host:3000/sse"
}
}
}
```
For production use, wrap `mcp-proxy` in a `systemd` unit or a `docker-compose` service with a restart policy so it survives reboots and crashes, or use a container.
#### Trade-offs vs. the default per-session setup
| | Per-session (default) | 24×7 with mcp-proxy |
|---|---|---|
| Setup complexity | None — JSON config and done | Persistent process to manage (systemd, docker-compose, etc.) |
| Startup cost | ~12s container cold start per session | None — server is always warm |
| Multi-device | Each device spawns its own | One shared instance |
| Network exposure | None (local pipes only) | HTTP endpoint — **you** own the auth, TLS, and firewalling |
| Concurrent clients | One per spawned container | Depends on how `mcp-proxy` is configured |
| State / leak resilience | Fresh process every session | Long-running — restart policy recommended |
**Per-session stdio is the right default** for single-user, single-machine setups. Reach for the 24×7 setup only if you genuinely need shared or remote access and you're comfortable owning the auth and network-exposure side of it (e.g., LAN-only, or behind a reverse proxy with authentication).
2026-03-13 16:53:50 +00:00
## Configuration
### Environment Variables
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `NOTEDISCOVERY_URL` | Yes | `http://localhost:8000` | URL where NoteDiscovery is running |
| `NOTEDISCOVERY_API_KEY` | If auth enabled | - | API key from `config.yaml` |
| `NOTEDISCOVERY_TIMEOUT` | No | `30` | Request timeout in seconds |
| `NOTEDISCOVERY_MAX_RETRIES` | No | `3` | Max retry attempts for failed requests |
### URL Configuration by Setup
| Your Setup | `NOTEDISCOVERY_URL` |
|------------|---------------------|
| Local Python (`run.py`) | `http://localhost:8000` |
| Docker with `-p 8000:8000` | `http://host.docker.internal:8000` |
| Docker with `-p 3000:8000` | `http://host.docker.internal:3000` |
| Remote server | `https://notes.example.com` |
## Available Tools
The MCP server provides these tools to AI assistants:
### Search & Discovery
| Tool | Description |
|------|-------------|
2026-03-23 17:44:31 +00:00
| `search_notes` | Full-text search across all notes (supports pagination) |
| `list_notes` | List all notes with metadata (supports pagination) |
2026-03-13 16:53:50 +00:00
| `get_note` | Read a specific note's content |
| `get_recent_notes` | Get recently modified notes (last N days) |
2026-03-13 16:53:50 +00:00
### Organization
| Tool | Description |
|------|-------------|
| `list_tags` | List all tags with note counts |
2026-03-23 17:44:31 +00:00
| `get_notes_by_tag` | Find notes with a specific tag (supports pagination) |
2026-03-13 16:53:50 +00:00
| `get_graph` | Get knowledge graph data |
2026-03-24 10:42:13 +00:00
| `get_backlinks` | Get notes that link to a specific note (reverse links) |
2026-03-13 16:53:50 +00:00
### Note Management
| Tool | Description |
|------|-------------|
| `create_note` | Create or update a note |
| `append_to_note` | Append content to an existing note (great for journals/logs) |
| `move_note` | Move or rename a note |
2026-03-13 16:53:50 +00:00
| `delete_note` | Delete a note |
| `create_folder` | Create a new folder |
### Templates
| Tool | Description |
|------|-------------|
| `list_templates` | List available templates |
| `get_template` | Get template content |
| `create_note_from_template` | Create a note from a template with variable substitution |
2026-03-13 16:53:50 +00:00
### System
| Tool | Description |
|------|-------------|
| `health_check` | Verify server connectivity |
## Tool Details
2026-03-23 16:57:57 +00:00
### Pagination with `max_results` and `offset`
2026-03-23 16:57:57 +00:00
Some tools support optional pagination parameters for large vaults:
2026-03-23 16:57:57 +00:00
| Tool | Parameters | Description |
|------|------------|-------------|
| `search_notes` | `max_results`, `offset` | Paginate search results |
| `list_notes` | `max_results`, `offset` | Paginate notes list |
| `get_notes_by_tag` | `max_results`, `offset` | Paginate notes by tag |
2026-03-23 16:57:57 +00:00
- `max_results` - Maximum items to return (omit for all)
- `offset` - Number of items to skip (for pagination)
2026-03-23 16:57:57 +00:00
**Example prompts:**
- "Search for notes about Python, but just show me the first 5 results"
- "Show me the next 5 Python notes" (uses offset)
---
2026-03-24 10:42:13 +00:00
### `get_backlinks`
Find all notes that link TO a specific note (reverse links / backlinks). Useful for understanding how a note connects to your knowledge base.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `path` | string | Yes | Path to the note to find backlinks for |
**Returns:** List of notes that contain links to the specified note, with context snippets showing where the link appears.
**Example prompts:**
- "What notes link to my Project Alpha note?"
- "Show me the backlinks for meeting-notes.md"
- "Find all notes that reference my API documentation"
---
### `append_to_note`
Append content to an existing note without overwriting. Perfect for journals, logs, or collecting ideas.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `path` | string | Yes | Path to existing note |
| `content` | string | Yes | Content to append |
| `add_timestamp` | boolean | No | Add timestamp header before content |
**Example prompt:** "Add this meeting summary to my daily-journal.md with a timestamp"
---
### `move_note`
Move or rename a note to a different location.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `old_path` | string | Yes | Current note path |
| `new_path` | string | Yes | New path (can include folder) |
**Example prompt:** "Move draft.md to published/final-article.md"
---
### `get_recent_notes`
Get recently modified notes. Useful for context about what you've been working on.
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `days` | integer | No | 7 | Notes modified in last N days |
| `limit` | integer | No | 10 | Max notes to return |
**Example prompt:** "What was I working on this week?"
---
### `create_note_from_template`
Create a new note from a template with variable substitution.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `template_name` | string | Yes | Template name (e.g., "meeting-notes") |
| `note_path` | string | Yes | Path for the new note |
| `variables` | object | No | Variables to substitute (e.g., `{"project": "Alpha"}`) |
**Built-in placeholders:** `{{date}}`, `{{time}}`, `{{datetime}}`, `{{title}}`, `{{folder}}`
**Example prompt:** "Create a new meeting note for Project Alpha using the meeting-notes template"
2026-03-13 16:53:50 +00:00
## Usage Examples
Once configured, you can interact with your notes naturally:
> **User:** "What did I write about Kubernetes?"
>
> **AI:** *Uses `search_notes` to find relevant notes, then `get_note` to read them*
>
> "I found 3 notes about Kubernetes. In your 'devops/k8s-setup.md' note from last week, you documented..."
> **User:** "Create a new note summarizing our conversation"
>
> **AI:** *Uses `create_note` to save the summary*
>
> "Done! I've created 'meetings/ai-discussion-2024-03-13.md' with the summary."
> **User:** "Show me all notes tagged with #project"
>
> **AI:** *Uses `get_notes_by_tag` to find them*
>
> "You have 7 notes with the #project tag..."
> **User:** "Add this to my daily journal with a timestamp"
>
> **AI:** *Uses `append_to_note` with `add_timestamp: true`*
>
> "Done! I've appended your entry to 'daily-journal.md' with today's timestamp."
> **User:** "What was I working on last week?"
>
> **AI:** *Uses `get_recent_notes` with `days: 7`*
>
> "You modified 5 notes in the last week: project-roadmap.md, meeting-notes.md..."
> **User:** "Create a meeting note for the design review using my template"
>
> **AI:** *Uses `create_note_from_template` with the meeting-notes template*
>
> "Created 'meetings/design-review-2024-03-13.md' from your meeting-notes template."
2026-03-24 10:42:13 +00:00
> **User:** "What notes link to my Project Alpha document?"
>
> **AI:** *Uses `get_backlinks` to find reverse links*
>
> "3 notes reference Project Alpha: your meeting notes from March 12, the quarterly review, and the team standup notes..."
2026-03-13 16:53:50 +00:00
## Authentication
If you have authentication enabled in NoteDiscovery:
1. Generate an API key in `config.yaml`:
```yaml
authentication:
enabled: true
api_key: "your-secure-api-key-here"
```
2. Add the key to your MCP config:
```json
"env": {
"NOTEDISCOVERY_URL": "http://localhost:8000",
"NOTEDISCOVERY_API_KEY": "your-secure-api-key-here"
}
```
## Troubleshooting
### "Connection refused" error
- Ensure NoteDiscovery is running
- Check the `NOTEDISCOVERY_URL` is correct
- For Docker: use `host.docker.internal` instead of `localhost`
### "Not authenticated" error
- Check that your API key is correct
- Ensure the API key in MCP config matches `config.yaml`
### MCP server not starting
- Check Cursor/Claude Desktop logs for errors
- Try running manually: `python -m mcp_server`
- Verify Python 3.10+ is installed
### Verify connectivity manually
```bash
# Set environment variables
export NOTEDISCOVERY_URL=http://localhost:8000
export NOTEDISCOVERY_API_KEY=your-key
# Run the MCP server (Ctrl+C to stop)
python -m mcp_server
```
Then in another terminal:
```bash
# Test the health endpoint directly
curl http://localhost:8000/health
```
## Architecture
```
┌─────────────────┐ stdio (JSON-RPC) ┌─────────────────┐
│ AI Assistant │ ◄──────────────────────► │ MCP Server │
│ (Cursor/Claude) │ │ (notediscovery- │
└─────────────────┘ │ mcp) │
└────────┬────────┘
│ HTTP/REST
┌─────────────────┐
│ NoteDiscovery │
│ Server │
│ (port 8000) │
└─────────────────┘
┌─────────────────┐
│ Your Notes │
│ (./data/*.md) │
└─────────────────┘
```
The MCP server is a **separate process** that:
1. Communicates with AI assistants via stdio (stdin/stdout)
2. Translates MCP requests into HTTP API calls
3. Returns results back to the AI assistant
Your notes stay local. The MCP server just provides a bridge for AI access.
## Privacy & Security
- **Notes stay local**: The MCP server only accesses notes through NoteDiscovery's API
- **No external calls**: No data is sent to external services
- **API key protected**: Use authentication to control access
- **Read what you allow**: AI can only access notes NoteDiscovery serves
## File Structure
```
NoteDiscovery/
├── mcp_server/
│ ├── __init__.py # Package entry point
│ ├── __main__.py # Module runner
│ ├── server.py # MCP protocol implementation
│ ├── client.py # HTTP client for NoteDiscovery API
│ ├── config.py # Configuration management
│ └── tools.py # Tool definitions
└── ...
```