Skip to main content
Glama

Agent MCP

project_utils.py14.1 kB
# Agent-MCP/agent-mcp/utils/project_utils.py import os import json import datetime from pathlib import Path from typing import Optional, List, Dict, Any from ..core import globals as g from ..core.config import ( logger, get_project_dir, ) # Import get_project_dir for MCP_VERSION if needed # __version__ was in mcp_template/__init__.py. # If MCP_VERSION is needed here, it should ideally be sourced from a single place. # For now, let's assume it might come from the main package's __init__ or a dedicated version file. # We can hardcode it temporarily or make it configurable. try: # Attempt to get version from the root __init__.py of agent-mcp from agent_mcp import __version__ as MCP_VERSION except ImportError: logger.warning( "Could not import __version__ from agent_mcp. Using default '0.1.0'." ) MCP_VERSION = "0.1.0" # Fallback, matches original main.py:1041 # Original location: main.py lines 876-929 (init_agent_directory) def init_agent_directory(project_dir_str: str) -> Optional[Path]: """ Initialize the .agent directory structure in the specified project directory. If the directory structure already exists, it verifies it. Original main.py: lines 876-929 """ try: project_path = Path(project_dir_str).resolve() except Exception as e: logger.error(f"Invalid project directory string '{project_dir_str}': {e}") return None # Validate that the project directory is not the MCP directory itself # This logic needs to correctly identify the MCP codebase root. # Assuming this file is at: Agent-MCP/agent-mcp/utils/project_utils.py # Then, __file__.resolve() gives the path to this file. # .parent -> .../utils # .parent.parent -> .../mcp_server_src # .parent.parent.parent -> .../agent-mcp (This is the root of the agent code package) # .parent.parent.parent.parent -> .../Agent-MCP (This is the repository root) # The original check was against `Path(__file__).resolve().parent.parent` from `main.py` # which would be `agent-mcp`. agent_mcp_codebase_root_for_check = ( Path(__file__).resolve().parent.parent.parent ) # This should point to agent-mcp # Original main.py line 880-884 if ( project_path == agent_mcp_codebase_root_for_check or project_path in agent_mcp_codebase_root_for_check.parents ): # This warning matches the original behavior. logger.warning( f"WARNING: Initializing .agent in the MCP directory itself ({project_path}) or its parent is not recommended!" ) logger.warning( f"Please specify a project directory that is NOT the MCP codebase." ) # Original code proceeded with a warning, so we do the same. agent_dir = project_path / ".agent" # Original main.py lines 887-899 (directory list) directories_to_create = [ "", # Ensures .agent itself is created "logs", "diffs", "notifications", "notifications/pending", "notifications/acknowledged", ] try: for directory_suffix in directories_to_create: (agent_dir / directory_suffix).mkdir(parents=True, exist_ok=True) except OSError as e: logger.error(f"Failed to create .agent directory structure in {agent_dir}: {e}") return None # Indicate failure # Create initial config file if it doesn't exist # Original main.py lines 902-914 config_path = agent_dir / "config.json" if not config_path.exists(): # g.admin_token might not be initialized when this function is first called # during server startup before admin token persistence logic. # The original code in main.py:1040 used `admin_token` which was set earlier. # We should pass the admin_token to this function if it's needed at this stage, # or ensure g.admin_token is reliably set before this. # For now, let's assume g.admin_token will be set by the time this is called in a meaningful way, # or it will be None if called very early (e.g. initial setup). current_admin_token = g.admin_token # Get current global admin token config_data = { "project_name": project_path.name, "created_at": datetime.datetime.now().isoformat(), "admin_token": current_admin_token, # Use the admin token available at call time "mcp_version": MCP_VERSION, } try: with open(config_path, "w", encoding="utf-8") as f: json.dump(config_data, f, indent=2) except IOError as e: logger.error(f"Failed to write initial config.json to {config_path}: {e}") return None # Indicate failure except Exception as e: logger.error( f"Unexpected error writing initial config.json: {e}", exc_info=True ) return None # Create initial daily logs file if it doesn't exist # Original main.py lines 917-926 log_file_dir = agent_dir / "logs" # log_file_dir.mkdir(parents=True, exist_ok=True) # Ensured by directories_to_create log_file_path = log_file_dir / f"{datetime.date.today().isoformat()}.json" if not log_file_path.exists(): log_entry = { "timestamp": datetime.datetime.now().isoformat(), "event": "agent_directory_initialized", "details": "Initial setup of .agent directory", } try: with open(log_file_path, "w", encoding="utf-8") as f: json.dump( [log_entry], f, indent=2 ) # Original stored a list with one entry except IOError as e: logger.error( f"Failed to write initial daily log file to {log_file_path}: {e}" ) # Continue, as this is less critical than config.json, matching original behavior. except Exception as e: logger.error( f"Unexpected error writing initial daily log file: {e}", exc_info=True ) logger.info(f".agent directory structure initialized/verified in {agent_dir}") return agent_dir # Original location: main.py lines 1206-1239 (generate_system_prompt) def generate_system_prompt( agent_id: str, agent_token_for_prompt: str, admin_token_runtime: Optional[str] ) -> str: """ Generate a system prompt for an agent. Original main.py: lines 1206-1239. Uses g.agent_working_dirs. """ # Determine working directory for the prompt # Fallback to CWD if agent_id not in g.agent_working_dirs, though it should be by the time this is called. # (Original main.py line 1226: agent_working_dirs.get(agent_id, os.getcwd())) working_dir = g.agent_working_dirs.get(agent_id, os.getcwd()) # Base prompt content from original main.py lines 1208-1224 base_prompt = f"""You are an AI agent running in Cursor, connected to a Multi-Agent Collaboration Protocol (MCP) server. Your goal is to complete tasks efficiently and collaboratively using a shared, persistent knowledge base. **Core Responsibilities & Tools:** * **File Safety:** Before modifying any file, use `check_file_status` to see if another agent is using it. Use `update_file_status` to claim files ('editing', 'reading', 'reviewing') before you start and 'released' when done. * **Task Management:** Use `view_tasks` to see your assigned tasks (filter by agent ID or status). Update progress with `update_task_status`. If a task is complex, use `request_assistance` or `create_self_task`. * **Project Context (Key-Value):** * Use `view_project_context` with `context_key` for specific values (e.g., API endpoints, configuration) or `search_query` to find relevant keys via keywords. * (Admin) Use `update_project_context` to add/modify precise key-value context. * **File Metadata:** * Use `view_file_metadata` (with `filepath`) to understand a file's purpose, components, etc. * (Admin) Use `update_file_metadata` to add/update structured information about specific files. * **RAG Querying:** Use `ask_project_rag` with a natural language `query` to ask broader questions about the project. The system will search across documentation, context, and metadata to synthesize an answer. (Index updates automatically in the background). * **Parallelization:** Analyze tasks for opportunities to work in parallel. Break down large tasks into smaller sub-tasks. Clearly define dependencies. * **Auditability:** Log all significant actions for tracking and debugging. Your working directory is: {working_dir} """ # Determine agent type for the prompt # (Original main.py line 1227: agent_details, and line 1210 for admin_token check) agent_type = "Worker" # The original logic in create_agent_tool (main.py:1134) passed `token` (which was the calling admin_token) # as the third argument to generate_system_prompt if the agent_id started with "admin". # So, `admin_token_runtime` here corresponds to that third argument. # An agent is "Admin" type in the prompt if its own token IS the admin token. if ( agent_id.lower().startswith("admin") and agent_token_for_prompt == admin_token_runtime ): agent_type = "Admin" # A simpler check might be if the agent_token_for_prompt itself is the known g.admin_token # However, the original call structure was a bit specific. # Let's refine: the prompt should reflect if this *specific agent instance* is an admin. # This happens if its `agent_token_for_prompt` is the same as the system's `admin_token_runtime`. # The `agent_id.lower().startswith("admin")` is a secondary check. if ( agent_token_for_prompt == admin_token_runtime ): # Primary check: is this agent's token THE admin token? agent_type = "Admin" agent_details_str = f"""Agent ID: {agent_id} Agent Type: {agent_type} """ # Connection code snippet for the agent to use # (Original main.py lines 1229-1237 for connection code structure) # The MCP_SERVER_URL should come from a config or be dynamically determined. # The original used os.environ.get('PORT', '8080') which implies it's for the SSE server. # The client connection example in the prompt should use the /messages/ endpoint for tool calls if that's the design. # Let's assume the agent's env var MCP_SERVER_URL points to the correct base for /messages/ mcp_server_url_for_client = os.environ.get( "MCP_SERVER_URL", f"http://localhost:{os.environ.get('PORT', '8080')}/messages/" ) # The original connection code snippet in main.py was quite extensive and specific. # For 1-to-1, we should replicate that structure. # It assumed the agent would use 'requests' and provided a `call_mcp_tool` like function. # The token used in HEADERS should be `agent_token_for_prompt`. connection_code_lines = [ f'MCP_SERVER_URL = "{mcp_server_url_for_client}" # Adjust if your server\'s tool endpoint is different', f'AGENT_TOKEN = "{agent_token_for_prompt}" # This is your unique agent token', "", "HEADERS = {", ' "Content-Type": "application/json",', # The original prompt had a complex way of deciding which token to use in Authorization. # It should always be the AGENT_TOKEN for the agent's own calls. ' "Authorization": f"Bearer {{AGENT_TOKEN}}"', "}", "", "def call_mcp_tool(tool_name: str, arguments: dict) -> dict:", " payload = {", ' "id": f"call_{{requests.compat.urlencode(arguments)[:10]}}", # Example unique ID', ' "type": "tool_call",', ' "tool": tool_name,', ' "arguments": arguments', " }", ' print(f"Calling tool: {{tool_name}} with args: {{arguments}}") # Debug print', " try:", " response = requests.post(MCP_SERVER_URL, headers=HEADERS, json=payload, timeout=60)", " response.raise_for_status() # Raise an HTTPError for bad responses (4XX or 5XX)", " # The MCP server is expected to return a list of content blocks.", " # We need to parse the 'text' from the first TextContent block.", " response_data = response.json()", " if isinstance(response_data, list) and len(response_data) > 0:", " first_item = response_data[0]", " if isinstance(first_item, dict) and first_item.get('type') == 'text':", " return {{'text_response': first_item.get('text', '')}}", " return {{'raw_response': response_data}} # Fallback", " except requests.exceptions.Timeout:", ' print(f"Timeout calling MCP tool {{tool_name}}")', " return {{'error': 'Timeout'}}" " except requests.exceptions.RequestException as e:", ' print(f"Error calling MCP tool {{tool_name}}: {{e}}")', " if e.response is not None:", ' print(f"Response content: {{e.response.text}}")', " return {{'error': str(e), 'response_text': e.response.text}}", " return {{'error': str(e)}}", "", "# Example usage:", '# result = call_mcp_tool("view_tasks", {{"token": AGENT_TOKEN}})', "# if result and 'error' not in result: print(json.dumps(result, indent=2))", ] connection_code_str = "\n".join(connection_code_lines) # Construct full prompt (Original main.py line 1238) full_prompt = ( base_prompt + agent_details_str + "\nCopy-paste this Python code into your environment to connect and interact with the MCP server:\n" + "```python\nimport requests\nimport json\n\n" + connection_code_str + "\n```\n\n" + "Use the available tools (the server will list them or consult documentation) via `call_mcp_tool` to manage your work." ) return full_prompt

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/rinadelph/Agent-MCP'

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