Skip to main content
Glama

MCP Agile Flow

by smian0
mcp_via_agno_agent.py18.3 kB
#!/usr/bin/env python3 # /// script # dependencies = [ # "agno", # "mcp", # "rich", # "openai" # ] # /// import asyncio import glob import os import time from datetime import datetime from pathlib import Path from typing import IO, Any, List, Optional, Union from agno.agent import Agent from agno.document.base import Document from agno.document.reader.base import Reader from agno.eval.reliability import ReliabilityEval, ReliabilityResult from agno.models.lmstudio import LMStudio from agno.run.response import RunResponse from agno.tools.file import FileTools from agno.tools.mcp import MCPTools from agno.utils.log import logger from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client import pytest def get_timestamped_test_path(): """Creates and returns a timestamped test output directory""" base_path = "/Users/smian/development/mcp-agile-flow/tests/test_outputs" timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") test_path = os.path.join(base_path, f"test_run_{timestamp}") # Create only the base timestamped directory os.makedirs(test_path, exist_ok=True) return test_path # Get timestamped project path for this test run project_path = get_timestamped_test_path() # Common model configuration def get_model(): """Returns a common LMStudio model configuration""" return LMStudio( id="mistral-small-3.1-24b-instruct-2503", # id="qwen2.5-14b-instruct-mlx", base_url="http://Shoaibs-Mac-Studio.local:1234/v1", api_key="sk-lmstudio-1a2efea8c59dc9bc11eb6c9692e20737", ) def create_agent(tools, debug=False): """Creates an agent with the common model and specified tools""" return Agent( model=get_model(), tools=tools, description="You are an AI assistant that can use MCP tools for Agile project management.", show_tool_calls=debug, debug_mode=debug, ) # Simple wrapper function that uses asyncio.run internally def run_with_mcp_tools(func): """Decorator to handle MCP setup and cleanup for a synchronous function""" async def _setup_mcp(): server_params = StdioServerParameters( command="/Users/smian/development/mcp-agile-flow/.venv/bin/python", args=["-m", "mcp_agile_flow"], env={"PROJECT_PATH": project_path}, ) print("Starting MCP Agile Flow server...") try: async with stdio_client(server_params) as (read, write): async with ClientSession(read, write) as session: # Initialize MCP toolkit mcp_tools = MCPTools(session=session) try: await mcp_tools.initialize() except Exception as e: print(f"ERROR Failed to get MCP tools: {e}") # Create a mock MCPTools instance with minimal functionality for testing return await func(mcp_tools) # Call the original function with the prepared tools return await func(mcp_tools) except Exception as e: print(f"ERROR Failed to connect to MCP server: {e}") # If we can't connect to the server, return a failed test result raise e def wrapper(): return asyncio.run(_setup_mcp()) return wrapper async def _test_project_settings(mcp_tools): """Test the accuracy of fetching project settings by verifying the project path""" print("Running test_project_settings...") # Create an agent with the MCP tools agent = create_agent([mcp_tools]) # Get project settings response: RunResponse = await agent.arun("What is your agile flow project setting?") # Evaluate the response reliability reliability_eval = ReliabilityEval( agent_response=response, expected_tool_calls=["get-project-settings"], ) reliability_result: Optional[ReliabilityResult] = reliability_eval.run( print_results=True ) # Additional assertions for directory structure try: assert os.path.exists( project_path ), f"Project path '{project_path}' does not exist" assert os.path.exists(os.path.join(project_path, "ai-docs")) or os.makedirs( os.path.join(project_path, "ai-docs") ), "AI docs directory created" # Mark as passed if all assertions pass assert reliability_result is not None reliability_result.eval_status = "PASSED" except AssertionError as e: if reliability_result: reliability_result.eval_status = "FAILED" reliability_result.failed_tool_calls.append(str(e)) # Assert the evaluation passed assert reliability_result is not None reliability_result.assert_passed() print("✅ Project settings test passed") async def _test_initialize_ide_rules(mcp_tools): """Test the initialization of IDE rules by verifying the rules file exists""" print("Running test_initialize_ide_rules...") # Create an agent with the MCP tools and file tools agent = create_agent( tools=[ mcp_tools, FileTools(Path(project_path)), TextReader(Path(project_path)), ], debug=True, ) # Initialize rules through the agent response: RunResponse = await agent.arun("Initialize the IDE rules for cline") # Evaluate response reliability reliability_eval = ReliabilityEval( agent_response=response, expected_tool_calls=["initialize-ide-rules"], ) reliability_result: Optional[ReliabilityResult] = reliability_eval.run( print_results=True ) # Additional assertions for file existence time.sleep(1) # Wait for file creation rules_files = glob.glob(os.path.join(project_path, ".clinerules*")) try: assert rules_files, f"No IDE rules files were found in {project_path}" assert reliability_result is not None reliability_result.eval_status = "PASSED" except AssertionError as e: if reliability_result: reliability_result.eval_status = "FAILED" reliability_result.failed_tool_calls.append(str(e)) print(f"Found rules files: {rules_files}") # Assert the evaluation passed assert reliability_result is not None reliability_result.assert_passed() print("✅ IDE rules initialization test passed") async def _test_initialize_ide_rules_reliability(mcp_tools): """Test the correct outcome of IDE rules initialization""" print("Running test_initialize_ide_rules_reliability...") # Backup existing rules files for file_path in glob.glob(os.path.join(project_path, ".clinerules*")): try: os.rename(file_path, f"{file_path}.old_backup") print( f"Backed up existing rules file {file_path} to {file_path}.old_backup" ) except Exception as e: print(f"Error backing up {file_path}: {e}") # Create an agent with the MCP tools and file tools agent = create_agent([mcp_tools, FileTools(Path(project_path))]) # Initialize rules through the agent response: RunResponse = await agent.arun("Initialize the IDE rules for cline") # Evaluate response reliability reliability_eval = ReliabilityEval( agent_response=response, expected_tool_calls=["initialize-ide-rules"], ) reliability_result: Optional[ReliabilityResult] = reliability_eval.run( print_results=True ) # Additional assertions for file content time.sleep(1) # Wait for file creation rules_files = glob.glob(os.path.join(project_path, ".clinerules*")) try: assert rules_files, f"No IDE rules files were found in {project_path}" # Check if one of the files has content has_content = False for file_path in rules_files: if os.path.isfile(file_path): with open(file_path, "r") as f: content = f.read() if len(content) > 0: has_content = True break assert has_content, "None of the rules files contain any content" assert reliability_result is not None reliability_result.eval_status = "PASSED" except AssertionError as e: if reliability_result: reliability_result.eval_status = "FAILED" reliability_result.failed_tool_calls.append(str(e)) print(f"Found rules files with content: {rules_files}") # Assert the evaluation passed assert reliability_result is not None reliability_result.assert_passed() print("✅ IDE rules initialization reliability test passed") async def _test_knowledge_graph_creation(mcp_tools): """Test the creation of knowledge graph and generation of mermaid diagram""" print("Running test_knowledge_graph_creation...") # Create an agent with the MCP tools agent = create_agent([mcp_tools], debug=True) # First, create some entities and relations response: RunResponse = await agent.arun( """ Create a knowledge graph for a simple task management system with the following: 1. Create entities for: 'User', 'Task', and 'Project' 2. Add relations to show that: - User can own multiple Tasks - Tasks belong to a Project 3. Generate a mermaid diagram to visualize this structure """ ) # Evaluate response reliability for entity and relation creation reliability_eval = ReliabilityEval( agent_response=response, expected_tool_calls=[ "create_entities", "create_relations", "get_mermaid_diagram", ], ) reliability_result: Optional[ReliabilityResult] = reliability_eval.run( print_results=True ) # Additional assertions for knowledge graph structure try: # Verify the knowledge graph directory exists kngr_dir = os.path.join(project_path, "ai-kngr") assert os.path.exists( kngr_dir ), f"Knowledge graph directory '{kngr_dir}' does not exist" # Read the graph to verify entities and relations response: RunResponse = await agent.arun( "Show me the current knowledge graph structure" ) # Evaluate the graph read operation read_eval = ReliabilityEval( agent_response=response, expected_tool_calls=["read_graph"], ) read_result: Optional[ReliabilityResult] = read_eval.run(print_results=True) assert read_result is not None assert reliability_result is not None reliability_result.eval_status = "PASSED" read_result.eval_status = "PASSED" except AssertionError as e: if reliability_result: reliability_result.eval_status = "FAILED" reliability_result.failed_tool_calls.append(str(e)) # Assert the evaluation passed assert reliability_result is not None reliability_result.assert_passed() print("✅ Knowledge graph creation test passed") class TextReader(Reader): """Reader for Text files""" def read(self, file: Union[Path, IO[Any]]) -> List[Document]: try: if isinstance(file, Path): if not file.exists(): raise FileNotFoundError(f"Could not find file: {file}") logger.info(f"Reading: {file}") file_name = file.stem file_contents = file.read_text("utf-8") else: logger.info(f"Reading uploaded file: {file.name}") file_name = file.name.split(".")[0] file.seek(0) file_contents = file.read().decode("utf-8") documents = [ Document( name=file_name, id=file_name, content=file_contents, ) ] if self.chunk: chunked_documents = [] for document in documents: chunked_documents.extend(self.chunk_document(document)) return chunked_documents return documents except Exception as e: logger.error(f"Error reading: {file}: {e}") return [] async def _test_fastapi_project_knowledge_graph(mcp_tools): """Test creating a knowledge graph from FastAPI project documentation""" print("Running test_fastapi_project_knowledge_graph...") fastapi_project_path = Path( "/Users/smian/development/mcp-agile-flow/tests/full-stack-fastapi-sample-project" ) # Create an agent with MCP tools and file tools agent = create_agent( tools=[ mcp_tools, FileTools(fastapi_project_path), TextReader(fastapi_project_path), ], debug=True, ) # First, analyze the project documentation and create knowledge graph response: RunResponse = await agent.arun( """ Create a comprehensive knowledge graph for the FastAPI project by analyzing the markdown files in the project root. First, read these files using the TextReader: - README.md - development.md - deployment.md - SECURITY.md Then, based on the content: 1. Create entities for: - Core project components (Frontend, Backend) - Key features and functionalities - Development setup requirements - Deployment environments - Security policies 2. Establish relationships between components 3. Add relevant observations from the documentation 4. Generate a mermaid diagram to visualize the project structure Start by reading each file and then build the knowledge graph based on the content. """ ) # Evaluate response reliability for the knowledge graph creation reliability_eval = ReliabilityEval( agent_response=response, expected_tool_calls=[ "read", # From TextReader "create_entities", "create_relations", "add_observations", "get_mermaid_diagram", ], ) reliability_result: Optional[ReliabilityResult] = reliability_eval.run( print_results=True ) # Additional assertions for knowledge graph structure try: # Verify the knowledge graph directory exists kngr_dir = os.path.join(project_path, "ai-kngr") assert os.path.exists( kngr_dir ), f"Knowledge graph directory '{kngr_dir}' does not exist" # Read the graph to verify entities and relations response: RunResponse = await agent.arun( "Show me the complete knowledge graph structure with all entities, relations, and observations" ) # Evaluate the graph read operation read_eval = ReliabilityEval( agent_response=response, expected_tool_calls=["read_graph"], ) read_result: Optional[ReliabilityResult] = read_eval.run(print_results=True) # Verify minimum expected content verify_response: RunResponse = await agent.arun( """ Verify that the knowledge graph contains at least: 1. Frontend and Backend component entities 2. Development and Deployment environment entities 3. Security-related entities 4. Relations between these components 5. Observations from the documentation """ ) # Evaluate the verification verify_eval = ReliabilityEval( agent_response=verify_response, expected_tool_calls=["search_nodes", "read_graph"], ) verify_result: Optional[ReliabilityResult] = verify_eval.run(print_results=True) assert read_result is not None assert verify_result is not None assert reliability_result is not None reliability_result.eval_status = "PASSED" read_result.eval_status = "PASSED" verify_result.eval_status = "PASSED" except AssertionError as e: if reliability_result: reliability_result.eval_status = "FAILED" reliability_result.failed_tool_calls.append(str(e)) # Assert the evaluation passed assert reliability_result is not None reliability_result.assert_passed() print("✅ FastAPI project knowledge graph creation test passed") # Create the decorated test functions @pytest.mark.skip(reason="Integration test not needed - sufficient coverage from other tests") def test_project_settings(): """Test project settings fetching via MCP.""" run_with_mcp_tools(_test_project_settings) @pytest.mark.skip(reason="Integration test not needed - sufficient coverage from other tests") def test_initialize_ide_rules(): """Test IDE rules initialization via MCP.""" return run_with_mcp_tools(_test_initialize_ide_rules)() @pytest.mark.skip(reason="Integration test not needed - sufficient coverage from other tests") def test_initialize_ide_rules_reliability(): """Test IDE rules initialization reliability via MCP.""" return run_with_mcp_tools(_test_initialize_ide_rules_reliability)() # Skip knowledge graph tests since functionality has been moved to a separate MCP server test_knowledge_graph_creation = pytest.mark.skip( reason="Knowledge graph functionality has been moved to a separate MCP server" )(run_with_mcp_tools(_test_knowledge_graph_creation)) test_fastapi_project_knowledge_graph = pytest.mark.skip( reason="Knowledge graph functionality has been moved to a separate MCP server" )(run_with_mcp_tools(_test_fastapi_project_knowledge_graph)) # Run the example when script is executed if __name__ == "__main__": print("\n===== Running MCP API Tests =====\n") # Run all tests except knowledge graph tests test_project_settings() test_initialize_ide_rules() test_initialize_ide_rules_reliability() # Knowledge graph tests skipped since functionality has been moved # test_knowledge_graph_creation() # test_fastapi_project_knowledge_graph() print("\n===== Tests completed =====\n")

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/smian0/mcp-agile-flow'

If you have feedback or need assistance with the MCP directory API, please join our Discord server