Add vixy_eyes_state tool - control eye display from MCP 🦊👁️
This commit is contained in:
84
vixy_mcp.py
84
vixy_mcp.py
@@ -1113,6 +1113,90 @@ async def get_weather(params: WeatherInput) -> str:
|
|||||||
}, indent=2)
|
}, 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 Terminal Tool ==========
|
||||||
|
|
||||||
VAULTTEC_URL = "http://vaulttec.local:8000"
|
VAULTTEC_URL = "http://vaulttec.local:8000"
|
||||||
|
|||||||
Reference in New Issue
Block a user