zzstoatzz commited on
Commit
dc7ce68
·
1 Parent(s): f407523

one more time

try defer

checkpoint

small updates

push code

examples/mount_example.py CHANGED
@@ -53,7 +53,9 @@ async def news_data():
53
 
54
 
55
  # Main application
56
- app = FastMCP("Main App")
 
 
57
 
58
 
59
  @app.tool()
@@ -109,4 +111,4 @@ if __name__ == "__main__":
109
  asyncio.run(get_server_details())
110
 
111
  # Then start the server (uncomment to run the server)
112
- # app.run()
 
53
 
54
 
55
  # Main application
56
+ app = FastMCP(
57
+ "Main App", dependencies=["fastmcp@git+https://github.com/jlowin/fastmcp.git"]
58
+ )
59
 
60
 
61
  @app.tool()
 
111
  asyncio.run(get_server_details())
112
 
113
  # Then start the server (uncomment to run the server)
114
+ app.run()
examples/smart_home/pyproject.toml CHANGED
@@ -11,7 +11,7 @@ dependencies = ["fastmcp@git+https://github.com/jlowin/fastmcp.git", "phue2"]
11
  smart-home = "smart_home.__main__:main"
12
 
13
  [dependency-groups]
14
- dev = ["ruff"]
15
 
16
 
17
  [build-system]
 
11
  smart-home = "smart_home.__main__:main"
12
 
13
  [dependency-groups]
14
+ dev = ["ruff", "ipython"]
15
 
16
 
17
  [build-system]
examples/smart_home/src/smart_home/hub.py CHANGED
@@ -1,6 +1,7 @@
1
  from phue2 import Bridge
2
 
3
  from fastmcp import FastMCP
 
4
  from smart_home.settings import settings
5
 
