Summary
Add an MCP server to Emanote that runs in the same long-lived process as the live server, so MCP clients can query Emanote's in-memory notebook model directly instead of going through the filesystem.
We will use the Haskell dpella/mcp library for protocol and transport support, while keeping Emanote's own model/query/export code as the source of truth.
Goals
- Run live server and MCP server in the same process
- Reuse the existing reactive Emanote model
- Expose notebook data primarily as MCP resources
- Add a small set of read-only tools for structured queries
- Ship this incrementally, one PR per phase
Non-goals
- No write/edit tools in the first iteration
- No separate notebook parser for MCP
- No filesystem-first MCP layer that bypasses Emanote's model
Design direction
- Ema remains responsible for keeping the in-memory model live and for normal site rendering.
- MCP sits beside Ema, sharing the same current model snapshot.
- MCP should not be forced through
SiteRoute/Ema.Asset; it should read from Emanote's model/query/export layer directly.
- Prefer MCP resources for notebook reads; use tools only for query-shaped operations.
Phases
Phase 1: Same-process MCP transport + minimal handshake — ✅ Shipped in #648
Scope:
- Add
dpella/mcp dependency
- Add CLI/config plumbing to enable MCP HTTP transport alongside live server
- Start MCP endpoint in the same long-running process as the live server
- Implement minimum lifecycle/RPC surface:
initialize
resources/list
resources/read
tools/list
tools/call
- Keep everything read-only
Acceptance criteria:
- Emanote live server and MCP endpoint both start in one process
- An MCP client can complete initialization and list resources/tools
- No resource subscriptions yet
Phase 2: Core read-only resources
Scope:
- Expose notebook state as MCP resources:
emanote://export/metadata
emanote://export/content
emanote://note/{path}
- Back these with existing Emanote export/model code
- Add resource templates where appropriate
Acceptance criteria:
- Clients can read full-notebook metadata/content without going through the filesystem abstraction
- Clients can fetch an individual note by path/route
Phase 3: Query tools
Scope:
- Add a small set of read-only MCP tools:
find_notes
get_backlinks
resolve_wikilink
- Back these with existing Emanote model/query/graph code
- Keep input/output schemas narrow and predictable
Acceptance criteria:
- Clients can ask structured questions without pulling the whole export blob
- Tool results are derived from the live model
Phase 4: Live update support
Scope:
- Add resource subscriptions for relevant URIs
- Emit MCP notifications when resources change
- Support at least:
- note resources
- export resources
Acceptance criteria:
- An MCP client subscribed to a note/export resource can observe updates after notebook changes
Phase 5: Optional prompt surfaces
Scope:
- Add
prompts/list / prompts/get only if they prove useful
- Possible prompts:
- summarize note + backlinks
- answer using notebook only
- compare two notes
Acceptance criteria:
- Prompts are optional sugar on top of the resource/tool API, not a replacement for it
Suggested PR breakdown
- PR 1: runtime wiring + minimal MCP lifecycle
- PR 2: notebook resources
- PR 3: query tools
- PR 4: subscriptions/notifications
- PR 5: optional prompts
Open questions
- Exact CLI shape: separate subcommand vs
emanote run --mcp-http
- Best way to share the current model snapshot between the Ema live server and MCP handlers
- Whether authentication should be deferred until after the local same-process workflow is stable
Notes
The existing Emanote exports and model/query layers already provide most of the read-side surface we need; the main architectural work is sharing the live model cleanly between Ema and the MCP server runtime.
Summary
Add an MCP server to Emanote that runs in the same long-lived process as the live server, so MCP clients can query Emanote's in-memory notebook model directly instead of going through the filesystem.
We will use the Haskell
dpella/mcplibrary for protocol and transport support, while keeping Emanote's own model/query/export code as the source of truth.Goals
Non-goals
Design direction
SiteRoute/Ema.Asset; it should read from Emanote's model/query/export layer directly.Phases
Phase 1: Same-process MCP transport + minimal handshake — ✅ Shipped in #648
Scope:
dpella/mcpdependencyinitializeresources/listresources/readtools/listtools/callAcceptance criteria:
Phase 2: Core read-only resources
Scope:
emanote://export/metadataemanote://export/contentemanote://note/{path}Acceptance criteria:
Phase 3: Query tools
Scope:
find_notesget_backlinksresolve_wikilinkAcceptance criteria:
Phase 4: Live update support
Scope:
Acceptance criteria:
Phase 5: Optional prompt surfaces
Scope:
prompts/list/prompts/getonly if they prove usefulAcceptance criteria:
Suggested PR breakdown
Open questions
emanote run --mcp-httpNotes
The existing Emanote exports and model/query layers already provide most of the read-side surface we need; the main architectural work is sharing the live model cleanly between Ema and the MCP server runtime.