Feat/mcp server expose#2539
Open
lianghaofeng wants to merge 3 commits intoOpenMind:mainfrom
Open
Conversation
Add src/expose/ — a stdio MCP server that exposes OM1 tools to external MCP clients (Claude Desktop, Cursor, Windsurf). Tools: - om1_move: 8 actions (forward/backward/turn_left/turn_right/spin/sit/stand/idle) - om1_speak: text (1-500 chars) - om1_face: 5 emotions (joy/smile/ponder/alert/sad) All tools validated via JSON Schema enums, giving the LLM a precise tool contract instead of free-form strings. Architecture: - server.py: MCP wiring and pure handle_tool_call() dispatcher - websim_adapter.py: WebSim interop with fail-fast port conflict check - tools.py: Enum definitions and tool schemas - config.py: Env-var config (OM1_WEBSIM_HOST/PORT, OM1_LOG_LEVEL) Known limitation: WebSim.py hardcodes port 8000 in _run_server (L509). OM1_WEBSIM_PORT is plumbed correctly on our side but won't take effect until WebSim reads its SimulatorConfig. Follow-up PR candidate.
24 tests covering:
- tools: schema structure, enum constraints on om1_move/om1_face,
om1_speak text length bounds
- config: env-var loading and defaults, ValueError on invalid port
- websim_adapter: move/speak/face dispatch via MagicMock, real
socket check for ensure_port_free (pass + raise paths)
- server: handle_tool_call 8 paths — 3 happy paths, enum rejection,
empty-text rejection, unknown tool, KeyError fallback
All tests use real code (no mocking of our own modules); only the
WebSim boundary is mocked via adapter injection.
Declare project.scripts so users can launch the server with a single command after `uv pip install -e .`: om1-mcp-server This simplifies Claude Desktop / Cursor configuration from multi-line (with explicit python path, PYTHONPATH, cwd, and -m module) to a single 'command' field pointing at the generated binary.
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.
Problem
OM1 currently consumes MCP servers (
src/mcp_servers/client.py) but cannot expose itself as one. External MCP clients (Claude Desktop, Cursor, Windsurf) have no standard way to drive the OM1 virtual agent.Solution
Add
src/expose/— a stdio MCP server exposing three tools:om1_moveenum: forward, backward, turn_left, turn_right, spin, sit, stand, idleom1_speakstring, 1–500 charsom1_faceenum: joy, smile, ponder, alert, sadAll tools use JSON Schema
enumconstraints so the LLM sees a precise tool contract instead of free-form strings (validated end-to-end with Claude Desktop — "moonwalk" correctly rejected, LLM falls back tospin).Architecture
server.py— MCP wiring + purehandle_tool_call()dispatcherwebsim_adapter.py— WebSim interop with fail-fast port conflict checktools.py— Enum definitions and tool schemasconfig.py— Env-var config (OM1_WEBSIM_HOST/PORT,OM1_LOG_LEVEL)Split this way so tests can inject a
MagicMockadapter without starting a real WebSim HTTP server.Usage
uv pip install -e . om1-mcp-serverClaude Desktop config becomes one line:
{ "mcpServers": { "om1": { "command": "/absolute/path/.venv/bin/om1-mcp-server" } } }Verification
uv run pytest tests/expose/— all greenpre-commit runon staged files — cleanKnown Limitation (follow-up candidate)
WebSim.py:509-510hardcodeshost="0.0.0.0"andport=8000, ignoring theSimulatorConfigpassed to it.OM1_WEBSIM_PORTis plumbed through correctly on the expose side but won't take effect until WebSim reads its own config. Happy to submit a follow-up PR for that if the team is open to it.Out of Scope
Per CONTRIBUTING.md, I skipped opening an issue first because the change is additive, non-invasive (no existing file touched except
pyproject.tomlto register the CLI), and fully tested. If a discussion thread is preferred, happy to convert this into a draft.