for assorted things
at main 109 lines 3.5 kB view raw
1#!/usr/bin/env -S uv run --script --quiet 2# /// script 3# requires-python = ">=3.12" 4# dependencies = ["marvin@git+https://github.com/prefecthq/marvin.git"] 5# /// 6""" 7Make some change to my phillips hue network of lights via agent + MCP server. 8 9Usage: 10 11```bash 12./update-lights -m "turn on sahara in the living room and nightlight in the kitchen" 13``` 14 15Details: 16- uses a [`marvin`](https://github.com/prefecthq/marvin) (built on [`pydantic-ai`](https://github.com/pydantic/pydantic-ai)) agent 17- the agent spins up a [`fastmcp`](https://github.com/jlowin/fastmcp) MCP server that talks to my [`phue`](https://github.com/studioimaginaire/phue) bridge 18- set `HUE_BRIDGE_IP` and `HUE_BRIDGE_USERNAME` in `.env` or otherwise in environment 19- uses `OPENAI_API_KEY` by default, but you can set `AI_MODEL` in `.env` or otherwise in environment to use a different model 20""" 21 22import marvin 23import argparse 24from pathlib import Path 25from pydantic_settings import BaseSettings, SettingsConfigDict 26from pydantic import Field 27from pydantic_ai.mcp import MCPServerStdio 28from pydantic_ai.models import KnownModelName 29from rich.console import Console 30from rich.panel import Panel 31from rich.prompt import Prompt 32 33 34class Settings(BaseSettings): 35 model_config = SettingsConfigDict( 36 env_file=Path(__file__).parent / ".env", extra="ignore" 37 ) 38 39 hue_bridge_ip: str = Field(default=...) 40 hue_bridge_username: str = Field(default=...) 41 anthropic_api_key: str | None = Field(default=None) 42 43 ai_model: KnownModelName = Field(default="anthropic:claude-opus-4-5") 44 45 46settings = Settings() 47console = Console() 48 49hub_mcp = MCPServerStdio( 50 command="uvx", 51 args=[ 52 "smart-home@git+https://github.com/jlowin/fastmcp.git#subdirectory=examples/smart_home" 53 ], 54 env={ 55 "HUE_BRIDGE_IP": settings.hue_bridge_ip, 56 "HUE_BRIDGE_USERNAME": settings.hue_bridge_username, 57 }, 58) 59 60 61if __name__ == "__main__": 62 import os 63 64 if settings.anthropic_api_key: 65 os.environ["ANTHROPIC_API_KEY"] = settings.anthropic_api_key 66 67 parser = argparse.ArgumentParser(description="Send a command to the Marvin agent.") 68 parser.add_argument( 69 "--message", 70 "-m", 71 type=str, 72 default="soft and dim - Jessica Pratt energy, all areas", 73 help="The message to send to the agent (defaults to 'soft and dim - Jessica Pratt energy, all areas').", 74 ) 75 parser.add_argument( 76 "--once", 77 action="store_true", 78 help="Run once and exit instead of entering interactive mode.", 79 ) 80 args = parser.parse_args() 81 82 agent = marvin.Agent( 83 model=settings.ai_model, 84 mcp_servers=[hub_mcp], 85 ) 86 87 console.print( 88 Panel.fit( 89 f"[bold cyan]🏠 lights agent[/bold cyan]\n" 90 f"[dim]model: {settings.ai_model}[/dim]", 91 border_style="blue", 92 ) 93 ) 94 95 with marvin.Thread(): 96 console.print(f"\n[bold yellow]→[/bold yellow] {args.message}") 97 agent.run(str(args.message)) 98 99 if not args.once: 100 while True: 101 try: 102 user_input = Prompt.ask( 103 "\n[bold green]enter a message[/bold green]" 104 ) 105 console.print(f"[bold yellow]→[/bold yellow] {user_input}") 106 agent.run(str(user_input)) 107 except KeyboardInterrupt: 108 console.print("\n[dim red]exiting...[/dim red]") 109 break