awnode 0.3.3__tar.gz → 0.3.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. {awnode-0.3.3 → awnode-0.3.4}/PKG-INFO +3 -3
  2. {awnode-0.3.3 → awnode-0.3.4}/README.md +1 -1
  3. {awnode-0.3.3 → awnode-0.3.4}/awnode/__init__.py +1 -1
  4. {awnode-0.3.3 → awnode-0.3.4}/awnode/mcp_server.py +71 -29
  5. {awnode-0.3.3 → awnode-0.3.4}/docs/aither-manifest.json +4 -4
  6. {awnode-0.3.3 → awnode-0.3.4}/docs/index.html +2 -2
  7. {awnode-0.3.3 → awnode-0.3.4}/pyproject.toml +9 -12
  8. awnode-0.3.4/tests/test_mcp_api_compat.py +60 -0
  9. {awnode-0.3.3 → awnode-0.3.4}/.github/workflows/publish.yml +0 -0
  10. {awnode-0.3.3 → awnode-0.3.4}/.gitignore +0 -0
  11. {awnode-0.3.3 → awnode-0.3.4}/CLAUDE.md +0 -0
  12. {awnode-0.3.3 → awnode-0.3.4}/Dockerfile +0 -0
  13. {awnode-0.3.3 → awnode-0.3.4}/LICENSE +0 -0
  14. {awnode-0.3.3 → awnode-0.3.4}/_smoke_check.py +0 -0
  15. {awnode-0.3.3 → awnode-0.3.4}/aither-constellation.js +0 -0
  16. {awnode-0.3.3 → awnode-0.3.4}/aither-manifest.json +0 -0
  17. {awnode-0.3.3 → awnode-0.3.4}/awnode/__main__.py +0 -0
  18. {awnode-0.3.3 → awnode-0.3.4}/awnode/_doctor.py +0 -0
  19. {awnode-0.3.3 → awnode-0.3.4}/awnode/cli.py +0 -0
  20. {awnode-0.3.3 → awnode-0.3.4}/awnode/comfyui_setup.py +0 -0
  21. {awnode-0.3.3 → awnode-0.3.4}/awnode/components/__init__.py +0 -0
  22. {awnode-0.3.3 → awnode-0.3.4}/awnode/components/autorig.yaml +0 -0
  23. {awnode-0.3.3 → awnode-0.3.4}/awnode/components/canvas.yaml +0 -0
  24. {awnode-0.3.3 → awnode-0.3.4}/awnode/components/comfyui-3d.yaml +0 -0
  25. {awnode-0.3.3 → awnode-0.3.4}/awnode/components/comfyui.yaml +0 -0
  26. {awnode-0.3.3 → awnode-0.3.4}/awnode/components/design.yaml +0 -0
  27. {awnode-0.3.3 → awnode-0.3.4}/awnode/components/iris.yaml +0 -0
  28. {awnode-0.3.3 → awnode-0.3.4}/awnode/components/muse.yaml +0 -0
  29. {awnode-0.3.3 → awnode-0.3.4}/awnode/components/ollama.yaml +0 -0
  30. {awnode-0.3.3 → awnode-0.3.4}/awnode/components/sd-webui.yaml +0 -0
  31. {awnode-0.3.3 → awnode-0.3.4}/awnode/extensions.py +0 -0
  32. {awnode-0.3.3 → awnode-0.3.4}/awnode/hub.py +0 -0
  33. {awnode-0.3.3 → awnode-0.3.4}/awnode/license.py +0 -0
  34. {awnode-0.3.3 → awnode-0.3.4}/awnode/proxy.py +0 -0
  35. {awnode-0.3.3 → awnode-0.3.4}/awnode/server.py +0 -0
  36. {awnode-0.3.3 → awnode-0.3.4}/awnode/service_install.py +0 -0
  37. {awnode-0.3.3 → awnode-0.3.4}/awnode/sync.py +0 -0
  38. {awnode-0.3.3 → awnode-0.3.4}/awnode/tools/__init__.py +0 -0
  39. {awnode-0.3.3 → awnode-0.3.4}/awnode/tools/_http.py +0 -0
  40. {awnode-0.3.3 → awnode-0.3.4}/awnode/tools/_scopes.py +0 -0
  41. {awnode-0.3.3 → awnode-0.3.4}/awnode/tools/commands.py +0 -0
  42. {awnode-0.3.3 → awnode-0.3.4}/awnode/tools/context.py +0 -0
  43. {awnode-0.3.3 → awnode-0.3.4}/awnode/tools/documents.py +0 -0
  44. {awnode-0.3.3 → awnode-0.3.4}/awnode/tools/embeddings.py +0 -0
  45. {awnode-0.3.3 → awnode-0.3.4}/awnode/tools/extensions.py +0 -0
  46. {awnode-0.3.3 → awnode-0.3.4}/awnode/tools/filesystem.py +0 -0
  47. {awnode-0.3.3 → awnode-0.3.4}/awnode/tools/fleet_host.py +0 -0
  48. {awnode-0.3.3 → awnode-0.3.4}/awnode/tools/fleet_verbs.py +0 -0
  49. {awnode-0.3.3 → awnode-0.3.4}/awnode/tools/generation.py +0 -0
  50. {awnode-0.3.3 → awnode-0.3.4}/awnode/tools/git_tools.py +0 -0
  51. {awnode-0.3.3 → awnode-0.3.4}/awnode/tools/http_client.py +0 -0
  52. {awnode-0.3.3 → awnode-0.3.4}/awnode/tools/knowledge.py +0 -0
  53. {awnode-0.3.3 → awnode-0.3.4}/awnode/tools/memory.py +0 -0
  54. {awnode-0.3.3 → awnode-0.3.4}/awnode/tools/mermaid.py +0 -0
  55. {awnode-0.3.3 → awnode-0.3.4}/awnode/tools/notebooks.py +0 -0
  56. {awnode-0.3.3 → awnode-0.3.4}/awnode/tools/ollama.py +0 -0
  57. {awnode-0.3.3 → awnode-0.3.4}/awnode/tools/orchestrator.py +0 -0
  58. {awnode-0.3.3 → awnode-0.3.4}/awnode/tools/search.py +0 -0
  59. {awnode-0.3.3 → awnode-0.3.4}/awnode/update_check.py +0 -0
  60. {awnode-0.3.3 → awnode-0.3.4}/dist_validate/aithernode-0.1.0-py3-none-any.whl +0 -0
  61. {awnode-0.3.3 → awnode-0.3.4}/docker-entrypoint.sh +0 -0
  62. {awnode-0.3.3 → awnode-0.3.4}/docs/aither-constellation.js +0 -0
  63. {awnode-0.3.3 → awnode-0.3.4}/docs/llms.txt +0 -0
  64. {awnode-0.3.3 → awnode-0.3.4}/packaging/aithernode-stub/README.md +0 -0
  65. {awnode-0.3.3 → awnode-0.3.4}/packaging/aithernode-stub/pyproject.toml +0 -0
  66. {awnode-0.3.3 → awnode-0.3.4}/tests/test_awnix_host_install.py +0 -0
  67. {awnode-0.3.3 → awnode-0.3.4}/tests/test_chat_endpoint_auth.py +0 -0
  68. {awnode-0.3.3 → awnode-0.3.4}/tests/test_component_image_env.py +0 -0
  69. {awnode-0.3.3 → awnode-0.3.4}/tests/test_custom_backend.py +0 -0
  70. {awnode-0.3.3 → awnode-0.3.4}/tests/test_extension_contract.py +0 -0
  71. {awnode-0.3.3 → awnode-0.3.4}/tests/test_fleet_host_tools.py +0 -0
  72. {awnode-0.3.3 → awnode-0.3.4}/tests/test_fleet_verbs_tools.py +0 -0
  73. {awnode-0.3.3 → awnode-0.3.4}/tests/test_hub_error_cooldown.py +0 -0
  74. {awnode-0.3.3 → awnode-0.3.4}/tests/test_mcp_mode_reprobe.py +0 -0
  75. {awnode-0.3.3 → awnode-0.3.4}/tests/test_mcp_server_auth.py +0 -0
  76. {awnode-0.3.3 → awnode-0.3.4}/tests/test_mcp_sse_transport.py +0 -0
  77. {awnode-0.3.3 → awnode-0.3.4}/tests/test_pair_mode_route.py +0 -0
  78. {awnode-0.3.3 → awnode-0.3.4}/tests/test_proxy_plane.py +0 -0
  79. {awnode-0.3.3 → awnode-0.3.4}/tests/test_server_route_hygiene.py +0 -0
  80. {awnode-0.3.3 → awnode-0.3.4}/tests/test_server_security_hardening.py +0 -0
  81. {awnode-0.3.3 → awnode-0.3.4}/tests/test_sync_enrollment_lifecycle.py +0 -0
  82. {awnode-0.3.3 → awnode-0.3.4}/tests/test_update_check.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: awnode
