From e013e3f003e16b1b4da4a854945a243549300411 Mon Sep 17 00:00:00 2001 From: StatPan Date: Thu, 8 Jan 2026 01:26:14 +0900 Subject: [PATCH] fix: resolve service discovery issues and enhance deployment trigger - Copy bundled specs to cache on startup to ensure 270+ services are visible - Prevent failed or partial API list downloads from breaking discovery - Add environment metadata to infrastructure deployment payload --- .github/workflows/deploy.yml | 3 +- assemblymcp/initialization.py | 67 ++++++++++++++++++++--------------- 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 162bff0..fb48ae3 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -152,7 +152,8 @@ jobs: \"repository\": \"${{ github.repository }}\", \"sha\": \"${{ github.sha }}\", \"ref\": \"${{ github.ref }}\", - \"actor\": \"${{ github.actor }}\" + \"actor\": \"${{ github.actor }}\", + \"env\": \"production\" } }" diff --git a/assemblymcp/initialization.py b/assemblymcp/initialization.py index 5f2d073..094a141 100644 --- a/assemblymcp/initialization.py +++ b/assemblymcp/initialization.py @@ -1,5 +1,7 @@ import json import logging +import shutil +from pathlib import Path from assembly_client.api import AssemblyAPIClient @@ -13,29 +15,46 @@ async def ensure_master_list(client: AssemblyAPIClient) -> None: """ Ensure the master list of APIs (all_apis.json) exists in the cache. - If not, download it from the OPENSRVAPI service. + 1. Try to copy from the bundled specs in the package. + 2. If not found, try to download from the OPENSRVAPI service. Raises: - RuntimeError: If the master list cannot be downloaded or is invalid. + RuntimeError: If the master list cannot be obtained. """ cache_dir = client.spec_parser.cache_dir master_file = cache_dir / "all_apis.json" - if master_file.exists(): + # Ensure cache directory exists + cache_dir.mkdir(parents=True, exist_ok=True) + + # 1. Check if master file already exists in cache + if master_file.exists() and master_file.stat().st_size > 1000: logger.info(f"Master list found at {master_file}") return - logger.info(f"Master list not found at {master_file}. Downloading...") + # 2. Try to copy from bundled specs (AssemblyMCP/assemblymcp/specs/all_apis.json) + bundled_file = Path(__file__).parent / "specs" / "all_apis.json" + if bundled_file.exists(): + logger.info(f"Copying bundled master list from {bundled_file} to {master_file}") + shutil.copy(bundled_file, master_file) + + # Reload maps after copy + _reload_client_maps(client, cache_dir) + return + + # 3. Fallback: Download from API (Requires ASSEMBLY_API_KEY) + logger.info(f"Master list not found. Attempting to download...") + if not client.api_key: + logger.warning("ASSEMBLY_API_KEY is missing. Only limited tools will be available.") + return try: - # Directly construct URL since we can't use client.get_data - # (it depends on the master list for ID resolution) url = f"{client.BASE_URL}/{SERVICE_LIST_API_ID}" params = { "KEY": client.api_key, "Type": "json", "pIndex": 1, - "pSize": 1000, # Fetch all (there are ~270, so 1000 is safe) + "pSize": 1000, } logger.info(f"Fetching service list from {url}") @@ -43,33 +62,25 @@ async def ensure_master_list(client: AssemblyAPIClient) -> None: response.raise_for_status() data = response.json() - # Validate response if SERVICE_LIST_API_ID not in data: - if "RESULT" in data: - code = data["RESULT"].get("CODE") - msg = data["RESULT"].get("MESSAGE") - raise RuntimeError(f"Failed to fetch master list: {code} - {msg}") - raise RuntimeError(f"Invalid response format for master list: {data.keys()}") + raise RuntimeError(f"Invalid response format for master list") - # Save to file with open(master_file, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) - logger.info(f"Successfully saved master list to {master_file}") - - # Reload the client's service maps - # NOTE: This is a workaround as the assembly_client library doesn't provide - # a public API to reload specs. If the library is updated, consider using - # a public method instead of directly modifying internal attributes. - from assembly_client.parser import load_service_map, load_service_metadata - - client.service_map = load_service_map(cache_dir) - client.name_to_id = {name: sid for sid, name in client.service_map.items()} - client.service_metadata = load_service_metadata(cache_dir) - - logger.info(f"Reloaded service map: {len(client.service_map)} services found.") + _reload_client_maps(client, cache_dir) + logger.info(f"Successfully downloaded and reloaded master list.") except Exception as e: logger.error(f"Failed to initialize master list: {e}") - # Re-raise the exception to prevent the server from starting in a broken state. raise + + +def _reload_client_maps(client: AssemblyAPIClient, cache_dir: Path) -> None: + """Helper to reload client service maps from cache directory.""" + from assembly_client.parser import load_service_map, load_service_metadata + + client.service_map = load_service_map(cache_dir) + client.name_to_id = {name: sid for sid, name in client.service_map.items()} + client.service_metadata = load_service_metadata(cache_dir) + logger.info(f"Reloaded service map: {len(client.service_map)} services found.") \ No newline at end of file