Skip to main content
Glama
geneontology

Noctua MCP Server

Official
by geneontology

create_model

Create a new GO-CAM model to represent biological causal activity. Optionally include a title to identify the model.

Instructions

Create a new empty GO-CAM model.

Args: title: Optional title for the model

Returns: Barista API response containing the new model ID and editor URLs

Examples:

  • create_model("RAS-RAF signaling pathway")

Notes: - The returned model_id can be used with other tools like add_individual - Models are created in "development" state by default - To add taxon information, use add_individual after creating the model

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
titleNo

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • The actual implementation of the 'create_model' tool. It is an async function decorated with @mcp.tool() that calls client.create_model(title=title), handles validation failures, errors, and returns a response with model_id and editor URLs.
    @mcp.tool()
    async def create_model(
        title: Optional[str] = None
    ) -> Dict[str, Any]:
        """
        Create a new empty GO-CAM model.
    
        Args:
            title: Optional title for the model
    
        Returns:
            Barista API response containing the new model ID and editor URLs
    
        Examples:
    
        - `create_model("RAS-RAF signaling pathway")`
          
    
        Notes:
            - The returned model_id can be used with other tools like add_individual
            - Models are created in "development" state by default
            - To add taxon information, use add_individual after creating the model
        """
        client = get_client()
        resp = client.create_model(title=title)
    
        if resp.validation_failed:
            return {
                "success": False,
                "error": "Validation failed",
                "reason": resp.validation_reason,
                "rolled_back": True
            }
    
        if resp.error:
            return {
                "success": False,
                "error": "Operation failed",
                "reason": resp.error
            }
    
        # Build minimal response
        result = {
            "success": True,
            "model_id": resp.model_id,
            "created": True
        }
    
        # Add editor URLs if we have a model ID
        if resp.model_id:
            import os
            token = os.environ.get("BARISTA_TOKEN", "")
    
            # Graph editor with token
            result["graph_editor_url"] = f"http://noctua-dev.berkeleybop.org/editor/graph/{resp.model_id}?barista_token={token}"
    
            # Pathway editor without token (URL encoded model ID)
            from urllib.parse import quote
            encoded_id = quote(resp.model_id, safe="")
            result["pathway_editor_url"] = f"http://noctua-dev.berkeleybop.org/workbench/noctua-visual-pathway-editor/?model_id={encoded_id}"
    
        return result
  • The configure_token tool and get_client helper function - get_client() lazily initializes the BaristaClient used by create_model.
    @mcp.tool()
    async def configure_token(token: str) -> Dict[str, Any]:
        """
        Configure the Barista authentication token.
    
        Args:
            token: The Barista authentication token
    
        Returns:
            Success status
        """
        import os
        global _client
    
        # Set environment variable
        os.environ["BARISTA_TOKEN"] = token
    
        # Reset client to pick up new token
        _client = None
    
        return {
            "success": True,
            "configured": True
        }
  • The FastMCP server instance ('mcp') initialization. The @mcp.tool() decorator on create_model registers it as an MCP tool.
    from fastmcp import FastMCP
    from noctua import BaristaClient
    from noctua.amigo import AmigoClient
    from noctua.models import ProteinComplexComponent, EntitySetMember
    
    # Path to guidelines directory
    GUIDELINES_DIR = Path(__file__).parent / "guidelines"
    
    mcp = FastMCP(
        "noctua-mcp",
        instructions="""
    Noctua MCP Server provides tools for editing GO-CAM models via the Barista API.
    Use these tools to create and edit GO-CAM models with individuals, facts, and evidence.
    This also provides tools for finding relevant genes and gene products (entities) for use in the model.
    
    Available operations:
    - Create new empty GO-CAM models
    - Add individuals (instances) of GO/ECO terms
    - Add facts (edges) between individuals
    - Add evidence to facts
    - Add protein complexes with validated components
    - Add entity sets (functionally interchangeable entities)
    - Remove individuals and facts
    - Query model structure
    - Search for bioentities and annotations
    
    """
    )
  • Tests for the create_model tool covering: creating with title, without title, integration test, parametric variations, and error handling.
    """
    Tests for the create_model functionality.
    """
    
    from unittest.mock import Mock, patch
    import pytest
    
    from noctua_mcp import mcp_server
    
    
    @pytest.mark.asyncio
    async def test_create_model_with_title():
        """Test creating a model with a title."""
    
        with patch("noctua_mcp.mcp_server.BaristaClient") as MockClient:
            mock_instance = Mock()
            MockClient.return_value = mock_instance
    
            from noctua import BaristaResponse
            mock_resp = Mock(spec=BaristaResponse)
            mock_resp.validation_failed = False
            mock_resp.validation_reason = None
            mock_resp.error = None
            mock_resp.success = True
            mock_resp.model_id = "gomodel:new_test_model_123"
            mock_resp.raw = {
                "message-type": "success",
                "signal": "merge",
                "data": {
                    "id": "gomodel:new_test_model_123"
                }
            }
    
            mock_instance.create_model.return_value = mock_resp
    
            mcp_server._client = None
    
            # Call the function
            result = await mcp_server.create_model.fn("Test Model Title")
    
            # Verify calls
            mock_instance.create_model.assert_called_once_with(title="Test Model Title")
    
            assert result["success"] is True
            assert result["model_id"] == "gomodel:new_test_model_123"
            assert result["created"] is True
            assert "graph_editor_url" in result
            assert "pathway_editor_url" in result
    
    
    @pytest.mark.asyncio
    async def test_create_model_without_title():
        """Test creating a model without a title."""
    
        with patch("noctua_mcp.mcp_server.BaristaClient") as MockClient:
            mock_instance = Mock()
            MockClient.return_value = mock_instance
    
            from noctua import BaristaResponse
            mock_resp = Mock(spec=BaristaResponse)
            mock_resp.validation_failed = False
            mock_resp.validation_reason = None
            mock_resp.error = None
            mock_resp.success = True
            mock_resp.model_id = "gomodel:untitled_model_456"
            mock_resp.raw = {
                "message-type": "success",
                "signal": "merge",
                "data": {
                    "id": "gomodel:untitled_model_456"
                }
            }
    
            mock_instance.create_model.return_value = mock_resp
    
            mcp_server._client = None
    
            # Call the function with no title
            result = await mcp_server.create_model.fn()
    
            # Verify calls
            mock_instance.create_model.assert_called_once_with(title=None)
    
            assert result["success"] is True
            assert result["model_id"] == "gomodel:untitled_model_456"
            assert result["created"] is True
    
    
    @pytest.mark.asyncio
    async def test_create_model_integration():
        """Test create_model integration with MCP client."""
        from fastmcp import Client
    
        client = Client("src/noctua_mcp/mcp_server.py")
        async with client:
            tools = await client.list_tools()
            tool_names = {t.name for t in tools}
    
            # Check create_model is available
            assert "create_model" in tool_names
    
            # Find the create_model tool
            create_tool = next(t for t in tools if t.name == "create_model")
    
            # Check it has the right parameters
            if hasattr(create_tool, 'parameters') and 'properties' in create_tool.parameters:
                props = create_tool.parameters['properties']
                assert 'title' in props
                assert props['title']['type'] == 'string'
    
                # title should be optional
                required = create_tool.parameters.get('required', [])
                assert 'title' not in required
    
    
    @pytest.mark.parametrize("title,expected_id", [
        ("Test Model 1", "gomodel:test1"),
        ("Another Model", "gomodel:test2"),
        (None, "gomodel:untitled"),
        ("", "gomodel:empty_title"),
    ])
    @pytest.mark.asyncio
    async def test_create_model_variations(title, expected_id):
        """Test create_model with various titles."""
    
        with patch("noctua_mcp.mcp_server.BaristaClient") as MockClient:
            mock_instance = Mock()
            MockClient.return_value = mock_instance
    
            from noctua import BaristaResponse
            mock_resp = Mock(spec=BaristaResponse)
            mock_resp.validation_failed = False
            mock_resp.validation_reason = None
            mock_resp.error = None
            mock_resp.success = True
            mock_resp.model_id = expected_id
            mock_resp.raw = {
                "message-type": "success",
                "data": {"id": expected_id}
            }
    
            mock_instance.create_model.return_value = mock_resp
    
            mcp_server._client = None
    
            result = await mcp_server.create_model.fn(title)
    
            if title is None:
                mock_instance.create_model.assert_called_once_with(title=None)
            else:
                mock_instance.create_model.assert_called_once_with(title=title)
    
            assert result["success"] is True
            assert result["model_id"] == expected_id
            assert result["created"] is True
    
    
    @pytest.mark.asyncio
    async def test_create_model_error_handling():
        """Test create_model error handling."""
    
        with patch("noctua_mcp.mcp_server.BaristaClient") as MockClient:
            mock_instance = Mock()
            MockClient.return_value = mock_instance
    
            from noctua import BaristaResponse
            mock_resp = Mock(spec=BaristaResponse)
            mock_resp.validation_failed = False
            mock_resp.validation_reason = None
            mock_resp.error = "Failed to create model"
            mock_resp.success = False
            mock_resp.model_id = None
            mock_resp.raw = {
                "message-type": "error",
                "message": "Failed to create model"
            }
    
            mock_instance.create_model.return_value = mock_resp
    
            mcp_server._client = None
    
            result = await mcp_server.create_model.fn("Test Model")
    
            assert result["success"] is False
            assert result["error"] == "Operation failed"
            assert result["reason"] == "Failed to create model"
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description carries full burden. It discloses that models are created in 'development' state by default, describes return format (model ID and editor URLs), and suggests post-creation steps. This is good behavioral transparency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Concise and well-structured with Args, Returns, Examples, and Notes sections. Every sentence adds value. No redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simplicity (1 optional param, output schema exists), the description is complete. It covers creation, default state, return value, and next steps. No gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Only one optional parameter (title) with 0% schema coverage. The description adds meaning: explains title is optional, gives example usage, and notes default behavior. It compensates well for the schema gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states 'Create a new empty GO-CAM model' using a specific verb and resource. It distinguishes from sibling tools like add_individual or add_fact, which operate on existing models.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides context on using the tool: models are created in 'development' state, and taxon can be added later with add_individual. Includes an example. Lacks explicit when-not-to-use or comparisons with other model-creation tools, but context is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/geneontology/noctua-mcp'

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