3
- Version: 0.3.3
3
+ Version: 0.3.4
4
4
  Summary: awnode — lightweight local gateway for AitherOS
5
5
  Project-URL: Homepage, https://aitherium.com
6
6
  Author-email: Aitherium <hello@aitherium.com>
@@ -11,7 +11,7 @@ Requires-Python: >=3.10
11
11
  Requires-Dist: awdk>=1.3.0
12
12
  Requires-Dist: fastapi>=0.109.0
13
13
  Requires-Dist: httpx>=0.26.0
14
- Requires-Dist: mcp<2.0.0,>=1.0.0
14
+ Requires-Dist: mcp<3.0.0,>=1.0.0
15
15
  Requires-Dist: pyyaml>=6.0
16
16
  Requires-Dist: uvicorn[standard]>=0.27.0
17
17
  Provides-Extra: all
@@ -282,7 +282,7 @@ Every repository here is public. Each publishes an `aither-manifest.json` beside
282
282
  | [awsettings](https://github.com/Aitherium/awsettings) | Your agent's permissions and config, following you to the next machine | [docs](https://aitherium.github.io/awsettings/) |
283
283
  | [awavatar](https://github.com/Aitherium/awavatar) | One character spec in, a rigged, animated, multi-style avatar pack out | [docs](https://aitherium.github.io/awavatar/) |
284
284
 
285
- **Built on** [llama.cpp](https://github.com/ggml-org/llama.cpp) · [vLLM](https://github.com/vllm-project/vllm) · [ComfyUI](https://github.com/comfyanonymous/ComfyUI) · [CentOS Stream](https://www.centos.org/centos-stream/) · [Podman](https://github.com/containers/podman) · [Docker](https://github.com/moby/moby) · [LanceDB](https://github.com/lancedb/lancedb) · [WireGuard](https://www.wireguard.com/) · [FFmpeg](https://ffmpeg.org/) · [Blender + Rigify](https://www.blender.org/) · [headroom](https://github.com/headroomlabs-ai/headroom) · [SANA](https://github.com/NVlabs/Sana) · [Hunyuan3D](https://github.com/Tencent-Hunyuan/Hunyuan3D-2.1) · [repowise](https://github.com/repowise-dev/repowise) · [Playwright](https://github.com/microsoft/playwright) · [Chromium](https://www.chromium.org/) · [Next.js](https://github.com/vercel/next.js) · [React](https://github.com/facebook/react).
285
+ **Built on** [REA (Reverse Engineer Anything)](https://github.com/morluto/rea) · [llama.cpp](https://github.com/ggml-org/llama.cpp) · [vLLM](https://github.com/vllm-project/vllm) · [ComfyUI](https://github.com/comfyanonymous/ComfyUI) · [CentOS Stream](https://www.centos.org/centos-stream/) · [Podman](https://github.com/containers/podman) · [Docker](https://github.com/moby/moby) · [LanceDB](https://github.com/lancedb/lancedb) · [WireGuard](https://www.wireguard.com/) · [FFmpeg](https://ffmpeg.org/) · [Blender + Rigify](https://www.blender.org/) · [headroom](https://github.com/headroomlabs-ai/headroom) · [SANA](https://github.com/NVlabs/Sana) · [Hunyuan3D](https://github.com/Tencent-Hunyuan/Hunyuan3D-2.1) · [repowise](https://github.com/repowise-dev/repowise) · [Playwright](https://github.com/microsoft/playwright) · [Chromium](https://www.chromium.org/) · [Next.js](https://github.com/vercel/next.js) · [React](https://github.com/facebook/react).
286
286
 
287
287
  <div id="aither-constellation" data-self="awnode"></div>
288
288
  <script src="aither-constellation.js"></script>
@@ -254,7 +254,7 @@ Every repository here is public. Each publishes an `aither-manifest.json` beside
254
254
  | [awsettings](https://github.com/Aitherium/awsettings) | Your agent's permissions and config, following you to the next machine | [docs](https://aitherium.github.io/awsettings/) |
255
255
  | [awavatar](https://github.com/Aitherium/awavatar) | One character spec in, a rigged, animated, multi-style avatar pack out | [docs](https://aitherium.github.io/awavatar/) |
256
256
 
257
- **Built on** [llama.cpp](https://github.com/ggml-org/llama.cpp) · [vLLM](https://github.com/vllm-project/vllm) · [ComfyUI](https://github.com/comfyanonymous/ComfyUI) · [CentOS Stream](https://www.centos.org/centos-stream/) · [Podman](https://github.com/containers/podman) · [Docker](https://github.com/moby/moby) · [LanceDB](https://github.com/lancedb/lancedb) · [WireGuard](https://www.wireguard.com/) · [FFmpeg](https://ffmpeg.org/) · [Blender + Rigify](https://www.blender.org/) · [headroom](https://github.com/headroomlabs-ai/headroom) · [SANA](https://github.com/NVlabs/Sana) · [Hunyuan3D](https://github.com/Tencent-Hunyuan/Hunyuan3D-2.1) · [repowise](https://github.com/repowise-dev/repowise) · [Playwright](https://github.com/microsoft/playwright) · [Chromium](https://www.chromium.org/) · [Next.js](https://github.com/vercel/next.js) · [React](https://github.com/facebook/react).
257
+ **Built on** [REA (Reverse Engineer Anything)](https://github.com/morluto/rea) · [llama.cpp](https://github.com/ggml-org/llama.cpp) · [vLLM](https://github.com/vllm-project/vllm) · [ComfyUI](https://github.com/comfyanonymous/ComfyUI) · [CentOS Stream](https://www.centos.org/centos-stream/) · [Podman](https://github.com/containers/podman) · [Docker](https://github.com/moby/moby) · [LanceDB](https://github.com/lancedb/lancedb) · [WireGuard](https://www.wireguard.com/) · [FFmpeg](https://ffmpeg.org/) · [Blender + Rigify](https://www.blender.org/) · [headroom](https://github.com/headroomlabs-ai/headroom) · [SANA](https://github.com/NVlabs/Sana) · [Hunyuan3D](https://github.com/Tencent-Hunyuan/Hunyuan3D-2.1) · [repowise](https://github.com/repowise-dev/repowise) · [Playwright](https://github.com/microsoft/playwright) · [Chromium](https://www.chromium.org/) · [Next.js](https://github.com/vercel/next.js) · [React](https://github.com/facebook/react).
258
258
 
259
259
  <div id="aither-constellation" data-self="awnode"></div>
260
260
  <script src="aither-constellation.js"></script>
@@ -17,4 +17,4 @@ Install:
17
17
  aithernode status # Check what's connected
18
18
  """
19
19
 
20
- __version__ = "0.3.3"
20
+ __version__ = "0.3.4"
@@ -43,6 +43,7 @@ import sys
43
43
  from enum import Enum
44
44
  from typing import Any, Callable, Dict, List, get_type_hints
45
45
 
46
+ from mcp import types as mcp_types
46
47
  from mcp.server import Server
47
48
  from mcp.server.lowlevel import NotificationOptions
48
49
  from mcp.server.stdio import stdio_server
@@ -113,7 +114,7 @@ async def detect_mode() -> RuntimeMode:
113
114
  # Scopes — which tool modules are available in which mode
114
115
  # ---------------------------------------------------------------------------
115
116
 
116
- from awnode.tools._scopes import SHELL_LOCAL, SHELL_CLOUD, PLATFORM_ONLY # noqa: E402
117
+ from awnode.tools._scopes import PLATFORM_ONLY, SHELL_CLOUD, SHELL_LOCAL # noqa: E402
117
118
 
118
119
 
119
120
  def _scope_for_module(module_name: str) -> str:
@@ -281,7 +282,6 @@ def _discover_tools() -> Dict[str, Dict[str, Any]]:
281
282
  # MCP Server
282
283
  # ---------------------------------------------------------------------------
283
284
 
284
- server = Server("awnode")
285
285
  _current_mode: RuntimeMode = RuntimeMode.STANDALONE
286
286
 
287
287
 
@@ -321,14 +321,8 @@ async def _reprobe_loop() -> None:
321
321
  logger.debug("mode re-probe failed: %s", exc)
322
322
 
323
323
 
324
- @server.list_tools()
325
- async def list_tools() -> list[Tool]:
326
- """Return tools available for the current runtime mode."""
327
- global _session
328
- try:
329
- _session = server.request_context.session
330
- except LookupError:
331
- pass
324
+ def visible_tools() -> list[Tool]:
325
+ """Tools available for the current runtime mode."""
332
326
  tools: list[Tool] = []
333
327
  for name, info in _TOOL_REGISTRY.items():
334
328
  if not _mode_allows_scope(_current_mode, info["scope"]):
@@ -341,15 +335,15 @@ async def list_tools() -> list[Tool]:
341
335
  return tools
342
336
 
343
337
 
344
- @server.call_tool()
345
- async def call_tool(name: str, arguments: dict) -> list[TextContent]:
346
- """Invoke a registered tool by name."""
338
+ async def dispatch_tool(name: str, arguments: dict) -> "tuple[str, bool]":
339
+ """Invoke a registered tool by name, applying the runtime-mode scope rule.
340
+
341
+ Returns ``(text, is_error)``. Transport-neutral: both mcp API generations
342
+ below wrap this one function, so the two can never disagree on a verdict.
343
+ """
347
344
  info = _TOOL_REGISTRY.get(name)
348
345
  if info is None:
349
- return [TextContent(
350
- type="text",
351
- text=json.dumps({"error": f"Unknown tool: {name}"}, indent=2),
352
- )]
346
+ return json.dumps({"error": f"Unknown tool: {name}"}, indent=2), True
353
347
 
354
348
  # Scope check
355
349
  if not _mode_allows_scope(_current_mode, info["scope"]):
@@ -366,13 +360,60 @@ async def call_tool(name: str, arguments: dict) -> list[TextContent]:
366
360
  )
367
361
  else:
368
362
  msg = f"Tool '{name}' is not available in {_current_mode.value} mode."
369
- return [TextContent(
370
- type="text",
371
- text=json.dumps({"error": msg, "scope": scope}, indent=2),
372
- )]
363
+ return json.dumps({"error": msg, "scope": scope}, indent=2), True
364
+
365
+ return await invoke_registered_tool(name, info, arguments)
366
+
367
+
368
+ # -- mcp 2.x: handlers are constructor arguments on the low-level Server -------------
369
+
370
+ async def _on_list_tools(ctx: Any, _params: Any) -> Any:
371
+ global _session
372
+ _session = getattr(ctx, "session", None) or _session
373
+ return mcp_types.ListToolsResult(tools=visible_tools())
374
+
375
+
376
+ async def _on_call_tool(_ctx: Any, params: Any) -> Any:
377
+ text, is_error = await dispatch_tool(params.name, dict(params.arguments or {}))
378
+ return mcp_types.CallToolResult(
379
+ content=[TextContent(type="text", text=text)], is_error=is_error,
380
+ )
381
+
382
+
383
+ def _build_server() -> Server:
384
+ """Construct the low-level Server against whichever mcp generation is installed.
373
385
 
374
- text, _is_error = await invoke_registered_tool(name, info, arguments)
375
- return [TextContent(type="text", text=text)]
386
+ mcp 2.x removed the 1.x ``@server.list_tools()`` / ``@server.call_tool()``
387
+ decorators (``awnode mcp`` died at import with AttributeError once a sibling aw
388
+ brick pulled mcp 2.2 into the shared env, 2026-10-14) and takes the handlers as
389
+ ``on_list_tools=`` / ``on_call_tool=`` instead. awdk[node] still pins mcp<2, so
390
+ awnode supports BOTH rather than forcing a resolver conflict on a shared env.
391
+ """
392
+ try:
393
+ return Server("awnode", on_list_tools=_on_list_tools, on_call_tool=_on_call_tool)
394
+ except TypeError as exc:
395
+ # mcp 1.x: no handler kwargs -- register through the decorators below.
396
+ logger.debug("mcp 1.x Server API (%s); using the decorator registration", exc)
397
+ srv = Server("awnode")
398
+
399
+ @srv.list_tools()
400
+ async def list_tools() -> list[Tool]:
401
+ global _session
402
+ try:
403
+ _session = srv.request_context.session
404
+ except LookupError:
405
+ logger.debug("tools/list outside a request context; keeping the last session")
406
+ return visible_tools()
407
+
408
+ @srv.call_tool()
409
+ async def call_tool(name: str, arguments: dict) -> list[TextContent]:
410
+ text, _is_error = await dispatch_tool(name, arguments)
411
+ return [TextContent(type="text", text=text)]
412
+
413
+ return srv
414
+
415
+
416
+ server = _build_server()
376
417
 
377
418
 
378
419
  async def invoke_registered_tool(
@@ -390,8 +431,9 @@ async def invoke_registered_tool(
390
431
  denial = get_license_manager().check_tool(info["module"])
391
432
  if denial:
392
433
  return json.dumps({"error": denial, "scope": "subscription"}, indent=2), True
393
- except ImportError:
394
- pass # License check failure should never block tools
434
+ except ImportError as exc:
435
+ # License check failure should never block tools.
436
+ logger.debug("license module unavailable; tool %s not gated: %s", name, exc)
395
437
 
396
438
  func = info["func"]
397
439
 
@@ -473,7 +515,7 @@ def _host_is_loopback(host_header: str) -> bool:
473
515
  def build_sse_app(mcp_key: str = "", loopback_only: bool = True):
474
516
  """The Starlette app behind ``awnode mcp --transport sse``.
475
517
 
476
- Routes (the standard MCP HTTP+SSE transport, mcp 1.x ``SseServerTransport``):
518
+ Routes (the standard MCP HTTP+SSE transport, ``SseServerTransport``):
477
519
  GET /health liveness (never behind auth)
478
520
  GET /sse opens the event stream; its first event names the
479
521
  per-session POST endpoint (``/messages/?session_id=...``)
@@ -490,9 +532,9 @@ def build_sse_app(mcp_key: str = "", loopback_only: bool = True):
490
532
  rebinding page reaches 127.0.0.1 as same-origin under its own Host name, so
491
533
  every request carrying an Origin is refused (MCP clients are not browsers --
492
534
  the same rule as aw-hub), and with ``loopback_only`` any Host that is not
493
- 127.0.0.1 / localhost / [::1] is refused too. The pin is ``mcp>=1.0,<2`` and
494
- TransportSecuritySettings exists only in later 1.x, so it is layered on when
495
- importable but never relied on alone.
535
+ 127.0.0.1 / localhost / [::1] is refused too. The pin is ``mcp>=1.0,<3`` and
536
+ TransportSecuritySettings exists only in later 1.x and 2.x, so it is layered on
537
+ when importable but never relied on alone.
496
538
  """
497
539
  from mcp.server.sse import SseServerTransport
498
540
  from starlette.applications import Starlette
@@ -15,12 +15,12 @@
15
15
  "includes": [],
16
16
  "metrics": {
17
17
  "files": {
18
- "value": 77,
19
- "as_of": "2026-10-09T09:45:48+00:00"
18
+ "value": 80,
19
+ "as_of": "2026-10-13T18:27:40+00:00"
20
20
  },
21
21
  "tests": {
22
- "value": 14,
23
- "as_of": "2026-10-09T09:45:48+00:00"
22
+ "value": 17,
23
+ "as_of": "2026-10-13T18:27:40+00:00"
24
24
  }
25
25
  }
26
26
  }
@@ -1179,7 +1179,7 @@ the read-only set; for full tool access from an IDE use <code>awnode mcp</code>.
1179
1179
  </tr>
1180
1180
  </tbody>
1181
1181
  </table>
1182
- <p><strong>Built on</strong> <a href="https://github.com/ggml-org/llama.cpp">llama.cpp</a> · <a href="https://github.com/vllm-project/vllm">vLLM</a> · <a href="https://github.com/comfyanonymous/ComfyUI">ComfyUI</a> · <a href="https://www.centos.org/centos-stream/">CentOS Stream</a> · <a href="https://github.com/containers/podman">Podman</a> · <a href="https://github.com/moby/moby">Docker</a> · <a href="https://github.com/lancedb/lancedb">LanceDB</a> · <a href="https://www.wireguard.com/">WireGuard</a> · <a href="https://ffmpeg.org/">FFmpeg</a> · <a href="https://www.blender.org/">Blender + Rigify</a> · <a href="https://github.com/headroomlabs-ai/headroom">headroom</a> · <a href="https://github.com/NVlabs/Sana">SANA</a> · <a href="https://github.com/Tencent-Hunyuan/Hunyuan3D-2.1">Hunyuan3D</a> · <a href="https://github.com/repowise-dev/repowise">repowise</a> · <a href="https://github.com/microsoft/playwright">Playwright</a> · <a href="https://www.chromium.org/">Chromium</a> · <a href="https://github.com/vercel/next.js">Next.js</a> · <a href="https://github.com/facebook/react">React</a>.</p>
1182
+ <p><strong>Built on</strong> <a href="https://github.com/morluto/rea">REA (Reverse Engineer Anything)</a> · <a href="https://github.com/ggml-org/llama.cpp">llama.cpp</a> · <a href="https://github.com/vllm-project/vllm">vLLM</a> · <a href="https://github.com/comfyanonymous/ComfyUI">ComfyUI</a> · <a href="https://www.centos.org/centos-stream/">CentOS Stream</a> · <a href="https://github.com/containers/podman">Podman</a> · <a href="https://github.com/moby/moby">Docker</a> · <a href="https://github.com/lancedb/lancedb">LanceDB</a> · <a href="https://www.wireguard.com/">WireGuard</a> · <a href="https://ffmpeg.org/">FFmpeg</a> · <a href="https://www.blender.org/">Blender + Rigify</a> · <a href="https://github.com/headroomlabs-ai/headroom">headroom</a> · <a href="https://github.com/NVlabs/Sana">SANA</a> · <a href="https://github.com/Tencent-Hunyuan/Hunyuan3D-2.1">Hunyuan3D</a> · <a href="https://github.com/repowise-dev/repowise">repowise</a> · <a href="https://github.com/microsoft/playwright">Playwright</a> · <a href="https://www.chromium.org/">Chromium</a> · <a href="https://github.com/vercel/next.js">Next.js</a> · <a href="https://github.com/facebook/react">React</a>.</p>
1183
1183
  <div id="aither-constellation" data-self="awnode"></div>
1184
1184
  <script src="aither-constellation.js"></script>
1185
1185
 
@@ -1257,7 +1257,7 @@ the read-only set; for full tool access from an IDE use <code>awnode mcp</code>.
1257
1257
  <div class="fk-element" aria-label="Aitherium, element 0, symbol Ai, the element of creation"><span class="fk-el-no">0</span><span class="fk-el-sy">Ai</span><span class="fk-el-nm">aitherium</span></div>
1258
1258
  <p class="fk-brandlines"><span>Aitherium · the element of creation</span><span>Aither — the medium creation moves through</span></p>
1259
1259
  <div class="fk-family"><a href="https://aitherium.com/about"><span class="n">Aitherium</span><span class="r">the company</span></a><a href="https://aitherium.com/"><span class="n">AitherOS</span><span class="r">the OS</span></a><a href="https://aitherium.github.io/awnix/"><span class="n m">awnix</span><span class="r">the base</span></a><a href="https://aitherium.github.io/awdk/"><span class="n m">awdk</span><span class="r">the SDK</span></a><a href="https://aitherium.github.io/awsh/"><span class="n m">awsh</span><span class="r">the shell</span></a><a href="https://aitherium.github.io/"><span class="n m">aw* bricks</span><span class="r">the frameworks</span></a><a href="https://aitherium.com/studio"><span class="n">The Studio</span><span class="r">build apps, agents, workflows</span></a></div>
1260
- <p class="fk-colophon fk-built-on">Built on <a href="https://www.blender.org/" rel="noopener">Blender + Rigify</a> &middot; <a href="https://www.centos.org/centos-stream/" rel="noopener">CentOS Stream</a> &middot; <a href="https://www.chromium.org/" rel="noopener">Chromium</a> &middot; <a href="https://github.com/comfyanonymous/ComfyUI" rel="noopener">ComfyUI</a> &middot; <a href="https://github.com/moby/moby" rel="noopener">Docker</a> &middot; <a href="https://ffmpeg.org/" rel="noopener">FFmpeg</a> &middot; <a href="https://github.com/headroomlabs-ai/headroom" rel="noopener">headroom</a> &middot; <a href="https://github.com/Tencent-Hunyuan/Hunyuan3D-2.1" rel="noopener">Hunyuan3D</a> &middot; <a href="https://github.com/lancedb/lancedb" rel="noopener">LanceDB</a> &middot; <a href="https://github.com/ggml-org/llama.cpp" rel="noopener">llama.cpp</a> &middot; <a href="https://github.com/vercel/next.js" rel="noopener">Next.js</a> &middot; <a href="https://github.com/microsoft/playwright" rel="noopener">Playwright</a> &middot; <a href="https://github.com/containers/podman" rel="noopener">Podman</a> &middot; <a href="https://github.com/facebook/react" rel="noopener">React</a> &middot; <a href="https://github.com/repowise-dev/repowise" rel="noopener">repowise</a> &middot; <a href="https://github.com/NVlabs/Sana" rel="noopener">SANA</a> &middot; <a href="https://github.com/vllm-project/vllm" rel="noopener">vLLM</a> &middot; <a href="https://www.wireguard.com/" rel="noopener">WireGuard</a></p>
1260
+ <p class="fk-colophon fk-built-on">Built on <a href="https://www.blender.org/" rel="noopener">Blender + Rigify</a> &middot; <a href="https://www.centos.org/centos-stream/" rel="noopener">CentOS Stream</a> &middot; <a href="https://www.chromium.org/" rel="noopener">Chromium</a> &middot; <a href="https://github.com/comfyanonymous/ComfyUI" rel="noopener">ComfyUI</a> &middot; <a href="https://github.com/moby/moby" rel="noopener">Docker</a> &middot; <a href="https://ffmpeg.org/" rel="noopener">FFmpeg</a> &middot; <a href="https://github.com/headroomlabs-ai/headroom" rel="noopener">headroom</a> &middot; <a href="https://github.com/Tencent-Hunyuan/Hunyuan3D-2.1" rel="noopener">Hunyuan3D</a> &middot; <a href="https://github.com/lancedb/lancedb" rel="noopener">LanceDB</a> &middot; <a href="https://github.com/ggml-org/llama.cpp" rel="noopener">llama.cpp</a> &middot; <a href="https://github.com/vercel/next.js" rel="noopener">Next.js</a> &middot; <a href="https://github.com/microsoft/playwright" rel="noopener">Playwright</a> &middot; <a href="https://github.com/containers/podman" rel="noopener">Podman</a> &middot; <a href="https://github.com/morluto/rea" rel="noopener">REA (Reverse Engineer Anything)</a> &middot; <a href="https://github.com/facebook/react" rel="noopener">React</a> &middot; <a href="https://github.com/repowise-dev/repowise" rel="noopener">repowise</a> &middot; <a href="https://github.com/NVlabs/Sana" rel="noopener">SANA</a> &middot; <a href="https://github.com/vllm-project/vllm" rel="noopener">vLLM</a> &middot; <a href="https://www.wireguard.com/" rel="noopener">WireGuard</a></p>
1261
1261
  <p class="fk-colophon">Part of the <strong>aw family</strong> — Aither World tools, each one <em>adoptable alone</em>. Generated from the brick README and the ecosystem registry.</p>
1262
1262
  </footer>
1263
1263
  <nav class="fk-dock" aria-label="The family"><a href="https://aitherium.com/" aria-label="AitherOS"><svg class="fk-mark" viewBox="0 0 512 512" fill="none" aria-hidden="true"><g transform="translate(256 256) scale(0.86) translate(-256 -256)"><polygon points="256,96 400,360 112,360" stroke="currentColor" stroke-width="26" stroke-linejoin="round"/><circle cx="256" cy="262" r="52" fill="currentColor"/><circle cx="256" cy="262" r="26" fill="#050507"/><circle cx="256" cy="262" r="10" fill="currentColor"/></g></svg></a><span class="fk-sep" aria-hidden="true"></span><a href="https://aitherium.github.io/" aria-current="page">bricks</a><a href="https://aitherium.github.io/awdk/">awdk</a><a href="https://aitherium.github.io/awsh/">awsh</a><a href="https://aitherium.github.io/awnix/">awnix</a><a href="https://aitherium.github.io/awknowledge/">guide</a><a href="https://blog.aitherium.com/">journal</a></nav>
@@ -8,7 +8,7 @@ build-backend = "hatchling.build"
8
8
  # [project.scripts]). The old `aithernode` distribution name is kept resolving by
9
9
  # the redirect stub in `packaging/aithernode-stub/`.
10
10
  name = "awnode"
11
- version = "0.3.3"
11
+ version = "0.3.4"
12
12
  description = "awnode — lightweight local gateway for AitherOS"
13
13
  readme = "README.md"
14
14
  requires-python = ">=3.10"
@@ -23,17 +23,14 @@ dependencies = [
23
23
  "pyyaml>=6.0",
24
24
  "fastapi>=0.109.0",
25
25
  "uvicorn[standard]>=0.27.0",
26
- # <2.0.0 pinned deliberately: mcp_server.py uses the 1.x low-level
27
- # Server API (@server.list_tools() / @server.call_tool()), which 2.0
28
- # removed entirely in favor of MCPServer's per-tool @server.tool()
29
- # decorator (see AitherOS/packages/awgraph/awgraph/mcp_server.py's own
30
- # docstring for the same cutover). Unpinned, `mcp>=1.0.0` silently
31
- # resolved to 2.0.0 and `test` died at collection with "'Server' object
32
- # has no attribute 'list_tools'" the first time CI ever got far enough
33
- # to import this module — a real drift the workflow's own setup-python
34
- # failure had been hiding, not something this pin causes. Migrating to
35
- # the 2.x API is real follow-up work, not a version-string fix.
36
- "mcp>=1.0.0,<2.0.0",
26
+ # mcp 1.x AND 2.x are both supported (mcp_server._build_server picks the
27
+ # API at import): 2.x moved the low-level handlers from the
28
+ # @server.list_tools()/@server.call_tool() decorators to on_list_tools= /
29
+ # on_call_tool= constructor kwargs. The old <2 pin broke `awnode mcp` the
30
+ # moment a sibling aw brick (awrelay/awgraph/awdelphi need >=2) pulled
31
+ # 2.2 into the same env (2026-10-14); widening to <3 instead of >=2 keeps
32
+ # awdk[node] (still mcp<2) co-installable in one env.
33
+ "mcp>=1.0.0,<3.0.0",
37
34
  ]
38
35
 
39
36
  # No Repository/Documentation entry: Aitherium/awnode is a PRIVATE
@@ -0,0 +1,60 @@
1
+ """`awnode mcp` must import and serve on whichever mcp generation is installed.
2
+
3
+ 2026-10-14: a sibling aw brick pulled mcp 2.2 into the shared env and `awnode mcp`
4
+ died at import (`'Server' object has no attribute 'list_tools'`). The live SSE test
5
+ proves list+call end to end; these pin the transport-neutral core both API paths wrap.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import asyncio
10
+ import json
11
+
12
+ import awnode.mcp_server as ms
13
+
14
+
15
+ def test_module_builds_a_server_on_the_installed_mcp():
16
+ assert ms.server is not None
17
+ # 2.x registers on_list_tools via the constructor; 1.x via decorators. Either
18
+ # way a tools/list handler must exist, or the server answers nothing.
19
+ handlers = getattr(ms.server, "request_handlers", None)
20
+ if handlers is not None: # mcp 1.x
21
+ assert any("ListTools" in getattr(k, "__name__", "") for k in handlers)
22
+ else: # mcp 2.x
23
+ assert ms.server.get_request_handler("tools/list") is not None
24
+ assert ms.server.get_request_handler("tools/call") is not None
25
+
26
+
27
+ def test_unknown_tool_is_an_error(monkeypatch):
28
+ monkeypatch.setattr(ms, "_TOOL_REGISTRY", {})
29
+ text, is_error = asyncio.run(ms.dispatch_tool("nope", {}))
30
+ assert is_error is True
31
+ assert "Unknown tool" in json.loads(text)["error"]
32
+
33
+
34
+ def test_scope_refusal_is_an_error_and_hidden_from_the_list(monkeypatch):
35
+ def _f():
36
+ return "ran"
37
+
38
+ monkeypatch.setattr(ms, "_TOOL_REGISTRY", {"plat": {
39
+ "func": _f, "scope": "platform_only", "module": "x",
40
+ "description": "d", "schema": {"type": "object", "properties": {}},
41
+ }})
42
+ monkeypatch.setattr(ms, "_current_mode", ms.RuntimeMode.STANDALONE)
43
+ assert [t.name for t in ms.visible_tools()] == []
44
+ text, is_error = asyncio.run(ms.dispatch_tool("plat", {}))
45
+ assert is_error is True and json.loads(text)["scope"] == "platform_only"
46
+
47
+
48
+ def test_allowed_tool_runs(monkeypatch):
49
+ def _f(x: str = "") -> str:
50
+ return f"got {x}"
51
+
52
+ # "filesystem" is a free-plan module, so the subscription gate passes.
53
+ monkeypatch.setattr(ms, "_TOOL_REGISTRY", {"echo": {
54
+ "func": _f, "scope": "shell_local", "module": "filesystem",
55
+ "description": "d", "schema": {"type": "object", "properties": {}},
56
+ }})
57
+ monkeypatch.setattr(ms, "_current_mode", ms.RuntimeMode.STANDALONE)
58
+ assert [t.name for t in ms.visible_tools()] == ["echo"]
59
+ text, is_error = asyncio.run(ms.dispatch_tool("echo", {"x": "hi", "junk": 1}))
60
+ assert (text, is_error) == ("got hi", False)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes