mirror of
https://github.com/himanshudongre/smriti.git
synced 2026-08-28 05:14:59 +00:00
Round 4 validated that the Smriti CLI surface is complete. This is the
next transport: an MCP stdio server that exposes the same operations as
tools inside MCP-aware hosts (Claude Code, Cursor, Windsurf) so agents
can read and write reasoning state natively in their session instead of
shelling out to the `smriti` binary.
The server lives inside the existing CLI package as a sibling to
client.py and main.py. One `pip install -e ./cli` installs both the
`smriti` and `smriti-mcp` console scripts. Architecture is a thin shim:
each tool builds a SmritiClient, calls 1-2 client methods, pipes the
result through an existing formatter, and returns a string. FastMCP
auto-wraps the string into TextContent. Errors raise SmritiToolError
(wrapping SmritiError with HTTP status + structured detail); FastMCP
converts raised exceptions into MCP error responses.
Zero reimplementation of API logic, zero duplicated formatting, zero
changes to client.py, formatters.py, main.py, or the backend.
Twelve tools, 1:1 with the CLI verbs:
smriti_list_spaces smriti_state
smriti_create_space smriti_list_checkpoints
smriti_delete_space smriti_show_checkpoint
smriti_create_checkpoint smriti_review_checkpoint
smriti_delete_checkpoint smriti_restore
smriti_fork smriti_compare
Two deliberate differences from the CLI:
- No `-y` confirmation flag on destructive tools. The MCP host's
tool-approval UI is the gate.
- smriti_create_checkpoint uses the extract path only (no
--from-json mode) and does NOT auto-capture cwd as project_root.
MCP servers run in the host's arbitrary working directory, so
cwd would plant garbage paths. Callers pass project_root
explicitly when they want it populated.
Testing: 33 unit tests across all 12 tools using a
MagicMock(spec=SmritiClient) fixture in tests/conftest.py. Each tool
has at least one happy path and one error path; the complex ones
(smriti_state, smriti_create_checkpoint, smriti_delete_checkpoint)
have extra tests for their branches (no-checkpoints short-circuit,
dry-run, existing-session, 409-with-dependents formatting,
409-with-non-dict-fallback, empty-content pre-check).
End-to-end stdio protocol smoke verified independently: the
`smriti-mcp` binary responds to `initialize` with protocol version
2025-03-26 and returns all 12 tools on `tools/list`. Ready for
`mcp dev smriti_cli.mcp_server:mcp` Inspector UI exploration or
direct Claude Code connection.
cli/README.md gets a new MCP server section with installation,
example Claude Code config, tool list, and notes on the project_root
and confirmation-gate differences from the CLI.
28 lines
933 B
Python
28 lines
933 B
Python
"""Shared pytest fixtures for the smriti-cli test suite.
|
|
|
|
The mock_client fixture lives here (rather than inside test_mcp_server.py)
|
|
so future test files (test_client.py, test_formatters.py, etc.) can reuse
|
|
it without cross-importing.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from unittest.mock import MagicMock
|
|
|
|
import pytest
|
|
|
|
from smriti_cli import mcp_server
|
|
from smriti_cli.client import SmritiClient
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_client(monkeypatch) -> MagicMock:
|
|
"""Patch mcp_server._client() to return a MagicMock(spec=SmritiClient).
|
|
|
|
Returns the mock directly so tests can configure return values and
|
|
assert on method_calls. The spec=SmritiClient argument gives us
|
|
attribute-access validation for free: typing `list_spacse` in a test
|
|
crashes loudly instead of silently passing.
|
|
"""
|
|
client = MagicMock(spec=SmritiClient)
|
|
monkeypatch.setattr(mcp_server, "_client", lambda: client)
|
|
return client
|