6
  hub_mcp = FastMCP(
@@ -11,7 +12,7 @@ hub_mcp = FastMCP(
11
  )
12
 
13
  # Mount the lights service under the 'hue' prefix
14
- # hub_mcp.mount("hue", lights_mcp)
15
 
16
 
17
  # Add a status check for the hub
@@ -19,7 +20,11 @@ hub_mcp = FastMCP(
19
  def hub_status() -> str:
20
  """Checks the status of the main hub and connections."""
21
  try:
22
- bridge = Bridge(str(settings.hue_bridge_ip), save_config=False)
 
 
 
 
23
  bridge.connect()
24
  return "Hub OK. Hue Bridge Connected (via phue2)."
25
  except Exception as e:
 
1
  from phue2 import Bridge
2
 
3
  from fastmcp import FastMCP
4
+ from smart_home.lights.server import lights_mcp
5
  from smart_home.settings import settings
6
 
7
  hub_mcp = FastMCP(
 
12
  )
13
 
14
  # Mount the lights service under the 'hue' prefix
15
+ hub_mcp.mount("hue", lights_mcp)
16
 
17
 
18
  # Add a status check for the hub
 
20
  def hub_status() -> str:
21
  """Checks the status of the main hub and connections."""
22
  try:
23
+ bridge = Bridge(
24
+ ip=str(settings.hue_bridge_ip),
25
+ username=settings.hue_bridge_username,
26
+ save_config=False,
27
+ )
28
  bridge.connect()
29
  return "Hub OK. Hue Bridge Connected (via phue2)."
30
  except Exception as e:
examples/smart_home/src/smart_home/lights/server.py CHANGED
@@ -1,45 +1,24 @@
1
- import sys
2
  from typing import Any
3
 
4
  from phue2 import Bridge
5
  from phue2.exceptions import (
6
  PhueException,
7
- PhueRegistrationException,
8
- PhueRequestTimeout,
9
  )
10
 
11
  from fastmcp import FastMCP
12
  from smart_home.settings import settings
13
 
14
- BRIDGE_IP = str(settings.hue_bridge_ip)
15
 
16
- try:
17
- bridge = Bridge(BRIDGE_IP, save_config=False)
18
- print(f"Attempting connection to Hue Bridge at {BRIDGE_IP} via phue2...")
19
- bridge.connect() # Explicitly connect (needed for registration check)
20
- print(f"Successfully connected to Hue Bridge at {BRIDGE_IP} via phue2.")
 
 
 
 
21
 
22
- except PhueRegistrationException:
23
- print(
24
- f"FATAL: phue2 not registered with Bridge {BRIDGE_IP}. Run registration first.",
25
- file=sys.stderr,
26
- )
27
- bridge = None
28
- except PhueRequestTimeout:
29
- print(
30
- f"FATAL: Timeout connecting to Hue Bridge at {BRIDGE_IP}. Check IP and network.",
31
- file=sys.stderr,
32
- )
33
- bridge = None
34
- except PhueException as e:
35
- print(f"FATAL: phue2 error connecting to Bridge {BRIDGE_IP}: {e}", file=sys.stderr)
36
- bridge = None
37
- except Exception as e:
38
- print(
39
- f"FATAL: Unexpected error connecting to Bridge {BRIDGE_IP}: {e}",
40
- file=sys.stderr,
41
- )
42
- bridge = None
43
 
44
  lights_mcp = FastMCP(
45
  "Hue Lights Service (phue2)",
@@ -51,14 +30,14 @@ lights_mcp = FastMCP(
51
  # --- Resources ---
52
 
53
 
54
- @lights_mcp.resource("hue://lights")
55
- def list_lights() -> list[str]:
56
  """Lists the names of all available Hue lights using phue2."""
57
- if not bridge:
58
  return ["Error: Bridge not connected"]
59
  try:
60
- light_dict = bridge.get_light()
61
- return list(light_dict.keys())
62
  except PhueException as e:
63
  return [f"Error listing lights: {e}"]
64
  except Exception as e:
@@ -71,7 +50,7 @@ def list_lights() -> list[str]:
71
  @lights_mcp.tool()
72
  def toggle_light(light_name: str, state: bool) -> dict[str, Any]:
73
  """Turns a specific light on (true) or off (false) using phue2."""
74
- if not bridge:
75
  return {"error": "Bridge not connected", "success": False}
76
  try:
77
  result = bridge.set_light(light_name, "on", state)
@@ -104,7 +83,7 @@ def toggle_light(light_name: str, state: bool) -> dict[str, Any]:
104
  @lights_mcp.tool()
105
  def set_brightness(light_name: str, brightness: int) -> dict[str, Any]:
106
  """Sets the brightness of a specific light (0-254) using phue2."""
107
- if not bridge:
108
  return {"error": "Bridge not connected", "success": False}
109
  if not 0 <= brightness <= 254:
110
  return {
 
 
1
  from typing import Any
2
 
3
  from phue2 import Bridge
4
  from phue2.exceptions import (
5
  PhueException,
 
 
6
  )
7
 
8
  from fastmcp import FastMCP
9
  from smart_home.settings import settings
10
 
 
11
 
12
+ def _get_bridge() -> Bridge | None:
13
+ try:
14
+ return Bridge(
15
+ ip=str(settings.hue_bridge_ip),
16
+ username=settings.hue_bridge_username,
17
+ save_config=False,
18
+ )
19
+ except Exception:
20
+ return None
21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
  lights_mcp = FastMCP(
24
  "Hue Lights Service (phue2)",
 
30
  # --- Resources ---
31
 
32
 
33
+ @lights_mcp.tool()
34
+ def read_all_lights() -> list[str]:
35
  """Lists the names of all available Hue lights using phue2."""
36
+ if not (bridge := _get_bridge()):
37
  return ["Error: Bridge not connected"]
38
  try:
39
+ light_dict = bridge.get_light_objects("list")
40
+ return [light.name for light in light_dict]
41
  except PhueException as e:
42
  return [f"Error listing lights: {e}"]
43
  except Exception as e:
 
50
  @lights_mcp.tool()
51
  def toggle_light(light_name: str, state: bool) -> dict[str, Any]:
52
  """Turns a specific light on (true) or off (false) using phue2."""
53
+ if not (bridge := _get_bridge()):
54
  return {"error": "Bridge not connected", "success": False}
55
  try:
56
  result = bridge.set_light(light_name, "on", state)
 
83
  @lights_mcp.tool()
84
  def set_brightness(light_name: str, brightness: int) -> dict[str, Any]:
85
  """Sets the brightness of a specific light (0-254) using phue2."""
86
+ if not (bridge := _get_bridge()):
87
  return {"error": "Bridge not connected", "success": False}
88
  if not 0 <= brightness <= 254:
89
  return {
examples/smart_home/src/smart_home/settings.py CHANGED
@@ -6,6 +6,7 @@ class Settings(BaseSettings):
6
  model_config = SettingsConfigDict(env_file=".env", extra="ignore")
7
 
8
  hue_bridge_ip: IPvAnyAddress = Field(default=...)
 
9
 
10
 
11
  settings = Settings()
 
6
  model_config = SettingsConfigDict(env_file=".env", extra="ignore")
7
 
8
  hue_bridge_ip: IPvAnyAddress = Field(default=...)
9
+ hue_bridge_username: str = Field(default=...)
10
 
11
 
12
  settings = Settings()
examples/smart_home/uv.lock CHANGED
@@ -25,6 +25,15 @@ wheels = [
25
  { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916 },
26
  ]
27
 
 
 
 
 
 
 
 
 
 
28
  [[package]]
29
  name = "certifi"
30
  version = "2025.1.31"
@@ -55,6 +64,15 @@ wheels = [
55
  { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
56
  ]
57
 
 
 
 
 
 
 
 
 
 
58
  [[package]]
59
  name = "dotenv"
60
  version = "0.9.9"
@@ -66,6 +84,15 @@ wheels = [
66
  { url = "https://files.pythonhosted.org/packages/b2/b7/545d2c10c1fc15e48653c91efde329a790f2eecfbbf2bd16003b5db2bab0/dotenv-0.9.9-py2.py3-none-any.whl", hash = "sha256:29cf74a087b31dafdb5a446b6d7e11cbce8ed2741540e2339c69fbef92c94ce9", size = 1892 },
67
  ]
68
 
 
 
 
 
 
 
 
 
 
69
  [[package]]
70
  name = "fastapi"
71
  version = "0.115.12"
@@ -149,6 +176,51 @@ wheels = [
149
  { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
150
  ]
151
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  [[package]]
153
  name = "markdown-it-py"
154
  version = "3.0.0"
@@ -161,6 +233,18 @@ wheels = [
161
  { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
162
  ]
163
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  [[package]]
165
  name = "mcp"
166
  version = "1.6.0"
@@ -201,6 +285,27 @@ wheels = [
201
  { url = "https://files.pythonhosted.org/packages/12/cf/03675d8bd8ecbf4445504d8071adab19f5f993676795708e36402ab38263/openapi_pydantic-0.5.1-py3-none-any.whl", hash = "sha256:a3a09ef4586f5bd760a8df7f43028b60cafb6d9f61de2acba9574766255ab146", size = 96381 },
202
  ]
203
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
  [[package]]
205
  name = "phue2"
206
  version = "0.0.3"
@@ -213,6 +318,36 @@ wheels = [
213
  { url = "https://files.pythonhosted.org/packages/88/58/5d4a080926d1862fac5524ac15fea38fd7b3dcdfa7315734b850e6051c63/phue2-0.0.3-py3-none-any.whl", hash = "sha256:d2717bcfe1f8572e8b6fff305aa7b051b2dc7818eb9f1bff5eb0941f80bde8b2", size = 27535 },
214
  ]
215
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
  [[package]]
217
  name = "pydantic"
218
  version = "2.11.3"
@@ -359,6 +494,7 @@ dependencies = [
359
 
360
  [package.dev-dependencies]
361
  dev = [
 
362
  { name = "ruff" },
363
  ]
364
 
@@ -369,7 +505,10 @@ requires-dist = [
369
  ]
370
 
371
  [package.metadata.requires-dev]
372
- dev = [{ name = "ruff" }]
 
 
 
373
 
374
  [[package]]
375
  name = "sniffio"
@@ -393,6 +532,20 @@ wheels = [
393
  { url = "https://files.pythonhosted.org/packages/d9/e0/5b8bd393f27f4a62461c5cf2479c75a2cc2ffa330976f9f00f5f6e4f50eb/sse_starlette-2.2.1-py3-none-any.whl", hash = "sha256:6410a3d3ba0c89e7675d4c273a301d64649c03a5ef1ca101f10b47f895fd0e99", size = 10120 },
394
  ]
395
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
396
  [[package]]
397
  name = "starlette"
398
  version = "0.46.1"
@@ -405,6 +558,15 @@ wheels = [
405
  { url = "https://files.pythonhosted.org/packages/a0/4b/528ccf7a982216885a1ff4908e886b8fb5f19862d1962f56a3fce2435a70/starlette-0.46.1-py3-none-any.whl", hash = "sha256:77c74ed9d2720138b25875133f3a2dae6d854af2ec37dceb56aef370c1d8a227", size = 71995 },
406
  ]
407
 
 
 
 
 
 
 
 
 
 
408
  [[package]]
409
  name = "typer"
410
  version = "0.15.2"
@@ -454,6 +616,15 @@ wheels = [
454
  { url = "https://files.pythonhosted.org/packages/61/14/33a3a1352cfa71812a3a21e8c9bfb83f60b0011f5e36f2b1399d51928209/uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4", size = 62315 },
455
  ]
456
 
 
 
 
 
 
 
 
 
 
457
  [[package]]
458
  name = "websockets"
459
  version = "15.0.1"
 
25
  { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916 },
26
  ]
27
 
28
+ [[package]]
29
+ name = "asttokens"
30
+ version = "3.0.0"
31
+ source = { registry = "https://pypi.org/simple" }
32
+ sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/82da0a03e7ba5141f05cce0d302e6eed121ae055e0456ca228bf693984bc/asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", size = 61978 }
33
+ wheels = [
34
+ { url = "https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2", size = 26918 },
35
+ ]
36
+
37
  [[package]]
38
  name = "certifi"
39
  version = "2025.1.31"
 
64
  { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
65
  ]
66
 
67
+ [[package]]
68
+ name = "decorator"
69
+ version = "5.2.1"
70
+ source = { registry = "https://pypi.org/simple" }
71
+ sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711 }
72
+ wheels = [
73
+ { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190 },
74
+ ]
75
+
76
  [[package]]
77
  name = "dotenv"
78
  version = "0.9.9"
 
84
  { url = "https://files.pythonhosted.org/packages/b2/b7/545d2c10c1fc15e48653c91efde329a790f2eecfbbf2bd16003b5db2bab0/dotenv-0.9.9-py2.py3-none-any.whl", hash = "sha256:29cf74a087b31dafdb5a446b6d7e11cbce8ed2741540e2339c69fbef92c94ce9", size = 1892 },
85
  ]
86
 
87
+ [[package]]
88
+ name = "executing"
89
+ version = "2.2.0"
90
+ source = { registry = "https://pypi.org/simple" }
91
+ sdist = { url = "https://files.pythonhosted.org/packages/91/50/a9d80c47ff289c611ff12e63f7c5d13942c65d68125160cefd768c73e6e4/executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755", size = 978693 }
92
+ wheels = [
93
+ { url = "https://files.pythonhosted.org/packages/7b/8f/c4d9bafc34ad7ad5d8dc16dd1347ee0e507a52c3adb6bfa8887e1c6a26ba/executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa", size = 26702 },
94
+ ]
95
+
96
  [[package]]
97
  name = "fastapi"
98
  version = "0.115.12"
 
176
  { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
177
  ]
178
 
179
+ [[package]]
180
+ name = "ipython"
181
+ version = "9.1.0"
182
+ source = { registry = "https://pypi.org/simple" }
183
+ dependencies = [
184
+ { name = "colorama", marker = "sys_platform == 'win32'" },
185
+ { name = "decorator" },
186
+ { name = "ipython-pygments-lexers" },
187
+ { name = "jedi" },
188
+ { name = "matplotlib-inline" },
189
+ { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" },
190
+ { name = "prompt-toolkit" },
191
+ { name = "pygments" },
192
+ { name = "stack-data" },
193
+ { name = "traitlets" },
194
+ ]
195
+ sdist = { url = "https://files.pythonhosted.org/packages/70/9a/6b8984bedc990f3a4aa40ba8436dea27e23d26a64527de7c2e5e12e76841/ipython-9.1.0.tar.gz", hash = "sha256:a47e13a5e05e02f3b8e1e7a0f9db372199fe8c3763532fe7a1e0379e4e135f16", size = 4373688 }
196
+ wheels = [
197
+ { url = "https://files.pythonhosted.org/packages/b2/9d/4ff2adf55d1b6e3777b0303fdbe5b723f76e46cba4a53a32fe82260d2077/ipython-9.1.0-py3-none-any.whl", hash = "sha256:2df07257ec2f84a6b346b8d83100bcf8fa501c6e01ab75cd3799b0bb253b3d2a", size = 604053 },
198
+ ]
199
+
200
+ [[package]]
201
+ name = "ipython-pygments-lexers"
202
+ version = "1.1.1"
203
+ source = { registry = "https://pypi.org/simple" }
204
+ dependencies = [
205
+ { name = "pygments" },
206
+ ]
207
+ sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393 }
208
+ wheels = [
209
+ { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074 },
210
+ ]
211
+
212
+ [[package]]
213
+ name = "jedi"
214
+ version = "0.19.2"
215
+ source = { registry = "https://pypi.org/simple" }
216
+ dependencies = [
217
+ { name = "parso" },
218
+ ]
219
+ sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287 }
220
+ wheels = [
221
+ { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278 },
222
+ ]
223
+
224
  [[package]]
225
  name = "markdown-it-py"
226
  version = "3.0.0"
 
233
  { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
234
  ]
235
 
236
+ [[package]]
237
+ name = "matplotlib-inline"
238
+ version = "0.1.7"
239
+ source = { registry = "https://pypi.org/simple" }
240
+ dependencies = [
241
+ { name = "traitlets" },
242
+ ]
243
+ sdist = { url = "https://files.pythonhosted.org/packages/99/5b/a36a337438a14116b16480db471ad061c36c3694df7c2084a0da7ba538b7/matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90", size = 8159 }
244
+ wheels = [
245
+ { url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899 },
246
+ ]
247
+
248
  [[package]]
249
  name = "mcp"
250
  version = "1.6.0"
 
285
  { url = "https://files.pythonhosted.org/packages/12/cf/03675d8bd8ecbf4445504d8071adab19f5f993676795708e36402ab38263/openapi_pydantic-0.5.1-py3-none-any.whl", hash = "sha256:a3a09ef4586f5bd760a8df7f43028b60cafb6d9f61de2acba9574766255ab146", size = 96381 },
286
  ]
287
 
288
+ [[package]]
289
+ name = "parso"
290
+ version = "0.8.4"
291
+ source = { registry = "https://pypi.org/simple" }
292
+ sdist = { url = "https://files.pythonhosted.org/packages/66/94/68e2e17afaa9169cf6412ab0f28623903be73d1b32e208d9e8e541bb086d/parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d", size = 400609 }
293
+ wheels = [
294
+ { url = "https://files.pythonhosted.org/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", size = 103650 },
295
+ ]
296
+
297
+ [[package]]
298
+ name = "pexpect"
299
+ version = "4.9.0"
300
+ source = { registry = "https://pypi.org/simple" }
301
+ dependencies = [
302
+ { name = "ptyprocess" },
303
+ ]
304
+ sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450 }
305
+ wheels = [
306
+ { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772 },
307
+ ]
308
+
309
  [[package]]
310
  name = "phue2"
311
  version = "0.0.3"
 
318
  { url = "https://files.pythonhosted.org/packages/88/58/5d4a080926d1862fac5524ac15fea38fd7b3dcdfa7315734b850e6051c63/phue2-0.0.3-py3-none-any.whl", hash = "sha256:d2717bcfe1f8572e8b6fff305aa7b051b2dc7818eb9f1bff5eb0941f80bde8b2", size = 27535 },
319
  ]
320
 
321
+ [[package]]
322
+ name = "prompt-toolkit"
323
+ version = "3.0.50"
324
+ source = { registry = "https://pypi.org/simple" }
325
+ dependencies = [
326
+ { name = "wcwidth" },
327
+ ]
328
+ sdist = { url = "https://files.pythonhosted.org/packages/a1/e1/bd15cb8ffdcfeeb2bdc215de3c3cffca11408d829e4b8416dcfe71ba8854/prompt_toolkit-3.0.50.tar.gz", hash = "sha256:544748f3860a2623ca5cd6d2795e7a14f3d0e1c3c9728359013f79877fc89bab", size = 429087 }
329
+ wheels = [
330
+ { url = "https://files.pythonhosted.org/packages/e4/ea/d836f008d33151c7a1f62caf3d8dd782e4d15f6a43897f64480c2b8de2ad/prompt_toolkit-3.0.50-py3-none-any.whl", hash = "sha256:9b6427eb19e479d98acff65196a307c555eb567989e6d88ebbb1b509d9779198", size = 387816 },
331
+ ]
332
+
333
+ [[package]]
334
+ name = "ptyprocess"
335
+ version = "0.7.0"
336
+ source = { registry = "https://pypi.org/simple" }
337
+ sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762 }
338
+ wheels = [
339
+ { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993 },
340
+ ]
341
+
342
+ [[package]]
343
+ name = "pure-eval"
344
+ version = "0.2.3"
345
+ source = { registry = "https://pypi.org/simple" }
346
+ sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752 }
347
+ wheels = [
348
+ { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842 },
349
+ ]
350
+
351
  [[package]]
352
  name = "pydantic"
353
  version = "2.11.3"
 
494
 
495
  [package.dev-dependencies]
496
  dev = [
497
+ { name = "ipython" },
498
  { name = "ruff" },
499
  ]
500
 
 
505
  ]
506
 
507
  [package.metadata.requires-dev]
508
+ dev = [
509
+ { name = "ipython" },
510
+ { name = "ruff" },
511
+ ]
512
 
513
  [[package]]
514
  name = "sniffio"
 
532
  { url = "https://files.pythonhosted.org/packages/d9/e0/5b8bd393f27f4a62461c5cf2479c75a2cc2ffa330976f9f00f5f6e4f50eb/sse_starlette-2.2.1-py3-none-any.whl", hash = "sha256:6410a3d3ba0c89e7675d4c273a301d64649c03a5ef1ca101f10b47f895fd0e99", size = 10120 },
533
  ]
