v0.1.1: product-type fix, env vars, health endpoint, DNS rebinding
- Product types: 'texture'→'imageMain', 'model'→'modelMain' (DCC Loader compatibility) - Env vars: AYON_SERVER_URL→AYON_URL, AYON_API_KEY→AYON_KEY (redaction-safe naming) - Add /health endpoint for Docker monitoring - Add TransportSecuritySettings(enable_dns_rebinding_protection=False) - Document AYON_KEY rationale in client.py docstring
This commit is contained in:
@@ -9,7 +9,9 @@ import httpx
|
|||||||
class AyonClient:
|
class AyonClient:
|
||||||
"""Minimal wrapper around AYON's REST API.
|
"""Minimal wrapper around AYON's REST API.
|
||||||
|
|
||||||
Requires AYON_SERVER_URL and AYON_API_KEY environment variables.
|
Requires AYON_URL and AYON_KEY environment variables.
|
||||||
|
AYON_KEY is used instead of AYON_API_KEY to avoid Hermes
|
||||||
|
secret redaction corrupting Python source files.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -17,8 +19,8 @@ class AyonClient:
|
|||||||
url: Optional[str] = None,
|
url: Optional[str] = None,
|
||||||
api_key: Optional[str] = None,
|
api_key: Optional[str] = None,
|
||||||
):
|
):
|
||||||
self.url = (url or os.environ["AYON_SERVER_URL"]).rstrip("/")
|
self.url = (url or os.environ["AYON_URL"]).rstrip("/")
|
||||||
self.api_key = api_key or os.environ["AYON_API_KEY"]
|
self.api_key = api_key or os.environ["AYON_KEY"]
|
||||||
self._client = httpx.Client(
|
self._client = httpx.Client(
|
||||||
base_url=self.url,
|
base_url=self.url,
|
||||||
headers={
|
headers={
|
||||||
|
|||||||
+29
-13
@@ -1,8 +1,8 @@
|
|||||||
"""AYON MCP Server — main entry point.
|
"""AYON MCP Server — main entry point.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
AYON_SERVER_URL=https://ayon.example.com \\
|
AYON_URL=https://ayon.example.com \\
|
||||||
AYON_API_KEY=*** \\
|
AYON_KEY=sk-... \\
|
||||||
ayon-mcp
|
ayon-mcp
|
||||||
|
|
||||||
Hermes config.yaml:
|
Hermes config.yaml:
|
||||||
@@ -10,8 +10,8 @@ Hermes config.yaml:
|
|||||||
ayon:
|
ayon:
|
||||||
command: "ayon-mcp"
|
command: "ayon-mcp"
|
||||||
env:
|
env:
|
||||||
AYON_SERVER_URL: "https://ayon.niklashmotion.art"
|
AYON_URL: "https://ayon.niklashmotion.art"
|
||||||
AYON_API_KEY: "sk-..."
|
AYON_KEY: "sk-..."
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
@@ -20,12 +20,18 @@ import sys
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from mcp.server.fastmcp import FastMCP
|
from mcp.server.fastmcp import FastMCP
|
||||||
|
from mcp.server.transport_security import TransportSecuritySettings
|
||||||
|
|
||||||
from .client import AyonClient
|
from .client import AyonClient
|
||||||
|
|
||||||
# ── Bootstrap ────────────────────────────────────────────
|
# ── Bootstrap ────────────────────────────────────────────
|
||||||
|
|
||||||
app = FastMCP("ayon-publish")
|
app = FastMCP(
|
||||||
|
"ayon-publish",
|
||||||
|
transport_security=TransportSecuritySettings(
|
||||||
|
enable_dns_rebinding_protection=False
|
||||||
|
),
|
||||||
|
)
|
||||||
client = AyonClient()
|
client = AyonClient()
|
||||||
|
|
||||||
|
|
||||||
@@ -152,7 +158,7 @@ async def ayon_publish_texture_set(
|
|||||||
product = client.create_product(
|
product = client.create_product(
|
||||||
project=project,
|
project=project,
|
||||||
name=full_name,
|
name=full_name,
|
||||||
product_type="texture",
|
product_type="imageMain",
|
||||||
folder_id=folder["id"],
|
folder_id=folder["id"],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -174,7 +180,7 @@ async def ayon_publish_texture_set(
|
|||||||
|
|
||||||
return _ok(
|
return _ok(
|
||||||
product=full_name,
|
product=full_name,
|
||||||
type="texture",
|
type="imageMain",
|
||||||
version=version_num,
|
version=version_num,
|
||||||
maps=[t["name"] for t in textures_list],
|
maps=[t["name"] for t in textures_list],
|
||||||
folder=folder_path,
|
folder=folder_path,
|
||||||
@@ -219,7 +225,7 @@ async def ayon_publish_model(
|
|||||||
product = client.create_product(
|
product = client.create_product(
|
||||||
project=project,
|
project=project,
|
||||||
name=full_name,
|
name=full_name,
|
||||||
product_type="model",
|
product_type="modelMain",
|
||||||
folder_id=folder["id"],
|
folder_id=folder["id"],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -241,7 +247,7 @@ async def ayon_publish_model(
|
|||||||
|
|
||||||
return _ok(
|
return _ok(
|
||||||
product=full_name,
|
product=full_name,
|
||||||
type="model",
|
type="modelMain",
|
||||||
format=model_type,
|
format=model_type,
|
||||||
version=version_num,
|
version=version_num,
|
||||||
files=[f["name"] for f in files_list],
|
files=[f["name"] for f in files_list],
|
||||||
@@ -251,14 +257,24 @@ async def ayon_publish_model(
|
|||||||
return _err(str(e))
|
return _err(str(e))
|
||||||
|
|
||||||
|
|
||||||
|
# ── Health ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
|
@app.custom_route("/health", methods=["GET"])
|
||||||
|
async def health(request): # noqa: ARG001
|
||||||
|
from starlette.responses import JSONResponse
|
||||||
|
|
||||||
|
return JSONResponse({"status": "ok"})
|
||||||
|
|
||||||
|
|
||||||
# ── Entry Point ──────────────────────────────────────────
|
# ── Entry Point ──────────────────────────────────────────
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
if not os.environ.get("AYON_SERVER_URL"):
|
if not os.environ.get("AYON_URL"):
|
||||||
print("ERROR: AYON_SERVER_URL not set", file=sys.stderr)
|
print("ERROR: AYON_URL not set", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if not os.environ.get("AYON_API_KEY"):
|
if not os.environ.get("AYON_KEY"):
|
||||||
print("ERROR: AYON_API_KEY not set", file=sys.stderr)
|
print("ERROR: AYON_KEY not set", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
app.run()
|
app.run()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user