Expose the tool layer over MCP - #8
Merged
Merged
Conversation
SessionIQ now speaks the Model Context Protocol, so any MCP client — Claude Desktop, Cursor, an agent of your own — can query a local library. It exposes the same seven tools the in-app assistant calls and delegates to the same LibraryToolbox, so a question answered here and the same question asked in the app are computed identically. That logic is already covered by evals/. The SDK is an optional extra. It is imported lazily so the base install is unaffected, and the entry point reports a missing extra rather than raising a protocol error. Retrieval runs in memory rather than through ChromaDB: this is a read-only query surface that should start instantly and must not need an embedding model, and the lexical-plus-metadata blend scored 0.733 hit@1 against 0.767 with vectors. Building this surfaced a bug in the shared tool path. execute() truncated over-long results by chopping the serialised string and appending a marker, which produced invalid JSON. The model coped with it; an MCP client would simply fail to parse. Payloads are now trimmed structurally — whole list items first, then oversized strings — so the JSON stays valid, and a payload that still cannot fit reports that instead of emitting something malformed.
Thirteen tests cover library loading, toolbox wiring, the exposed tool set, and that each MCP tool delegates rather than reimplementing. They skip cleanly when the optional SDK is absent, and CI installs it so the surface stays covered. Five more cover the trimming, including the case that used to emit invalid JSON: a filter over sixty assets, parsed through the same helper the app uses.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
SessionIQ now speaks the Model Context Protocol, so any MCP client — Claude Desktop, Cursor, or an agent of your own — can query a local library.
What it exposes
The same seven tools the in-app assistant calls:
list_projects,search_library,filter_assets,compute_stat,asset_details,similar_tracksandnext_up.Each one delegates to the same
LibraryToolbox, so a question answered through MCP and the same question asked inside the app are computed by identical code. That logic is whatevals/already measures — the tool layer scores 7/7 — so this adds a new surface without adding a second implementation to maintain.Design decisions
The SDK is an optional extra (
pip install -e ".[mcp]"), imported lazily, so the base install is unaffected. The entry point reports a missing extra in plain language rather than letting a protocol error surface.stdio transport. MCP servers for local tools normally speak stdio, which fits a local-first app exactly: no port, no network exposure, the client launches it.
Retrieval runs in memory, not through ChromaDB. This is a read-only query surface that should start instantly and must not need an embedding model. The lexical-plus-metadata blend scored 0.733 hit@1 against 0.767 with vectors — not worth a model download here. Documented in the module.
Read-only, and reads at startup. It answers questions and changes nothing. It sees the library as of when the client launched it, which is stated in the README so nobody is surprised that a file added mid-session is missing.
A bug this surfaced in the shared path
LibraryToolbox.execute()truncated over-long results by chopping the serialised string and appending'…"}', which produced invalid JSON. The model coped with it; an MCP client would simply fail to parse it, and a largefilter_assetsover a real library hits this.Payloads are now trimmed structurally — whole list items first, then oversized strings — so the JSON stays valid, and a payload that still cannot fit reports that rather than emitting something malformed. This fixes the in-app path too.
Verification
ruffcleancompute_stat(rms_db, max)→loud.wav @ -9.0 dB,filter_assets(status=Ready)→ 1 match,list_projects→ both projects.Note
The SDK is at version 2.x, where
FastMCPwas renamed toMCPServerand moved tomcp.server.mcpserver. This is built against the current API rather than the superseded one.