From 4cc7271a2e1754658a077473b2f054da335356b7 Mon Sep 17 00:00:00 2001 From: georgiedekker Date: Tue, 6 May 2025 13:29:46 +0200 Subject: [PATCH 1/2] fixed --- main.py | 339 ++++++++++++++++++++++++++++++++++++++++++++++--- pyproject.toml | 27 +++- uv.lock | 251 ++++++++++++++++++++++++------------ 3 files changed, 512 insertions(+), 105 deletions(-) diff --git a/main.py b/main.py index b20781a..191605b 100644 --- a/main.py +++ b/main.py @@ -1,12 +1,38 @@ -from typing import Any, List, Optional -from mcp.server.fastmcp import FastMCP +from typing import Any, List, Optional, Dict, Union +from fastmcp import FastMCP, Context import subprocess from pydantic import Field import json +import os +import tempfile +import glob +import pathlib +import yaml as pyyaml # Import pyyaml for create_new_rule -# Initialize FastMCP server +# Initialize FastMCP server - capabilities handled differently or default? +# FastMCP might auto-detect capabilities based on registered handlers. +# Let's remove explicit capabilities for now, can add back if needed. mcp = FastMCP("ast-grep") +RULES_DIR = "sg_rules" # Define a standard directory for rules + +# Helper function to ensure rules directory exists +def ensure_rules_dir(project_folder: str) -> pathlib.Path: + # Check if project_folder is valid + proj_path = pathlib.Path(project_folder) + if not proj_path.is_dir(): + # Use ValueError for invalid parameters + raise ValueError(f"Project folder '{project_folder}' not found or is not a directory.") + rules_path = proj_path / RULES_DIR + try: + rules_path.mkdir(parents=True, exist_ok=True) + except OSError as e: + # Use RuntimeError for internal/OS errors + raise RuntimeError(f"Could not create rules directory '{rules_path}': {e}") + return rules_path + +# --- Tool Implementation (Decorators likely remain the same) --- # + @mcp.tool() def find_code( project_folder: str = Field(description="The path to the project folder"), @@ -32,45 +58,316 @@ def find_code_by_rule( """ return run_ast_grep_yaml(yaml, project_folder) +# --- Subprocess Execution Helpers (No changes needed for library migration) --- # + def run_ast_grep_command(pattern: str, project_folder: str, language: Optional[str]) -> List[dict[str, Any]]: + # Check project folder exists before running subprocess + if not pathlib.Path(project_folder).is_dir(): + raise ValueError(f"Project folder '{project_folder}' not found.") try: args = ["ast-grep", "--pattern", pattern, "--json", project_folder] if language: args.extend(["--lang", language]) - # Run command and capture output result = subprocess.run( args, capture_output=True, text=True, - check=True # Raises CalledProcessError if return code is non-zero + check=False # Let json_processor handle errors ) - return json.loads(result.stdout) - except subprocess.CalledProcessError as e: - print(f"Command failed with return code {e.returncode}") - print("Error output:", e.stderr) - return e.stderr + processed_output = json_processor(result) + if isinstance(processed_output, str): # Check if json_processor returned an error string + raise RuntimeError(processed_output) + return processed_output except FileNotFoundError: - print("Command not found") - return [] + raise RuntimeError("'ast-grep' command not found. Ensure it is installed and in PATH.") + except Exception as e: + # Catch other potential errors during subprocess run + raise RuntimeError(f"Error running find_code: {e}") def run_ast_grep_yaml(yaml: str, project_folder: str) -> List[dict[str, Any]]: + # Check project folder exists + if not pathlib.Path(project_folder).is_dir(): + raise ValueError(f"Project folder '{project_folder}' not found.") try: args = ["ast-grep", "scan","--inline-rules", yaml, "--json", project_folder] - # Run command and capture output result = subprocess.run( args, capture_output=True, text=True, - check=True # Raises CalledProcessError if return code is non-zero + check=False # Let json_processor handle errors ) - return json.loads(result.stdout) - except subprocess.CalledProcessError as e: - print(f"Command failed with return code {e.returncode}") - print("Error output:", e.stderr) - return e.stderr + processed_output = json_processor(result) + if isinstance(processed_output, str): # Check if json_processor returned an error string + raise RuntimeError(processed_output) + return processed_output except FileNotFoundError: - print("Command not found") - return [] + raise RuntimeError("'ast-grep' command not found. Ensure it is installed and in PATH.") + except Exception as e: + raise RuntimeError(f"Error running find_code_by_rule: {e}") + + +@mcp.tool() +def get_supported_languages() -> List[str]: + """Returns supported languages list""" + args = ["ast-grep", "language", "--list"] + return handle_subprocess(args, list_processor) + +@mcp.tool() +def get_language_kinds(language: str) -> List[str]: + """Returns AST node kinds for a language""" + args = ["ast-grep", "language", "--kinds", language] + return handle_subprocess(args, list_processor) + +@mcp.tool() +def dump_ast(code: str, language: str) -> Dict[str, Any]: + """Dumps AST structure of code""" + # Ensure language is alphanumeric for file extension safety + safe_lang = ''.join(filter(str.isalnum, language)) or 'txt' + with tempfile.NamedTemporaryFile(mode='w', suffix=f'.{safe_lang}', delete=True) as f: + f.write(code) + temp_path = f.name + # Ensure file is flushed before ast-grep reads it + f.flush() + os.fsync(f.fileno()) + args = ["ast-grep", "dump", "--json", temp_path] + # Pass temp_path=None to handle_subprocess as it's auto-deleted + return handle_subprocess(args, json_processor, cleanup=None) + +@mcp.tool() +def validate_rule_syntax(rule: str) -> Dict[str, Any]: + """Validates YAML rule syntax""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.yml', delete=True) as f: + f.write(rule) + temp_path = f.name + f.flush() + os.fsync(f.fileno()) + args = ["ast-grep", "scan", "--validate", temp_path, "--no-ignore"] + return handle_subprocess(args, validation_processor, cleanup=None) + +@mcp.tool() +def list_rules(project_folder: str = Field(description="The path to the project folder")) -> List[str]: + """Lists YAML rule files in the project's sg_rules directory.""" + try: + rules_path = ensure_rules_dir(project_folder) + rule_files = [f.name for f in rules_path.glob('*.yml')] + rule_files.extend([f.name for f in rules_path.glob('*.yaml')]) + return rule_files + except (ValueError, RuntimeError) as e: # Catch specific errors from ensure_rules_dir + raise e + except Exception as e: + # Wrap other exceptions + raise RuntimeError(f"Error listing rules: {e}") + +@mcp.tool() +def get_rule(project_folder: str = Field(description="The path to the project folder"), + rule_name: str = Field(description="The name of the rule file (e.g., my-rule.yml)")) -> str: + """Gets the content of a specific YAML rule file from the project's sg_rules directory.""" + try: + rules_path = ensure_rules_dir(project_folder) + rule_file_path = rules_path / rule_name + if not rule_file_path.is_file(): + raise FileNotFoundError(f"Rule file '{rule_name}' not found in {rules_path}.") + with open(rule_file_path, 'r') as f: + return f.read() + except (ValueError, RuntimeError, FileNotFoundError) as e: + raise e + except Exception as e: + raise RuntimeError(f"Error reading rule file '{rule_name}': {e}") + +@mcp.tool() +def create_new_rule(project_folder: str = Field(description="The path to the project folder"), + rule_name: str = Field(description="The desired name for the new rule file (e.g., new-rule.yml)"), + rule_content: str = Field(description="The YAML content for the new rule")) -> str: + """Creates a new YAML rule file in the project's sg_rules directory.""" + if not (rule_name.endswith(".yml") or rule_name.endswith(".yaml")): + raise ValueError("Rule name must end with .yml or .yaml") + + try: + rules_path = ensure_rules_dir(project_folder) + rule_file_path = rules_path / rule_name + + if rule_file_path.exists(): + raise ValueError(f"Rule file '{rule_name}' already exists in {rules_path}.") + + # Basic YAML validation check before writing + try: + pyyaml.safe_load(rule_content) + except pyyaml.YAMLError as yaml_err: + raise ValueError(f"Invalid YAML syntax - {yaml_err}") + + with open(rule_file_path, 'w') as f: + f.write(rule_content) + return f"Successfully created rule file '{rule_name}' in {rules_path}." + except (ValueError, RuntimeError) as e: + raise e + except Exception as e: + raise RuntimeError(f"Error creating rule file '{rule_name}': {e}") + + +@mcp.tool() +def list_all_diagnostics_in_the_workspace(project_folder: str = Field(description="The path to the project folder")) -> Union[List[dict[str, Any]], str]: + """Runs 'sg scan --json' to find diagnostics based on rules in sg_rules directory.""" + try: + rules_path = ensure_rules_dir(project_folder) + rule_files = list(rules_path.glob('*.yml')) + list(rules_path.glob('*.yaml')) + if not rule_files: + return "No rule files found in the sg_rules directory. Cannot run scan." + + args = ["ast-grep", "scan", "-r", str(rules_path), "--json", project_folder] + return handle_subprocess(args, json_processor) + except (ValueError, RuntimeError) as e: + raise e + except Exception as e: + raise RuntimeError(f"Error running diagnostics scan: {e}") + +# --- Resource Implementation (Adapting to fastmcp conventions) --- # + +# Resource to list local rules +@mcp.resource("rule://sg_rules") +async def list_local_rules_resource(project_folder: str) -> List[Dict[str, str]]: + """Lists local rule files as resources. Requires project_folder.""" + if not project_folder: + raise ValueError("Listing local rules requires the 'project_folder' argument.") + try: + rules_path = ensure_rules_dir(project_folder) + rule_files = list(rules_path.glob('*.yml')) + list(rules_path.glob('*.yaml')) + return [ + { + "uri": f"rule://sg_rules/{rule_file.name}", + "name": f"Rule: {rule_file.name}", + # Add other resource metadata if needed by fastmcp resource model + } + for rule_file in rule_files + ] + except (ValueError, RuntimeError) as e: + raise e + except Exception as e: + raise RuntimeError(f"Error listing local rules resource: {e}") + +# Template Resource to read a specific local rule +@mcp.resource("rule://sg_rules/{rule_name}") +async def read_local_rule_resource(rule_name: str, project_folder: str) -> Dict[str, Any]: + """Reads a specific local rule file. Requires project_folder.""" + if not project_folder: + raise ValueError("Reading local rules requires the 'project_folder' argument.") + try: + # Validate rule_name format slightly + if not (rule_name.endswith(".yml") or rule_name.endswith(".yaml")): + raise ValueError("Invalid rule name format.") + + rules_path = ensure_rules_dir(project_folder) + rule_file_path = rules_path / rule_name + + if not rule_file_path.is_file(): + raise FileNotFoundError(f"Rule file '{rule_name}' not found in {rules_path}.") # Use NotFound + + with open(rule_file_path, 'r') as f: + content = f.read() + return { + "uri": f"rule://sg_rules/{rule_name}", + "mimeType": "application/yaml", + "content": content # fastmcp might expect 'content' instead of 'text' + } + except (ValueError, RuntimeError, FileNotFoundError) as e: + raise e + except Exception as e: + raise RuntimeError(f"Error reading rule file '{rule_name}': {e}") + +# Static Resource for mock global rule +@mcp.resource("rule://global/example-global-rule.yml") +def global_rule_resource() -> Dict[str, Any]: + """Provides mock content for the global rule example.""" + return { + "uri": "rule://global/example-global-rule.yml", + "mimeType": "application/yaml", + "content": "# Mock Global Rule Content\nlanguage: python\nrule:\n pattern: print($ARG)" + } + +# Static Resource for mock python test case +@mcp.resource("testcase://python/example-test.py") +def python_test_case_resource() -> Dict[str, Any]: + """Provides mock content for the python test case example.""" + return { + "uri": "testcase://python/example-test.py", + "mimeType": "text/x-python", + "content": "# Mock Python Test Case\nimport os\n\nprint(\"Hello, world!\")\n\ndef old_function(x):\n return x * 2" + } + +# Static Resource for mock javascript test case +@mcp.resource("testcase://javascript/example-test.js") +def javascript_test_case_resource() -> Dict[str, Any]: + """Provides mock content for the javascript test case example.""" + return { + "uri": "testcase://javascript/example-test.js", + "mimeType": "text/javascript", + "content": "// Mock JavaScript Test Case\nconsole.log(\"Hello, world!\");\n\nfunction deprecatedFunc(a) {\n return a + 1;\n}" + } + +# --- Subprocess Result Processors (Refined error propagation) --- # + +def validation_processor(result: subprocess.CompletedProcess) -> Dict[str, Any]: + if result.returncode != 0 and result.stderr: + # Raise error if validation fails and there's stderr output + raise RuntimeError(f"Rule validation failed: {result.stderr}") + elif result.returncode != 0: + raise RuntimeError(f"Rule validation failed with exit code {result.returncode}. No specific error message.") + return { + "valid": True, + "message": "Valid rule", + "error": None + } + +def json_processor(result: subprocess.CompletedProcess) -> Union[List[Dict[str, Any]], Dict[str, Any]]: # Allow single dict too + if result.returncode == 0: + if not result.stdout: + return [] # Return empty list for empty output + try: + return json.loads(result.stdout) + except json.JSONDecodeError as e: + # Raise an error if JSON decoding fails + raise RuntimeError(f"Error decoding JSON output: {e}\nRaw output:\n{result.stdout}") + else: + error_message = f"Command failed with return code {result.returncode}." + if result.stderr: + error_message += f"\nError output:\n{result.stderr}" + raise RuntimeError(error_message) + +def list_processor(result: subprocess.CompletedProcess) -> List[str]: + if result.returncode == 0: + return result.stdout.strip().split('\n') if result.stdout else [] + else: + raise RuntimeError(f"Command failed with return code {result.returncode}: {result.stderr}") + +def error_handler(e: Exception, cleanup: Optional[str] = None) -> Exception: # Return the original or wrapped exception + if cleanup and os.path.exists(cleanup): + try: + os.unlink(cleanup) + except OSError as unlink_err: + print(f"Warning: Failed to remove temp file '{cleanup}': {unlink_err}") + + # Re-raise standard Python exceptions directly, wrap others in RuntimeError + if isinstance(e, (ValueError, FileNotFoundError, RuntimeError, subprocess.CalledProcessError)): + return e + elif isinstance(e, FileNotFoundError): # This specific FileNotFoundError check for ast-grep + return RuntimeError("'ast-grep' command not found. Please ensure it is installed and in your PATH.") + else: + return RuntimeError(f"An unexpected error occurred: {e}") + +# Updated handle_subprocess to raise RuntimeError instead of returning strings on error +def handle_subprocess(args, processor, cleanup=None): + try: + result = subprocess.run(args, capture_output=True, text=True, check=False) + output = processor(result) # Processor now raises RuntimeError on failure + if cleanup and os.path.exists(cleanup): + try: os.unlink(cleanup) + except OSError as e: print(f"Warning: Failed to cleanup {cleanup}: {e}") + return output + except Exception as e: + # Wrap exceptions from run() or processor() using error_handler + raise error_handler(e, cleanup) + +# --- Main Execution --- # if __name__ == "__main__": + # No need to check for pyyaml import here if it's in pyproject.toml mcp.run(transport = "stdio") diff --git a/pyproject.toml b/pyproject.toml index 938787a..ba419f3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,31 @@ [project] name = "sg-mcp" version = "0.1.0" -description = "Add your description here" +description = "AST-based code search MCP server using ast-grep" +authors = [{name = "Your Name", email = "your.email@example.com"}] readme = "README.md" requires-python = ">=3.13" + dependencies = [ - "pydantic>=2.11.0", - "mcp[cli]>=1.6.0", + "pydantic==2.11.1", + "pydantic-core==2.33.0", + "fastmcp", + "anyio==4.9.0", + "httpx==0.28.1", + "httpx-sse==0.4.0", + "pydantic-settings==2.8.1", + "python-dotenv==1.1.0", + "starlette==0.46.1", + "uvicorn==0.34.0", + "pyyaml", ] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +include = ["main.py"] + +[project.scripts] +sg-mcp = "main:mcp.run" \ No newline at end of file diff --git a/uv.lock b/uv.lock index b75522b..0f21b40 100644 --- a/uv.lock +++ b/uv.lock @@ -1,14 +1,14 @@ version = 1 -revision = 1 +revision = 2 requires-python = ">=3.13" [[package]] name = "annotated-types" version = "0.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, ] [[package]] @@ -19,18 +19,18 @@ dependencies = [ { name = "idna" }, { name = "sniffio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949 } +sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949, upload-time = "2025-03-17T00:02:54.77Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916 }, + { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916, upload-time = "2025-03-17T00:02:52.713Z" }, ] [[package]] name = "certifi" version = "2025.1.31" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 } +sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577, upload-time = "2025-01-31T02:16:47.166Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 }, + { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393, upload-time = "2025-01-31T02:16:45.015Z" }, ] [[package]] @@ -40,27 +40,55 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, ] [[package]] name = "colorama" version = "0.4.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "exceptiongroup" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883, upload-time = "2024-07-12T22:26:00.161Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453, upload-time = "2024-07-12T22:25:58.476Z" }, +] + +[[package]] +name = "fastmcp" +version = "2.2.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "exceptiongroup" }, + { name = "httpx" }, + { name = "mcp" }, + { name = "openapi-pydantic" }, + { name = "python-dotenv" }, + { name = "rich" }, + { name = "typer" }, + { name = "websockets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6b/be/17c9bea3d1dd9eea293967ba8736d06b720ef9be7178a52f3402b99a2834/fastmcp-2.2.6.tar.gz", hash = "sha256:88a371b7587658bc8911e2a0e29e9300b76fba91808c8d5283cd4796f7a88590", size = 940585, upload-time = "2025-04-30T17:13:38.391Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/59/2eadf92f5d3f6128a729a6d2ea4bc980e204b3d05f643f31494732fb8972/fastmcp-2.2.6-py3-none-any.whl", hash = "sha256:bb90590bcc408155912ffb96fd43d9939e5aa486dd35274f2c736f87d97f2125", size = 86320, upload-time = "2025-04-30T17:13:36.557Z" }, ] [[package]] name = "h11" version = "0.14.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } +sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418, upload-time = "2022-09-25T15:40:01.519Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, + { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259, upload-time = "2022-09-25T15:39:59.68Z" }, ] [[package]] @@ -71,9 +99,9 @@ dependencies = [ { name = "certifi" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6a/41/d7d0a89eb493922c37d343b607bc1b5da7f5be7e383740b4753ad8943e90/httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", size = 85196 } +sdist = { url = "https://files.pythonhosted.org/packages/6a/41/d7d0a89eb493922c37d343b607bc1b5da7f5be7e383740b4753ad8943e90/httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", size = 85196, upload-time = "2024-11-15T12:30:47.531Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/87/f5/72347bc88306acb359581ac4d52f23c0ef445b57157adedb9aee0cd689d2/httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd", size = 78551 }, + { url = "https://files.pythonhosted.org/packages/87/f5/72347bc88306acb359581ac4d52f23c0ef445b57157adedb9aee0cd689d2/httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd", size = 78551, upload-time = "2024-11-15T12:30:45.782Z" }, ] [[package]] @@ -86,27 +114,27 @@ dependencies = [ { name = "httpcore" }, { name = "idna" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, ] [[package]] name = "httpx-sse" version = "0.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624 } +sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624, upload-time = "2023-12-22T08:01:21.083Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819 }, + { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819, upload-time = "2023-12-22T08:01:19.89Z" }, ] [[package]] name = "idna" version = "3.10" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, ] [[package]] @@ -116,9 +144,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mdurl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" }, ] [[package]] @@ -135,24 +163,30 @@ dependencies = [ { name = "starlette" }, { name = "uvicorn" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/95/d2/f587cb965a56e992634bebc8611c5b579af912b74e04eb9164bd49527d21/mcp-1.6.0.tar.gz", hash = "sha256:d9324876de2c5637369f43161cd71eebfd803df5a95e46225cab8d280e366723", size = 200031 } +sdist = { url = "https://files.pythonhosted.org/packages/95/d2/f587cb965a56e992634bebc8611c5b579af912b74e04eb9164bd49527d21/mcp-1.6.0.tar.gz", hash = "sha256:d9324876de2c5637369f43161cd71eebfd803df5a95e46225cab8d280e366723", size = 200031, upload-time = "2025-03-27T16:46:32.336Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/30/20a7f33b0b884a9d14dd3aa94ff1ac9da1479fe2ad66dd9e2736075d2506/mcp-1.6.0-py3-none-any.whl", hash = "sha256:7bd24c6ea042dbec44c754f100984d186620d8b841ec30f1b19eda9b93a634d0", size = 76077 }, -] - -[package.optional-dependencies] -cli = [ - { name = "python-dotenv" }, - { name = "typer" }, + { url = "https://files.pythonhosted.org/packages/10/30/20a7f33b0b884a9d14dd3aa94ff1ac9da1479fe2ad66dd9e2736075d2506/mcp-1.6.0-py3-none-any.whl", hash = "sha256:7bd24c6ea042dbec44c754f100984d186620d8b841ec30f1b19eda9b93a634d0", size = 76077, upload-time = "2025-03-27T16:46:29.919Z" }, ] [[package]] name = "mdurl" version = "0.1.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + +[[package]] +name = "openapi-pydantic" +version = "0.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/02/2e/58d83848dd1a79cb92ed8e63f6ba901ca282c5f09d04af9423ec26c56fd7/openapi_pydantic-0.5.1.tar.gz", hash = "sha256:ff6835af6bde7a459fb93eb93bb92b8749b754fc6e51b2f1590a19dc3005ee0d", size = 60892, upload-time = "2025-01-08T19:29:27.083Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, + { url = "https://files.pythonhosted.org/packages/12/cf/03675d8bd8ecbf4445504d8071adab19f5f993676795708e36402ab38263/openapi_pydantic-0.5.1-py3-none-any.whl", hash = "sha256:a3a09ef4586f5bd760a8df7f43028b60cafb6d9f61de2acba9574766255ab146", size = 96381, upload-time = "2025-01-08T19:29:25.275Z" }, ] [[package]] @@ -165,9 +199,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/93/a3/698b87a4d4d303d7c5f62ea5fbf7a79cab236ccfbd0a17847b7f77f8163e/pydantic-2.11.1.tar.gz", hash = "sha256:442557d2910e75c991c39f4b4ab18963d57b9b55122c8b2a9cd176d8c29ce968", size = 782817 } +sdist = { url = "https://files.pythonhosted.org/packages/93/a3/698b87a4d4d303d7c5f62ea5fbf7a79cab236ccfbd0a17847b7f77f8163e/pydantic-2.11.1.tar.gz", hash = "sha256:442557d2910e75c991c39f4b4ab18963d57b9b55122c8b2a9cd176d8c29ce968", size = 782817, upload-time = "2025-03-28T21:14:58.347Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/12/f9221a949f2419e2e23847303c002476c26fbcfd62dc7f3d25d0bec5ca99/pydantic-2.11.1-py3-none-any.whl", hash = "sha256:5b6c415eee9f8123a14d859be0c84363fec6b1feb6b688d6435801230b56e0b8", size = 442648 }, + { url = "https://files.pythonhosted.org/packages/cc/12/f9221a949f2419e2e23847303c002476c26fbcfd62dc7f3d25d0bec5ca99/pydantic-2.11.1-py3-none-any.whl", hash = "sha256:5b6c415eee9f8123a14d859be0c84363fec6b1feb6b688d6435801230b56e0b8", size = 442648, upload-time = "2025-03-28T21:14:55.856Z" }, ] [[package]] @@ -177,25 +211,25 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/05/91ce14dfd5a3a99555fce436318cc0fd1f08c4daa32b3248ad63669ea8b4/pydantic_core-2.33.0.tar.gz", hash = "sha256:40eb8af662ba409c3cbf4a8150ad32ae73514cd7cb1f1a2113af39763dd616b3", size = 434080 } +sdist = { url = "https://files.pythonhosted.org/packages/b9/05/91ce14dfd5a3a99555fce436318cc0fd1f08c4daa32b3248ad63669ea8b4/pydantic_core-2.33.0.tar.gz", hash = "sha256:40eb8af662ba409c3cbf4a8150ad32ae73514cd7cb1f1a2113af39763dd616b3", size = 434080, upload-time = "2025-03-26T20:30:05.906Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/79/20/de2ad03ce8f5b3accf2196ea9b44f31b0cd16ac6e8cfc6b21976ed45ec35/pydantic_core-2.33.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f00e8b59e1fc8f09d05594aa7d2b726f1b277ca6155fc84c0396db1b373c4555", size = 2032214 }, - { url = "https://files.pythonhosted.org/packages/f9/af/6817dfda9aac4958d8b516cbb94af507eb171c997ea66453d4d162ae8948/pydantic_core-2.33.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1a73be93ecef45786d7d95b0c5e9b294faf35629d03d5b145b09b81258c7cd6d", size = 1852338 }, - { url = "https://files.pythonhosted.org/packages/44/f3/49193a312d9c49314f2b953fb55740b7c530710977cabe7183b8ef111b7f/pydantic_core-2.33.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff48a55be9da6930254565ff5238d71d5e9cd8c5487a191cb85df3bdb8c77365", size = 1896913 }, - { url = "https://files.pythonhosted.org/packages/06/e0/c746677825b2e29a2fa02122a8991c83cdd5b4c5f638f0664d4e35edd4b2/pydantic_core-2.33.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26a4ea04195638dcd8c53dadb545d70badba51735b1594810e9768c2c0b4a5da", size = 1986046 }, - { url = "https://files.pythonhosted.org/packages/11/ec/44914e7ff78cef16afb5e5273d480c136725acd73d894affdbe2a1bbaad5/pydantic_core-2.33.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:41d698dcbe12b60661f0632b543dbb119e6ba088103b364ff65e951610cb7ce0", size = 2128097 }, - { url = "https://files.pythonhosted.org/packages/fe/f5/c6247d424d01f605ed2e3802f338691cae17137cee6484dce9f1ac0b872b/pydantic_core-2.33.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ae62032ef513fe6281ef0009e30838a01057b832dc265da32c10469622613885", size = 2681062 }, - { url = "https://files.pythonhosted.org/packages/f0/85/114a2113b126fdd7cf9a9443b1b1fe1b572e5bd259d50ba9d5d3e1927fa9/pydantic_core-2.33.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f225f3a3995dbbc26affc191d0443c6c4aa71b83358fd4c2b7d63e2f6f0336f9", size = 2007487 }, - { url = "https://files.pythonhosted.org/packages/e6/40/3c05ed28d225c7a9acd2b34c5c8010c279683a870219b97e9f164a5a8af0/pydantic_core-2.33.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5bdd36b362f419c78d09630cbaebc64913f66f62bda6d42d5fbb08da8cc4f181", size = 2121382 }, - { url = "https://files.pythonhosted.org/packages/8a/22/e70c086f41eebd323e6baa92cc906c3f38ddce7486007eb2bdb3b11c8f64/pydantic_core-2.33.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:2a0147c0bef783fd9abc9f016d66edb6cac466dc54a17ec5f5ada08ff65caf5d", size = 2072473 }, - { url = "https://files.pythonhosted.org/packages/3e/84/d1614dedd8fe5114f6a0e348bcd1535f97d76c038d6102f271433cd1361d/pydantic_core-2.33.0-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:c860773a0f205926172c6644c394e02c25421dc9a456deff16f64c0e299487d3", size = 2249468 }, - { url = "https://files.pythonhosted.org/packages/b0/c0/787061eef44135e00fddb4b56b387a06c303bfd3884a6df9bea5cb730230/pydantic_core-2.33.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:138d31e3f90087f42aa6286fb640f3c7a8eb7bdae829418265e7e7474bd2574b", size = 2254716 }, - { url = "https://files.pythonhosted.org/packages/ae/e2/27262eb04963201e89f9c280f1e10c493a7a37bc877e023f31aa72d2f911/pydantic_core-2.33.0-cp313-cp313-win32.whl", hash = "sha256:d20cbb9d3e95114325780f3cfe990f3ecae24de7a2d75f978783878cce2ad585", size = 1916450 }, - { url = "https://files.pythonhosted.org/packages/13/8d/25ff96f1e89b19e0b70b3cd607c9ea7ca27e1dcb810a9cd4255ed6abf869/pydantic_core-2.33.0-cp313-cp313-win_amd64.whl", hash = "sha256:ca1103d70306489e3d006b0f79db8ca5dd3c977f6f13b2c59ff745249431a606", size = 1956092 }, - { url = "https://files.pythonhosted.org/packages/1b/64/66a2efeff657b04323ffcd7b898cb0354d36dae3a561049e092134a83e9c/pydantic_core-2.33.0-cp313-cp313-win_arm64.whl", hash = "sha256:6291797cad239285275558e0a27872da735b05c75d5237bbade8736f80e4c225", size = 1908367 }, - { url = "https://files.pythonhosted.org/packages/52/54/295e38769133363d7ec4a5863a4d579f331728c71a6644ff1024ee529315/pydantic_core-2.33.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7b79af799630af263eca9ec87db519426d8c9b3be35016eddad1832bac812d87", size = 1813331 }, - { url = "https://files.pythonhosted.org/packages/4c/9c/0c8ea02db8d682aa1ef48938abae833c1d69bdfa6e5ec13b21734b01ae70/pydantic_core-2.33.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eabf946a4739b5237f4f56d77fa6668263bc466d06a8036c055587c130a46f7b", size = 1986653 }, - { url = "https://files.pythonhosted.org/packages/8e/4f/3fb47d6cbc08c7e00f92300e64ba655428c05c56b8ab6723bd290bae6458/pydantic_core-2.33.0-cp313-cp313t-win_amd64.whl", hash = "sha256:8a1d581e8cdbb857b0e0e81df98603376c1a5c34dc5e54039dcc00f043df81e7", size = 1931234 }, + { url = "https://files.pythonhosted.org/packages/79/20/de2ad03ce8f5b3accf2196ea9b44f31b0cd16ac6e8cfc6b21976ed45ec35/pydantic_core-2.33.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f00e8b59e1fc8f09d05594aa7d2b726f1b277ca6155fc84c0396db1b373c4555", size = 2032214, upload-time = "2025-03-26T20:27:56.197Z" }, + { url = "https://files.pythonhosted.org/packages/f9/af/6817dfda9aac4958d8b516cbb94af507eb171c997ea66453d4d162ae8948/pydantic_core-2.33.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1a73be93ecef45786d7d95b0c5e9b294faf35629d03d5b145b09b81258c7cd6d", size = 1852338, upload-time = "2025-03-26T20:27:57.876Z" }, + { url = "https://files.pythonhosted.org/packages/44/f3/49193a312d9c49314f2b953fb55740b7c530710977cabe7183b8ef111b7f/pydantic_core-2.33.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff48a55be9da6930254565ff5238d71d5e9cd8c5487a191cb85df3bdb8c77365", size = 1896913, upload-time = "2025-03-26T20:27:59.719Z" }, + { url = "https://files.pythonhosted.org/packages/06/e0/c746677825b2e29a2fa02122a8991c83cdd5b4c5f638f0664d4e35edd4b2/pydantic_core-2.33.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26a4ea04195638dcd8c53dadb545d70badba51735b1594810e9768c2c0b4a5da", size = 1986046, upload-time = "2025-03-26T20:28:01.583Z" }, + { url = "https://files.pythonhosted.org/packages/11/ec/44914e7ff78cef16afb5e5273d480c136725acd73d894affdbe2a1bbaad5/pydantic_core-2.33.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:41d698dcbe12b60661f0632b543dbb119e6ba088103b364ff65e951610cb7ce0", size = 2128097, upload-time = "2025-03-26T20:28:03.437Z" }, + { url = "https://files.pythonhosted.org/packages/fe/f5/c6247d424d01f605ed2e3802f338691cae17137cee6484dce9f1ac0b872b/pydantic_core-2.33.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ae62032ef513fe6281ef0009e30838a01057b832dc265da32c10469622613885", size = 2681062, upload-time = "2025-03-26T20:28:05.498Z" }, + { url = "https://files.pythonhosted.org/packages/f0/85/114a2113b126fdd7cf9a9443b1b1fe1b572e5bd259d50ba9d5d3e1927fa9/pydantic_core-2.33.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f225f3a3995dbbc26affc191d0443c6c4aa71b83358fd4c2b7d63e2f6f0336f9", size = 2007487, upload-time = "2025-03-26T20:28:07.879Z" }, + { url = "https://files.pythonhosted.org/packages/e6/40/3c05ed28d225c7a9acd2b34c5c8010c279683a870219b97e9f164a5a8af0/pydantic_core-2.33.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5bdd36b362f419c78d09630cbaebc64913f66f62bda6d42d5fbb08da8cc4f181", size = 2121382, upload-time = "2025-03-26T20:28:09.651Z" }, + { url = "https://files.pythonhosted.org/packages/8a/22/e70c086f41eebd323e6baa92cc906c3f38ddce7486007eb2bdb3b11c8f64/pydantic_core-2.33.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:2a0147c0bef783fd9abc9f016d66edb6cac466dc54a17ec5f5ada08ff65caf5d", size = 2072473, upload-time = "2025-03-26T20:28:11.69Z" }, + { url = "https://files.pythonhosted.org/packages/3e/84/d1614dedd8fe5114f6a0e348bcd1535f97d76c038d6102f271433cd1361d/pydantic_core-2.33.0-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:c860773a0f205926172c6644c394e02c25421dc9a456deff16f64c0e299487d3", size = 2249468, upload-time = "2025-03-26T20:28:13.651Z" }, + { url = "https://files.pythonhosted.org/packages/b0/c0/787061eef44135e00fddb4b56b387a06c303bfd3884a6df9bea5cb730230/pydantic_core-2.33.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:138d31e3f90087f42aa6286fb640f3c7a8eb7bdae829418265e7e7474bd2574b", size = 2254716, upload-time = "2025-03-26T20:28:16.105Z" }, + { url = "https://files.pythonhosted.org/packages/ae/e2/27262eb04963201e89f9c280f1e10c493a7a37bc877e023f31aa72d2f911/pydantic_core-2.33.0-cp313-cp313-win32.whl", hash = "sha256:d20cbb9d3e95114325780f3cfe990f3ecae24de7a2d75f978783878cce2ad585", size = 1916450, upload-time = "2025-03-26T20:28:18.252Z" }, + { url = "https://files.pythonhosted.org/packages/13/8d/25ff96f1e89b19e0b70b3cd607c9ea7ca27e1dcb810a9cd4255ed6abf869/pydantic_core-2.33.0-cp313-cp313-win_amd64.whl", hash = "sha256:ca1103d70306489e3d006b0f79db8ca5dd3c977f6f13b2c59ff745249431a606", size = 1956092, upload-time = "2025-03-26T20:28:20.129Z" }, + { url = "https://files.pythonhosted.org/packages/1b/64/66a2efeff657b04323ffcd7b898cb0354d36dae3a561049e092134a83e9c/pydantic_core-2.33.0-cp313-cp313-win_arm64.whl", hash = "sha256:6291797cad239285275558e0a27872da735b05c75d5237bbade8736f80e4c225", size = 1908367, upload-time = "2025-03-26T20:28:22.498Z" }, + { url = "https://files.pythonhosted.org/packages/52/54/295e38769133363d7ec4a5863a4d579f331728c71a6644ff1024ee529315/pydantic_core-2.33.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7b79af799630af263eca9ec87db519426d8c9b3be35016eddad1832bac812d87", size = 1813331, upload-time = "2025-03-26T20:28:25.004Z" }, + { url = "https://files.pythonhosted.org/packages/4c/9c/0c8ea02db8d682aa1ef48938abae833c1d69bdfa6e5ec13b21734b01ae70/pydantic_core-2.33.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eabf946a4739b5237f4f56d77fa6668263bc466d06a8036c055587c130a46f7b", size = 1986653, upload-time = "2025-03-26T20:28:27.02Z" }, + { url = "https://files.pythonhosted.org/packages/8e/4f/3fb47d6cbc08c7e00f92300e64ba655428c05c56b8ab6723bd290bae6458/pydantic_core-2.33.0-cp313-cp313t-win_amd64.whl", hash = "sha256:8a1d581e8cdbb857b0e0e81df98603376c1a5c34dc5e54039dcc00f043df81e7", size = 1931234, upload-time = "2025-03-26T20:28:29.237Z" }, ] [[package]] @@ -206,27 +240,44 @@ dependencies = [ { name = "pydantic" }, { name = "python-dotenv" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/88/82/c79424d7d8c29b994fb01d277da57b0a9b09cc03c3ff875f9bd8a86b2145/pydantic_settings-2.8.1.tar.gz", hash = "sha256:d5c663dfbe9db9d5e1c646b2e161da12f0d734d422ee56f567d0ea2cee4e8585", size = 83550 } +sdist = { url = "https://files.pythonhosted.org/packages/88/82/c79424d7d8c29b994fb01d277da57b0a9b09cc03c3ff875f9bd8a86b2145/pydantic_settings-2.8.1.tar.gz", hash = "sha256:d5c663dfbe9db9d5e1c646b2e161da12f0d734d422ee56f567d0ea2cee4e8585", size = 83550, upload-time = "2025-02-27T10:10:32.338Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/53/a64f03044927dc47aafe029c42a5b7aabc38dfb813475e0e1bf71c4a59d0/pydantic_settings-2.8.1-py3-none-any.whl", hash = "sha256:81942d5ac3d905f7f3ee1a70df5dfb62d5569c12f51a5a647defc1c3d9ee2e9c", size = 30839 }, + { url = "https://files.pythonhosted.org/packages/0b/53/a64f03044927dc47aafe029c42a5b7aabc38dfb813475e0e1bf71c4a59d0/pydantic_settings-2.8.1-py3-none-any.whl", hash = "sha256:81942d5ac3d905f7f3ee1a70df5dfb62d5569c12f51a5a647defc1c3d9ee2e9c", size = 30839, upload-time = "2025-02-27T10:10:30.711Z" }, ] [[package]] name = "pygments" version = "2.19.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 } +sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581, upload-time = "2025-01-06T17:26:30.443Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, + { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293, upload-time = "2025-01-06T17:26:25.553Z" }, ] [[package]] name = "python-dotenv" version = "1.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920 } +sdist = { url = "https://files.pythonhosted.org/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920, upload-time = "2025-03-25T10:14:56.835Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256, upload-time = "2025-03-25T10:14:55.034Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256 }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, ] [[package]] @@ -237,42 +288,60 @@ dependencies = [ { name = "markdown-it-py" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a1/53/830aa4c3066a8ab0ae9a9955976fb770fe9c6102117c8ec4ab3ea62d89e8/rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725", size = 224078 } +sdist = { url = "https://files.pythonhosted.org/packages/a1/53/830aa4c3066a8ab0ae9a9955976fb770fe9c6102117c8ec4ab3ea62d89e8/rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725", size = 224078, upload-time = "2025-03-30T14:15:14.23Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229 }, + { url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229, upload-time = "2025-03-30T14:15:12.283Z" }, ] [[package]] name = "sg-mcp" version = "0.1.0" -source = { virtual = "." } +source = { editable = "." } dependencies = [ - { name = "mcp", extra = ["cli"] }, + { name = "anyio" }, + { name = "fastmcp" }, + { name = "httpx" }, + { name = "httpx-sse" }, { name = "pydantic" }, + { name = "pydantic-core" }, + { name = "pydantic-settings" }, + { name = "python-dotenv" }, + { name = "pyyaml" }, + { name = "starlette" }, + { name = "uvicorn" }, ] [package.metadata] requires-dist = [ - { name = "mcp", extras = ["cli"], specifier = ">=1.6.0" }, - { name = "pydantic", specifier = ">=2.11.0" }, + { name = "anyio", specifier = "==4.9.0" }, + { name = "fastmcp" }, + { name = "httpx", specifier = "==0.28.1" }, + { name = "httpx-sse", specifier = "==0.4.0" }, + { name = "pydantic", specifier = "==2.11.1" }, + { name = "pydantic-core", specifier = "==2.33.0" }, + { name = "pydantic-settings", specifier = "==2.8.1" }, + { name = "python-dotenv", specifier = "==1.1.0" }, + { name = "pyyaml" }, + { name = "starlette", specifier = "==0.46.1" }, + { name = "uvicorn", specifier = "==0.34.0" }, ] [[package]] name = "shellingham" version = "1.5.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 }, + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, ] [[package]] name = "sniffio" version = "1.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, ] [[package]] @@ -283,9 +352,9 @@ dependencies = [ { name = "anyio" }, { name = "starlette" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/71/a4/80d2a11af59fe75b48230846989e93979c892d3a20016b42bb44edb9e398/sse_starlette-2.2.1.tar.gz", hash = "sha256:54470d5f19274aeed6b2d473430b08b4b379ea851d953b11d7f1c4a2c118b419", size = 17376 } +sdist = { url = "https://files.pythonhosted.org/packages/71/a4/80d2a11af59fe75b48230846989e93979c892d3a20016b42bb44edb9e398/sse_starlette-2.2.1.tar.gz", hash = "sha256:54470d5f19274aeed6b2d473430b08b4b379ea851d953b11d7f1c4a2c118b419", size = 17376, upload-time = "2024-12-25T09:09:30.616Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/e0/5b8bd393f27f4a62461c5cf2479c75a2cc2ffa330976f9f00f5f6e4f50eb/sse_starlette-2.2.1-py3-none-any.whl", hash = "sha256:6410a3d3ba0c89e7675d4c273a301d64649c03a5ef1ca101f10b47f895fd0e99", size = 10120 }, + { url = "https://files.pythonhosted.org/packages/d9/e0/5b8bd393f27f4a62461c5cf2479c75a2cc2ffa330976f9f00f5f6e4f50eb/sse_starlette-2.2.1-py3-none-any.whl", hash = "sha256:6410a3d3ba0c89e7675d4c273a301d64649c03a5ef1ca101f10b47f895fd0e99", size = 10120, upload-time = "2024-12-25T09:09:26.761Z" }, ] [[package]] @@ -295,9 +364,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/04/1b/52b27f2e13ceedc79a908e29eac426a63465a1a01248e5f24aa36a62aeb3/starlette-0.46.1.tar.gz", hash = "sha256:3c88d58ee4bd1bb807c0d1acb381838afc7752f9ddaec81bbe4383611d833230", size = 2580102 } +sdist = { url = "https://files.pythonhosted.org/packages/04/1b/52b27f2e13ceedc79a908e29eac426a63465a1a01248e5f24aa36a62aeb3/starlette-0.46.1.tar.gz", hash = "sha256:3c88d58ee4bd1bb807c0d1acb381838afc7752f9ddaec81bbe4383611d833230", size = 2580102, upload-time = "2025-03-08T10:55:34.504Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/4b/528ccf7a982216885a1ff4908e886b8fb5f19862d1962f56a3fce2435a70/starlette-0.46.1-py3-none-any.whl", hash = "sha256:77c74ed9d2720138b25875133f3a2dae6d854af2ec37dceb56aef370c1d8a227", size = 71995 }, + { url = "https://files.pythonhosted.org/packages/a0/4b/528ccf7a982216885a1ff4908e886b8fb5f19862d1962f56a3fce2435a70/starlette-0.46.1-py3-none-any.whl", hash = "sha256:77c74ed9d2720138b25875133f3a2dae6d854af2ec37dceb56aef370c1d8a227", size = 71995, upload-time = "2025-03-08T10:55:32.662Z" }, ] [[package]] @@ -310,18 +379,18 @@ dependencies = [ { name = "shellingham" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8b/6f/3991f0f1c7fcb2df31aef28e0594d8d54b05393a0e4e34c65e475c2a5d41/typer-0.15.2.tar.gz", hash = "sha256:ab2fab47533a813c49fe1f16b1a370fd5819099c00b119e0633df65f22144ba5", size = 100711 } +sdist = { url = "https://files.pythonhosted.org/packages/8b/6f/3991f0f1c7fcb2df31aef28e0594d8d54b05393a0e4e34c65e475c2a5d41/typer-0.15.2.tar.gz", hash = "sha256:ab2fab47533a813c49fe1f16b1a370fd5819099c00b119e0633df65f22144ba5", size = 100711, upload-time = "2025-02-27T19:17:34.807Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/fc/5b29fea8cee020515ca82cc68e3b8e1e34bb19a3535ad854cac9257b414c/typer-0.15.2-py3-none-any.whl", hash = "sha256:46a499c6107d645a9c13f7ee46c5d5096cae6f5fc57dd11eccbbb9ae3e44ddfc", size = 45061 }, + { url = "https://files.pythonhosted.org/packages/7f/fc/5b29fea8cee020515ca82cc68e3b8e1e34bb19a3535ad854cac9257b414c/typer-0.15.2-py3-none-any.whl", hash = "sha256:46a499c6107d645a9c13f7ee46c5d5096cae6f5fc57dd11eccbbb9ae3e44ddfc", size = 45061, upload-time = "2025-02-27T19:17:32.111Z" }, ] [[package]] name = "typing-extensions" version = "4.13.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0e/3e/b00a62db91a83fff600de219b6ea9908e6918664899a2d85db222f4fbf19/typing_extensions-4.13.0.tar.gz", hash = "sha256:0a4ac55a5820789d87e297727d229866c9650f6521b64206413c4fbada24d95b", size = 106520 } +sdist = { url = "https://files.pythonhosted.org/packages/0e/3e/b00a62db91a83fff600de219b6ea9908e6918664899a2d85db222f4fbf19/typing_extensions-4.13.0.tar.gz", hash = "sha256:0a4ac55a5820789d87e297727d229866c9650f6521b64206413c4fbada24d95b", size = 106520, upload-time = "2025-03-26T03:49:41.628Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/86/39b65d676ec5732de17b7e3c476e45bb80ec64eb50737a8dce1a4178aba1/typing_extensions-4.13.0-py3-none-any.whl", hash = "sha256:c8dd92cc0d6425a97c18fbb9d1954e5ff92c1ca881a309c45f06ebc0b79058e5", size = 45683 }, + { url = "https://files.pythonhosted.org/packages/e0/86/39b65d676ec5732de17b7e3c476e45bb80ec64eb50737a8dce1a4178aba1/typing_extensions-4.13.0-py3-none-any.whl", hash = "sha256:c8dd92cc0d6425a97c18fbb9d1954e5ff92c1ca881a309c45f06ebc0b79058e5", size = 45683, upload-time = "2025-03-26T03:49:40.35Z" }, ] [[package]] @@ -331,9 +400,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222 } +sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222, upload-time = "2025-02-25T17:27:59.638Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125 }, + { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125, upload-time = "2025-02-25T17:27:57.754Z" }, ] [[package]] @@ -344,7 +413,27 @@ dependencies = [ { name = "click" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4b/4d/938bd85e5bf2edeec766267a5015ad969730bb91e31b44021dfe8b22df6c/uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9", size = 76568 } +sdist = { url = "https://files.pythonhosted.org/packages/4b/4d/938bd85e5bf2edeec766267a5015ad969730bb91e31b44021dfe8b22df6c/uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9", size = 76568, upload-time = "2024-12-15T13:33:30.42Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/14/33a3a1352cfa71812a3a21e8c9bfb83f60b0011f5e36f2b1399d51928209/uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4", size = 62315, upload-time = "2024-12-15T13:33:27.467Z" }, +] + +[[package]] +name = "websockets" +version = "15.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/61/14/33a3a1352cfa71812a3a21e8c9bfb83f60b0011f5e36f2b1399d51928209/uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4", size = 62315 }, + { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440, upload-time = "2025-03-05T20:02:36.695Z" }, + { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098, upload-time = "2025-03-05T20:02:37.985Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329, upload-time = "2025-03-05T20:02:39.298Z" }, + { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111, upload-time = "2025-03-05T20:02:40.595Z" }, + { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054, upload-time = "2025-03-05T20:02:41.926Z" }, + { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496, upload-time = "2025-03-05T20:02:43.304Z" }, + { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829, upload-time = "2025-03-05T20:02:48.812Z" }, + { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217, upload-time = "2025-03-05T20:02:50.14Z" }, + { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195, upload-time = "2025-03-05T20:02:51.561Z" }, + { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" }, + { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" }, + { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, ] From f1ffb3590402bdffea3ed5b0db2d6dd5e2508f6a Mon Sep 17 00:00:00 2001 From: georgiedekker Date: Wed, 7 May 2025 07:30:48 +0200 Subject: [PATCH 2/2] bug fixes --- main.py | 151 +++++++++++++++------- mock_global_rules/example-global-rule.yml | 2 + mock_test_cases/example-test.js | 1 + mock_test_cases/example-test.py | 1 + 4 files changed, 106 insertions(+), 49 deletions(-) create mode 100644 mock_global_rules/example-global-rule.yml create mode 100644 mock_test_cases/example-test.js create mode 100644 mock_test_cases/example-test.py diff --git a/main.py b/main.py index 191605b..77a4905 100644 --- a/main.py +++ b/main.py @@ -15,6 +15,8 @@ mcp = FastMCP("ast-grep") RULES_DIR = "sg_rules" # Define a standard directory for rules +MOCK_TEST_CASES_DIR = "mock_test_cases" +MOCK_GLOBAL_RULES_DIR = "mock_global_rules" # Helper function to ensure rules directory exists def ensure_rules_dir(project_folder: str) -> pathlib.Path: @@ -31,10 +33,43 @@ def ensure_rules_dir(project_folder: str) -> pathlib.Path: raise RuntimeError(f"Could not create rules directory '{rules_path}': {e}") return rules_path +# Helper function to ensure mock directories exist and create them with example files if not +def ensure_mock_dir(base_path: pathlib.Path, dir_name: str, example_files: Dict[str, str]) -> pathlib.Path: + mock_dir_path = base_path / dir_name + try: + mock_dir_path.mkdir(parents=True, exist_ok=True) + for file_name, content in example_files.items(): + file_path = mock_dir_path / file_name + if not file_path.exists(): + with open(file_path, "w") as f: + f.write(content) + except OSError as e: + # Non-critical, log or handle as warning if necessary + print(f"Warning: Could not create/populate mock directory '{mock_dir_path}': {e}") + return mock_dir_path + +# Ensure mock directories are set up on server start (using a placeholder base like current dir) +# In a real scenario, this base_path might be configurable or a fixed location. +_startup_base_path = pathlib.Path(".") +ensure_mock_dir( + _startup_base_path, + MOCK_GLOBAL_RULES_DIR, + {"example-global-rule.yml": "kind: Denton\\nselector: pattern\nrule:\n pattern: print($A)"} +) +ensure_mock_dir( + _startup_base_path, + MOCK_TEST_CASES_DIR, + { + "example-test.py": "print('hello world')", + "example-test.js": "console.log('hello world');" + } +) + # --- Tool Implementation (Decorators likely remain the same) --- # @mcp.tool() def find_code( + ctx: Context, project_folder: str = Field(description="The path to the project folder"), pattern: str = Field(description="The ast-grep pattern to search for. Note the pattern must has valid AST structure."), language: str = Field(description="The language of the query", default=""), @@ -48,6 +83,7 @@ def find_code( @mcp.tool() def find_code_by_rule( + ctx: Context, project_folder: str = Field(description="The path to the project folder"), yaml: str = Field(description="The ast-grep YAML rule to search. It must have id, language, rule fields."), ) -> List[dict[str, Any]]: @@ -107,19 +143,19 @@ def run_ast_grep_yaml(yaml: str, project_folder: str) -> List[dict[str, Any]]: @mcp.tool() -def get_supported_languages() -> List[str]: +def get_supported_languages(ctx: Context) -> List[str]: """Returns supported languages list""" args = ["ast-grep", "language", "--list"] return handle_subprocess(args, list_processor) @mcp.tool() -def get_language_kinds(language: str) -> List[str]: +def get_language_kinds(ctx: Context, language: str) -> List[str]: """Returns AST node kinds for a language""" args = ["ast-grep", "language", "--kinds", language] return handle_subprocess(args, list_processor) @mcp.tool() -def dump_ast(code: str, language: str) -> Dict[str, Any]: +def dump_ast(ctx: Context, code: str, language: str) -> Dict[str, Any]: """Dumps AST structure of code""" # Ensure language is alphanumeric for file extension safety safe_lang = ''.join(filter(str.isalnum, language)) or 'txt' @@ -134,7 +170,7 @@ def dump_ast(code: str, language: str) -> Dict[str, Any]: return handle_subprocess(args, json_processor, cleanup=None) @mcp.tool() -def validate_rule_syntax(rule: str) -> Dict[str, Any]: +def validate_rule_syntax(ctx: Context, rule: str) -> Dict[str, Any]: """Validates YAML rule syntax""" with tempfile.NamedTemporaryFile(mode='w', suffix='.yml', delete=True) as f: f.write(rule) @@ -145,7 +181,7 @@ def validate_rule_syntax(rule: str) -> Dict[str, Any]: return handle_subprocess(args, validation_processor, cleanup=None) @mcp.tool() -def list_rules(project_folder: str = Field(description="The path to the project folder")) -> List[str]: +def list_rules(ctx: Context, project_folder: str = Field(description="The path to the project folder")) -> List[str]: """Lists YAML rule files in the project's sg_rules directory.""" try: rules_path = ensure_rules_dir(project_folder) @@ -159,7 +195,7 @@ def list_rules(project_folder: str = Field(description="The path to the project raise RuntimeError(f"Error listing rules: {e}") @mcp.tool() -def get_rule(project_folder: str = Field(description="The path to the project folder"), +def get_rule(ctx: Context, project_folder: str = Field(description="The path to the project folder"), rule_name: str = Field(description="The name of the rule file (e.g., my-rule.yml)")) -> str: """Gets the content of a specific YAML rule file from the project's sg_rules directory.""" try: @@ -175,7 +211,7 @@ def get_rule(project_folder: str = Field(description="The path to the project fo raise RuntimeError(f"Error reading rule file '{rule_name}': {e}") @mcp.tool() -def create_new_rule(project_folder: str = Field(description="The path to the project folder"), +def create_new_rule(ctx: Context, project_folder: str = Field(description="The path to the project folder"), rule_name: str = Field(description="The desired name for the new rule file (e.g., new-rule.yml)"), rule_content: str = Field(description="The YAML content for the new rule")) -> str: """Creates a new YAML rule file in the project's sg_rules directory.""" @@ -205,7 +241,7 @@ def create_new_rule(project_folder: str = Field(description="The path to the pro @mcp.tool() -def list_all_diagnostics_in_the_workspace(project_folder: str = Field(description="The path to the project folder")) -> Union[List[dict[str, Any]], str]: +def list_all_diagnostics_in_the_workspace(ctx: Context, project_folder: str = Field(description="The path to the project folder")) -> Union[List[dict[str, Any]], str]: """Runs 'sg scan --json' to find diagnostics based on rules in sg_rules directory.""" try: rules_path = ensure_rules_dir(project_folder) @@ -224,30 +260,34 @@ def list_all_diagnostics_in_the_workspace(project_folder: str = Field(descriptio # Resource to list local rules @mcp.resource("rule://sg_rules") -async def list_local_rules_resource(project_folder: str) -> List[Dict[str, str]]: - """Lists local rule files as resources. Requires project_folder.""" +async def list_local_rules_resource(ctx: Context) -> List[Dict[str, Any]]: + """Dynamically lists rule files under sg_rules for a given project_folder.""" + project_folder = ctx.params.get("project_folder") if not project_folder: - raise ValueError("Listing local rules requires the 'project_folder' argument.") + # Default to current directory or raise an error if project_folder is essential + # raise ValueError("project_folder parameter is required to list local rules.") + project_folder = "." # Or some sensible default, or make it mandatory via client + # Consider if this resource should even be listed by list_mcp_resources if project_folder isn't known then. + try: - rules_path = ensure_rules_dir(project_folder) - rule_files = list(rules_path.glob('*.yml')) + list(rules_path.glob('*.yaml')) - return [ - { - "uri": f"rule://sg_rules/{rule_file.name}", - "name": f"Rule: {rule_file.name}", - # Add other resource metadata if needed by fastmcp resource model - } - for rule_file in rule_files - ] - except (ValueError, RuntimeError) as e: - raise e + rules_p = ensure_rules_dir(project_folder) + rule_files_info = [] + for p in rules_p.glob("*.yml"): + rule_files_info.append({"uri": f"rule://sg_rules/{p.name}", "name": p.name, "description": "A local ast-grep rule."}) + for p in rules_p.glob("*.yaml"): + rule_files_info.append({"uri": f"rule://sg_rules/{p.name}", "name": p.name, "description": "A local ast-grep rule."}) + return rule_files_info + except ValueError as ve: # Catch specific error from ensure_rules_dir + raise ValueError(f"Cannot list rules: {ve}") # Re-raise to be sent as error response except Exception as e: - raise RuntimeError(f"Error listing local rules resource: {e}") + # Generic error for other issues + raise RuntimeError(f"Error accessing rules in '{project_folder}': {e}") # Template Resource to read a specific local rule @mcp.resource("rule://sg_rules/{rule_name}") -async def read_local_rule_resource(rule_name: str, project_folder: str) -> Dict[str, Any]: +async def read_local_rule_resource(ctx: Context, rule_name: str) -> str: """Reads a specific local rule file. Requires project_folder.""" + project_folder = ctx.params.get("project_folder") if not project_folder: raise ValueError("Reading local rules requires the 'project_folder' argument.") try: @@ -263,11 +303,7 @@ async def read_local_rule_resource(rule_name: str, project_folder: str) -> Dict[ with open(rule_file_path, 'r') as f: content = f.read() - return { - "uri": f"rule://sg_rules/{rule_name}", - "mimeType": "application/yaml", - "content": content # fastmcp might expect 'content' instead of 'text' - } + return content # Return only the content string except (ValueError, RuntimeError, FileNotFoundError) as e: raise e except Exception as e: @@ -275,33 +311,22 @@ async def read_local_rule_resource(rule_name: str, project_folder: str) -> Dict[ # Static Resource for mock global rule @mcp.resource("rule://global/example-global-rule.yml") -def global_rule_resource() -> Dict[str, Any]: +def global_rule_resource() -> str: """Provides mock content for the global rule example.""" - return { - "uri": "rule://global/example-global-rule.yml", - "mimeType": "application/yaml", - "content": "# Mock Global Rule Content\nlanguage: python\nrule:\n pattern: print($ARG)" - } + # In a real scenario, this might load from a file or predefined string + return "# Mock Global Rule Content\nlanguage: python\nrule:\n pattern: print($ARG)" # Static Resource for mock python test case @mcp.resource("testcase://python/example-test.py") -def python_test_case_resource() -> Dict[str, Any]: +def python_test_case_resource() -> str: """Provides mock content for the python test case example.""" - return { - "uri": "testcase://python/example-test.py", - "mimeType": "text/x-python", - "content": "# Mock Python Test Case\nimport os\n\nprint(\"Hello, world!\")\n\ndef old_function(x):\n return x * 2" - } + return "# Mock Python Test Case\nimport os\n\nprint(\"Hello, world!\")\n\ndef old_function(x):\n return x * 2" # Static Resource for mock javascript test case @mcp.resource("testcase://javascript/example-test.js") -def javascript_test_case_resource() -> Dict[str, Any]: +def javascript_test_case_resource() -> str: """Provides mock content for the javascript test case example.""" - return { - "uri": "testcase://javascript/example-test.js", - "mimeType": "text/javascript", - "content": "// Mock JavaScript Test Case\nconsole.log(\"Hello, world!\");\n\nfunction deprecatedFunc(a) {\n return a + 1;\n}" - } + return "// Mock JavaScript Test Case\nconsole.log(\"Hello, world!\");\n\nfunction deprecatedFunc(a) {\n return a + 1;\n}" # --- Subprocess Result Processors (Refined error propagation) --- # @@ -369,5 +394,33 @@ def handle_subprocess(args, processor, cleanup=None): # --- Main Execution --- # if __name__ == "__main__": - # No need to check for pyyaml import here if it's in pyproject.toml + print("--- Server Starting ---") + print("Registered Tools (from mcp.router._tools):") + if hasattr(mcp, 'router') and hasattr(mcp.router, '_tools') and mcp.router._tools: + for tool_name, tool_def in mcp.router._tools.items(): + # tool_def is likely a ToolDefinition object + # Accessing description directly might work, or via a method/property + description = getattr(tool_def, 'description', tool_def.function.__doc__ if hasattr(tool_def, 'function') else 'No description') + print(f" - {tool_name}: {description}") + else: + print(" No tools found in mcp.router._tools or router not available.") + + print("Registered Static Resources (from mcp.router._resources):") + if hasattr(mcp, 'router') and hasattr(mcp.router, '_resources') and mcp.router._resources: + for uri, resource_obj in mcp.router._resources.items(): + # resource_obj could be a StaticResource or similar + name = getattr(resource_obj, 'name', 'N/A') + print(f" - URI: {uri}, Name: {name}") + else: + print(" No static resources found in mcp.router._resources or router not available.") + + print("Registered Resource Templates (from mcp.router._resource_templates):") + if hasattr(mcp, 'router') and hasattr(mcp.router, '_resource_templates') and mcp.router._resource_templates: + for uri_template, template_obj in mcp.router._resource_templates.items(): + name = getattr(template_obj, 'name', 'N/A') + print(f" - Template URI: {uri_template}, Name: {name}") + else: + print(" No resource templates found in mcp.router._resource_templates or router not available.") + + print("-----------------------") mcp.run(transport = "stdio") diff --git a/mock_global_rules/example-global-rule.yml b/mock_global_rules/example-global-rule.yml new file mode 100644 index 0000000..3e2a30c --- /dev/null +++ b/mock_global_rules/example-global-rule.yml @@ -0,0 +1,2 @@ +kind: Denton\nselector: pattern +rule:\n pattern: print($A) \ No newline at end of file diff --git a/mock_test_cases/example-test.js b/mock_test_cases/example-test.js new file mode 100644 index 0000000..5893f9d --- /dev/null +++ b/mock_test_cases/example-test.js @@ -0,0 +1 @@ +console.log('hello world'); \ No newline at end of file diff --git a/mock_test_cases/example-test.py b/mock_test_cases/example-test.py new file mode 100644 index 0000000..00950d9 --- /dev/null +++ b/mock_test_cases/example-test.py @@ -0,0 +1 @@ +print('hello world') \ No newline at end of file