Skip to main content
Glama
AstroMined

PyGithub MCP Server

by AstroMined

create_or_update_file

Create or update files in GitHub repositories by specifying owner, repo, path, content, commit message, and branch parameters.

Instructions

Create or update a file in a GitHub repository.

Args:
    params: Dictionary with file parameters
        - owner: Repository owner (username or organization)
        - repo: Repository name
        - path: Path where to create/update the file
        - content: Content of the file
        - message: Commit message
        - branch: Branch to create/update the file in
        - sha: SHA of file being replaced (for updates, optional)

Returns:
    MCP response with file creation/update result

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
paramsYes

Implementation Reference

  • MCP tool handler function for create_or_update_file. Validates params using Pydantic model and delegates to operations layer.
    @tool()
    def create_or_update_file(params: Dict) -> Dict:
        """Create or update a file in a GitHub repository.
    
        Args:
            params: Dictionary with file parameters
                - owner: Repository owner (username or organization)
                - repo: Repository name
                - path: Path where to create/update the file
                - content: Content of the file
                - message: Commit message
                - branch: Branch to create/update the file in
                - sha: SHA of file being replaced (for updates, optional)
    
        Returns:
            MCP response with file creation/update result
        """
        try:
            logger.debug(f"create_or_update_file called with params: {params}")
            # Convert dict to Pydantic model
            file_params = CreateOrUpdateFileParams(**params)
            
            # Call operation
            result = repositories.create_or_update_file(file_params)
            
            logger.debug(f"File created/updated: {file_params.path}")
            return {
                "content": [{"type": "text", "text": json.dumps(result, indent=2)}]
            }
        except ValidationError as e:
            logger.error(f"Validation error: {e}")
            return {
                "content": [{"type": "error", "text": f"Validation error: {str(e)}"}],
                "is_error": True
            }
        except GitHubError as e:
            logger.error(f"GitHub error: {e}")
            return {
                "content": [{"type": "error", "text": format_github_error(e)}],
                "is_error": True
            }
        except Exception as e:
            logger.error(f"Unexpected error: {e}")
            logger.error(traceback.format_exc())
            error_msg = str(e) if str(e) else "An unexpected error occurred"
            return {
                "content": [{"type": "error", "text": f"Internal server error: {error_msg}"}],
                "is_error": True
            }
  • Pydantic schema for CreateOrUpdateFileParams used for input validation.
    class CreateOrUpdateFileParams(RepositoryRef):
        """Parameters for creating or updating a single file."""
    
        model_config = ConfigDict(strict=True)
        
        path: str = Field(..., description="Path where to create/update the file")
        content: str = Field(..., description="Content of the file")
        message: str = Field(..., description="Commit message")
        branch: str = Field(..., description="Branch to create/update the file in")
        sha: Optional[str] = Field(None, description="SHA of file being replaced (for updates)")
    
        @field_validator('path')
        @classmethod
        def validate_path(cls, v):
            """Validate that path is not empty."""
            if not v.strip():
                raise ValueError("path cannot be empty")
            return v
    
        @field_validator('content')
        @classmethod
        def validate_content(cls, v):
            """Validate that content is not empty."""
            if not v.strip():
                raise ValueError("content cannot be empty")
            return v
    
        @field_validator('message')
        @classmethod
        def validate_message(cls, v):
            """Validate that message is not empty."""
            if not v.strip():
                raise ValueError("message cannot be empty")
            return v
    
        @field_validator('branch')
        @classmethod
        def validate_branch(cls, v):
            """Validate that branch is not empty."""
            if not v.strip():
                raise ValueError("branch cannot be empty")
            return v
  • Registration of repository tools including create_or_update_file with the MCP server.
    def register(mcp: FastMCP) -> None:
        """Register all repository tools with the MCP server.
    
        Args:
            mcp: The MCP server instance
        """
        from pygithub_mcp_server.tools import register_tools
        from .tools import (
            get_repository,
            create_repository,
            fork_repository,
            search_repositories,
            get_file_contents,
            create_or_update_file,
            push_files,
            create_branch,
            list_commits
        )
    
        # Register all repository tools
        register_tools(mcp, [
            get_repository,
            create_repository,
            fork_repository,
            search_repositories,
            get_file_contents,
            create_or_update_file,
            push_files,
            create_branch,
            list_commits
        ])
  • Core implementation that interacts with GitHub API to create or update the file, called by the tool handler.
    def create_or_update_file(params: CreateOrUpdateFileParams) -> Dict[str, Any]:
        """Create or update a file in a repository.
    
        Args:
            params: Parameters for creating or updating a file
    
        Returns:
            Result data including commit info
    
        Raises:
            GitHubError: If file creation/update fails
        """
        logger.debug(f"Creating/updating file: {params.owner}/{params.repo}/{params.path}")
        try:
            client = GitHubClient.get_instance()
            repository = client.get_repo(f"{params.owner}/{params.repo}")
            
            # Build kwargs from Pydantic model
            kwargs = {
                "path": params.path,
                "message": params.message,
                "content": params.content,
                "branch": params.branch
            }
            
            # Determine whether to create or update based on sha presence
            if params.sha:
                # Update existing file
                result = repository.update_file(sha=params.sha, **kwargs)
            else:
                # Create new file
                result = repository.create_file(**kwargs)
            
            logger.debug(f"File created/updated successfully: {params.path}")
            
            # Log detailed information about the response structure for debugging
            logger.debug(f"Commit result type: {type(result['commit']).__name__}")
            if hasattr(result['commit'], 'commit'):
                logger.debug(f"Has nested commit: {result['commit'].commit is not None}")
            
            # Extract commit information safely with fallbacks
            commit_sha = None
            commit_message = None
            commit_url = None
            
            try:
                # Get commit SHA
                if hasattr(result["commit"], "sha"):
                    commit_sha = result["commit"].sha
                
                # Get commit message with multiple fallbacks
                if hasattr(result["commit"], "commit") and result["commit"].commit is not None:
                    if hasattr(result["commit"].commit, "message"):
                        commit_message = result["commit"].commit.message
                if commit_message is None and hasattr(result["commit"], "message"):
                    commit_message = result["commit"].message
                if commit_message is None:
                    commit_message = params.message  # Fall back to the provided commit message
                
                # Get commit URL
                if hasattr(result["commit"], "html_url"):
                    commit_url = result["commit"].html_url
            except (AttributeError, KeyError) as e:
                logger.warning(f"Error extracting commit details: {e}")
                # Continue execution even if we can't get all commit details
            
            # Extract content information safely
            content_sha = None
            content_size = None
            content_url = None
            
            try:
                if hasattr(result["content"], "sha"):
                    content_sha = result["content"].sha
                if hasattr(result["content"], "size"):
                    content_size = result["content"].size
                if hasattr(result["content"], "html_url"):
                    content_url = result["content"].html_url
            except (AttributeError, KeyError) as e:
                logger.warning(f"Error extracting content details: {e}")
                # Continue execution even if we can't get all content details
            
            return {
                "commit": {
                    "sha": commit_sha,
                    "message": commit_message,
                    "html_url": commit_url
                },
                "content": {
                    "path": params.path,
                    "sha": content_sha,
                    "size": content_size,
                    "html_url": content_url
                }
            }
        except GithubException as e:
            logger.error(f"GitHub exception when creating/updating file: {str(e)}")
            raise client._handle_github_exception(e, resource_hint="content_file")

Latest Blog Posts

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/AstroMined/pygithub-mcp-server'

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