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
| Name | Required | Description | Default |
|---|---|---|---|
| title | No |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/noctua_mcp/mcp_server.py:91-152 (handler)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 - src/noctua_mcp/mcp_server.py:64-87 (helper)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 } - src/noctua_mcp/mcp_server.py:22-49 (registration)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/test_create_model.py:1-186 (helper)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"