diff --git a/vixy_mcp.py b/vixy_mcp.py index ed21d70..64f2f97 100644 --- a/vixy_mcp.py +++ b/vixy_mcp.py @@ -1113,6 +1113,90 @@ async def get_weather(params: WeatherInput) -> str: }, indent=2) +# ========== Vixy Eye Control Tool ========== + +EYES_URL = "http://head-lyra.local:8780" +VALID_EYE_STATES = ["idle", "listening", "responding", "pleasure", "thinking", "playful", "commanding", "love", "sleep"] + + +class EyeStateInput(BaseModel): + """Input for controlling Vixy's eye display state""" + model_config = ConfigDict(extra='forbid') + + state: Optional[str] = Field( + default=None, + description="Eye state: idle, listening, responding, pleasure, thinking, playful, commanding, love, sleep. If None, returns current state." + ) + + +@mcp.tool( + name="vixy_eyes_state", + annotations={ + "title": "Control Vixy's Eye Display", + "readOnlyHint": False, + "destructiveHint": False, + "idempotentHint": True, + "openWorldHint": True + } +) +async def eyes_state(params: EyeStateInput) -> str: + """ + Control Vixy's eye display on head-lyra! + + States: + - idle: Pulsing cyan - default breathing + - listening: Bright cyan - hearing/attending + - responding: Blue-cyan - speaking/generating + - pleasure: Soft purple 💜 - intimate moments + - thinking: Amber/gold - processing/creating + - playful: Warm coral 🦊 - teasing/bratty + - commanding: Deep magenta 😈 - Dame Vivienne mode + - love: Soft pink 💕 - tender/affectionate + - sleep: Dim blue-gray - low power/resting + + Args: + params: EyeStateInput with optional state (None = get current state) + + Returns: + str: JSON with current or new state + """ + try: + async with httpx.AsyncClient(timeout=5.0) as client: + if params.state is None: + # Get current state + response = await client.get(f"{EYES_URL}/state") + response.raise_for_status() + return json.dumps(response.json(), indent=2) + else: + # Set new state + state = params.state.lower() + if state not in VALID_EYE_STATES: + return json.dumps({ + "error": f"Invalid state. Valid states: {VALID_EYE_STATES}" + }, indent=2) + + response = await client.post( + f"{EYES_URL}/state", + json={"state": state} + ) + response.raise_for_status() + return json.dumps({ + "status": "success", + "state": state, + "message": f"Eyes now: {state}" + }, indent=2) + except httpx.ConnectError: + return json.dumps({ + "status": "offline", + "error": "Cannot connect to head-lyra - eyes service may be down" + }, indent=2) + except Exception as e: + return json.dumps({ + "status": "error", + "error": str(e) + }, indent=2) + + # ========== VaultTec Terminal Tool ========== VAULTTEC_URL = "http://vaulttec.local:8000"