# MCP Servers — Timmy's Perception & Action Layer Two off-the-shelf MCP servers replace all custom perception and action code. Zero lines of infrastructure. `pip install` and config. ## Architecture ``` Ollama (DPO model) ↓ tool_calls (Hermes protocol) ↓ MCP Client (heartbeat loop) ├── steam-info-mcp → game perception (playtime, achievements, friends) └── mcp-pyautogui → desktop action (screenshot, keypress, mouse) ``` The heartbeat loop is the MCP client. It: 1. Calls `tools/list` on each MCP server at startup to discover available tools 2. Passes tool schemas to Ollama via the `tools` parameter 3. When the model returns `tool_calls`, executes them via `tools/call` on the right server 4. Feeds results back to the model as `tool` role messages ## Servers ### steam-info-mcp (#545) **What:** Steam Web API exposed as MCP tools. Timmy can see what games are installed, what's been played recently, achievements, friends, news. **Package:** [steam-info-mcp](https://pypi.org/project/steam-info-mcp/) **Tools available:** | Tool | Description | |------|-------------| | `steam-owned-games` | List all owned games | | `steam-recently-played` | Recently played games + hours | | `steam-player-achievements` | Achievements for a game | | `steam-user-stats` | Player stats for a game | | `steam-current-players` | Live player count for a game | | `steam-news` | Latest news for a game | | `steam-player-summaries` | Player profile info | | `steam-friend-list` | Friends list | | `steam-level` | Steam level | | `steam-badges` | Badge collection | **Requires:** `STEAM_API_KEY` env var. Get one at https://steamcommunity.com/dev/apikey **Run:** `steam-info-mcp` (stdio transport) ### mcp-pyautogui (#546) **What:** Desktop control via PyAutoGUI exposed as MCP tools. This IS the `execute_action()` implementation — no wrapper needed. **Package:** [mcp-pyautogui](https://pypi.org/project/mcp-pyautogui/) **Tools available:** | Tool | Description | |------|-------------| | `take_screenshot` | Capture screen to file | | `click` | Left-click at (x, y) | | `right_click` | Right-click at (x, y) | | `double_click` | Double-click at (x, y) | | `move_to` | Move mouse to (x, y) | | `drag_to` | Drag mouse to (x, y) | | `type_text` | Type a string | | `press_key` | Press a single key | | `hotkey` | Key combo (e.g., "ctrl c") | | `scroll` | Scroll up/down | | `get_mouse_position` | Current mouse (x, y) | | `get_screen_size` | Screen resolution | | `pixel_color` | RGB at pixel (x, y) | | `get_os` | Current OS name | **Requires:** macOS Accessibility permissions for Terminal / Python process. System Settings → Privacy & Security → Accessibility. **Run:** `mcp-pyautogui` (stdio transport) ## Setup ```bash cd ~/.timmy/timmy-config/mcp bash setup.sh ``` ## How Ollama Connects Both servers communicate over **stdio** — they read JSON-RPC from stdin and write to stdout. The heartbeat loop spawns each server as a subprocess and talks to it over pipes. Ollama's native tool-calling works like this: ```python import ollama import subprocess, json # 1. Spawn MCP server proc = subprocess.Popen( ["mcp-pyautogui"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) # 2. Discover tools (JSON-RPC over stdio) request = {"jsonrpc": "2.0", "id": 1, "method": "tools/list"} proc.stdin.write(json.dumps(request).encode() + b"\n") proc.stdin.flush() tools = json.loads(proc.stdout.readline()) # 3. Pass tool schemas to Ollama response = ollama.chat( model="timmy:v0.2-dpo", messages=[{"role": "user", "content": "Take a screenshot"}], tools=[...convert MCP tools to Ollama format...] ) # 4. Execute tool calls via MCP for call in response["message"]["tool_calls"]: mcp_request = { "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": call["function"]["name"], "arguments": call["function"]["arguments"]} } proc.stdin.write(json.dumps(mcp_request).encode() + b"\n") proc.stdin.flush() result = json.loads(proc.stdout.readline()) ``` This is pseudocode. The actual heartbeat loop (#547) will be ~30 lines of glue connecting Ollama's tool-calling API to MCP's stdio protocol. No custom infrastructure. ## What We Don't Own - Steam API integration → `steam-info-mcp` (beta/steam-info-mcp on GitHub) - Desktop automation → `mcp-pyautogui` (PyAutoGUI wrapper) - MCP protocol → JSON-RPC 2.0 over stdio (industry standard) - Tool calling → Ollama native (Hermes protocol) - Model serving → Ollama ## What We Own - `servers.json` — which servers to run and their env vars - `setup.sh` — one-command install - This README