534
 
535
+ [[package]]
536
+ name = "stack-data"
537
+ version = "0.6.3"
538
+ source = { registry = "https://pypi.org/simple" }
539
+ dependencies = [
540
+ { name = "asttokens" },
541
+ { name = "executing" },
542
+ { name = "pure-eval" },
543
+ ]
544
+ sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707 }
545
+ wheels = [
546
+ { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521 },
547
+ ]
548
+
549
  [[package]]
550
  name = "starlette"
551
  version = "0.46.1"
 
558
  { url = "https://files.pythonhosted.org/packages/a0/4b/528ccf7a982216885a1ff4908e886b8fb5f19862d1962f56a3fce2435a70/starlette-0.46.1-py3-none-any.whl", hash = "sha256:77c74ed9d2720138b25875133f3a2dae6d854af2ec37dceb56aef370c1d8a227", size = 71995 },
559
  ]
560
 
561
+ [[package]]
562
+ name = "traitlets"
563
+ version = "5.14.3"
564
+ source = { registry = "https://pypi.org/simple" }
565
+ sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621 }
566
+ wheels = [
567
+ { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359 },
568
+ ]
569
+
570
  [[package]]
571
  name = "typer"
572
  version = "0.15.2"
 
616
  { url = "https://files.pythonhosted.org/packages/61/14/33a3a1352cfa71812a3a21e8c9bfb83f60b0011f5e36f2b1399d51928209/uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4", size = 62315 },
617
  ]
618
 
619
+ [[package]]
620
+ name = "wcwidth"
621
+ version = "0.2.13"
622
+ source = { registry = "https://pypi.org/simple" }
623
+ sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301 }
624
+ wheels = [
625
+ { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 },
626
+ ]
627
+
628
  [[package]]
629
  name = "websockets"
630
  version = "15.0.1"
pyproject.toml CHANGED
@@ -48,7 +48,7 @@ bump = true
48
  fallback-version = "0.0.0"
49
 
50
 
51
- [tool.uv.workspace]
52
  # uncomment to omit `dev` default group
53
  # default-groups = []
54
 
 
48
  fallback-version = "0.0.0"
49
 
50
 
51
+ [tool.uv]
52
  # uncomment to omit `dev` default group
53
  # default-groups = []
54