get_recent_touch_events
Retrieve recent keyboard and mouse activity events with timestamps, event types, aggregate counts, and active app. Analyze input patterns, detect idle periods, or time activities. Privacy: no keystroke contents are stored.
Instructions
Return the most recent N keyboard and mouse activity events.
Returns events with timestamp, type (key/click/scroll), aggregate counts (not individual keystrokes — content is not logged), and active app.
USE WHEN: analyzing input patterns, idle detection, or activity timing. NOT FOR: keylogging — actual keystroke contents are NEVER stored, only aggregate event metadata.
BEHAVIOR: pure read. Privacy guarantee: no key contents, no clipboard targets, no scroll positions inside sensitive apps.
PARAMETERS: n: number of events. Range 1-500. Default 50.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| n | No |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |
Implementation Reference
- Primary handler implementation of get_recent_touch_events tool. Decorated with @mcp_app.tool(), it queries an SQLite activity database for recent keyboard/mouse events (typing bursts, clicks, scrolls, drags, corrections) within a configurable time window and event type filter, returning a formatted string.
@mcp_app.tool() def get_recent_touch_events(seconds: int = 300, event_types: str = "all") -> str: """Get recent keyboard and mouse activity events. Returns typing bursts (word counts, WPM), clicks, scrolls, and drags. Privacy-safe: shows activity patterns, not keystrokes. Args: seconds: How many seconds back to look (default 300 = 5 min). event_types: Filter — "all", "keyboard", "mouse", or "corrections". """ seconds = max(1, min(seconds, 86400)) # cap at 24h valid_types = {"all", "keyboard", "mouse", "corrections"} if event_types not in valid_types: return f"Invalid event_types '{event_types}'. Must be one of: {', '.join(sorted(valid_types))}" conn = _get_db() if not conn: return "No activity database found. Touch module may not have been started yet." try: cutoff = time.time() - seconds modality_filter = "" if event_types == "keyboard": modality_filter = "AND modality = 'keys'" elif event_types == "mouse": modality_filter = "AND modality = 'flow'" elif event_types == "corrections": modality_filter = "AND event_type = 'correction_detected'" rows = conn.execute( f"SELECT timestamp, modality, event_type, app_name, payload FROM events " f"WHERE (modality IN ('keys', 'flow')) {modality_filter} " f"AND timestamp > ? ORDER BY timestamp DESC LIMIT 50", (cutoff,), ).fetchall() conn.close() if not rows: return f"No touch events in the last {seconds} seconds." lines = [f"=== Recent Touch Events (last {seconds}s) ===\n"] for row in rows: payload = json.loads(row["payload"]) ts = time.strftime("%H:%M:%S", time.localtime(row["timestamp"])) et = row["event_type"] app = row["app_name"] or "" if et == "typing_burst": chars = payload.get("char_count", 0) wpm = payload.get("wpm", 0) bs = payload.get("backspace_count", 0) lines.append(f"[{ts}] BURST {chars} chars, {wpm} WPM, {bs} backspaces") elif et == "click": btn = payload.get("button", "?") x, y = payload.get("x", 0), payload.get("y", 0) lines.append(f"[{ts}] CLICK {btn} ({x},{y}) in {app}") elif et == "scroll": dy = payload.get("dy", 0) lines.append(f"[{ts}] SCROLL dy={dy} in {app}") elif et == "drag": lines.append(f"[{ts}] DRAG in {app}") elif et == "correction_detected": orig = payload.get("original_text", "?") corr = payload.get("corrected_text", "?") conf = payload.get("confidence", 0) lines.append(f"[{ts}] CORRECTION: {orig!r} -> {corr!r} (conf={conf:.0%})") else: lines.append(f"[{ts}] {et} in {app}") return "\n".join(lines) except Exception as e: return f"Error reading touch events: {e}" - packages/touch/src/contextpulse_touch/mcp_server.py:31-32 (registration)Registration via @mcp_app.tool() decorator on the FastMCP instance, making get_recent_touch_events available as an MCP tool.
@mcp_app.tool() def get_recent_touch_events(seconds: int = 300, event_types: str = "all") -> str: - Input schema defined via function signature: seconds (int, default 300) and event_types (str, default 'all') with validation constraints (1-86400 seconds, one of 'all'/'keyboard'/'mouse'/'corrections').
def get_recent_touch_events(seconds: int = 300, event_types: str = "all") -> str: """Get recent keyboard and mouse activity events. Returns typing bursts (word counts, WPM), clicks, scrolls, and drags. Privacy-safe: shows activity patterns, not keystrokes. Args: seconds: How many seconds back to look (default 300 = 5 min). event_types: Filter — "all", "keyboard", "mouse", or "corrections". """ seconds = max(1, min(seconds, 86400)) # cap at 24h valid_types = {"all", "keyboard", "mouse", "corrections"} if event_types not in valid_types: return f"Invalid event_types '{event_types}'. Must be one of: {', '.join(sorted(valid_types))}" - Helper function _get_db() that connects to the SQLite activity database at ACTIVITY_DB_PATH, returning None if the DB doesn't exist yet.
def _get_db() -> sqlite3.Connection | None: if not _DB_PATH.exists(): return None conn = sqlite3.connect(str(_DB_PATH), timeout=5) conn.row_factory = sqlite3.Row return conn - glama/server.py:614-631 (handler)Secondary/glama stub handler for get_recent_touch_events — returns a local-only message placeholder (does not contain the real implementation).
@mcp_app.tool() def get_recent_touch_events(n: int = 50) -> str: """Return the most recent N keyboard and mouse activity events. Returns events with timestamp, type (key/click/scroll), aggregate counts (not individual keystrokes — content is not logged), and active app. USE WHEN: analyzing input patterns, idle detection, or activity timing. NOT FOR: keylogging — actual keystroke contents are NEVER stored, only aggregate event metadata. BEHAVIOR: pure read. Privacy guarantee: no key contents, no clipboard targets, no scroll positions inside sensitive apps. PARAMETERS: n: number of events. Range 1-500. Default 50. """ return _LOCAL_ONLY_MSG