We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/brianirish/laravel-docs-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
"""Integration tests for Laravel MCP Companion using FastMCP Client."""
import pytest
import asyncio
from pathlib import Path
import tempfile
from fastmcp import Client
from laravel_mcp_companion import create_mcp_server
@pytest.fixture
def temp_docs_dir():
"""Create a temporary documentation directory."""
with tempfile.TemporaryDirectory() as tmpdir:
docs_path = Path(tmpdir)
# Create some test documentation files
version_dir = docs_path / "12.x"
version_dir.mkdir(parents=True)
(version_dir / "blade.md").write_text("# Blade Templates\n\nThis is Blade documentation.")
(version_dir / "eloquent.md").write_text("# Eloquent ORM\n\nThis is Eloquent documentation.")
(version_dir / "routing.md").write_text("# Routing\n\nThis is routing documentation.")
# Create metadata file
metadata_file = version_dir / ".metadata.json"
metadata_file.write_text("""{
"version": "12.x",
"commit_sha": "abc123",
"commit_date": "2024-01-01",
"commit_message": "Test commit",
"sync_time": "2024-01-01T00:00:00Z"
}""")
yield docs_path
@pytest.fixture
def mcp_server(temp_docs_dir):
"""Create an MCP server instance for testing."""
server = create_mcp_server("TestLaravelMCP", temp_docs_dir, "12.x")
return server
class TestMCPIntegration:
"""Test MCP server integration using FastMCP Client."""
@pytest.mark.asyncio
async def test_list_laravel_docs_tool(self, mcp_server):
"""Test the list_laravel_docs tool through MCP protocol."""
async with Client(mcp_server) as client:
# Call the tool
result = await client.call_tool("list_laravel_docs", {"version": "12.x"})
# Verify result
assert result is not None
# FastMCP 2.10+ returns CallToolResult with .content attribute containing list of TextContent
if hasattr(result, 'content') and isinstance(result.content, list):
content = result.content[0].text if result.content else ""
else:
content = str(result)
assert "blade.md" in content
assert "eloquent.md" in content
assert "routing.md" in content
# Removed test_read_laravel_doc_resource - it was testing FastMCP's URI resolution, not our code.
# The read_laravel_doc function should be tested directly in unit tests.
@pytest.mark.asyncio
async def test_search_laravel_docs_tool(self, mcp_server):
"""Test the search_laravel_docs tool through MCP protocol."""
async with Client(mcp_server) as client:
# Search for a term
result = await client.call_tool("search_laravel_docs", {
"query": "Eloquent",
"version": "12.x"
})
# Verify result
assert result is not None
# FastMCP 2.10+ returns CallToolResult with .content attribute containing list of TextContent
if hasattr(result, 'content') and isinstance(result.content, list):
content = result.content[0].text if result.content else ""
else:
content = str(result)
assert "eloquent.md" in content
@pytest.mark.asyncio
async def test_laravel_docs_info_tool(self, mcp_server):
"""Test the laravel_docs_info tool through MCP protocol."""
async with Client(mcp_server) as client:
# Get documentation info
result = await client.call_tool("laravel_docs_info", {"version": "12.x"})
# Verify result
assert result is not None
# FastMCP 2.10+ returns CallToolResult with .content attribute containing list of TextContent
if hasattr(result, 'content') and isinstance(result.content, list):
content = result.content[0].text if result.content else ""
else:
content = str(result)
# TOON format: version and commit_sha fields
assert "12.x" in content
assert "abc123" in content
@pytest.mark.asyncio
async def test_get_laravel_package_recommendations_tool(self, mcp_server):
"""Test the get_laravel_package_recommendations tool through MCP protocol."""
async with Client(mcp_server) as client:
# Get package recommendations
result = await client.call_tool("get_laravel_package_recommendations", {
"use_case": "authentication"
})
# Verify result
assert result is not None
# FastMCP 2.10+ returns CallToolResult with .content attribute containing list of TextContent
if hasattr(result, 'content') and isinstance(result.content, list):
content = result.content[0].text if result.content else ""
else:
content = str(result)
# TOON format: context field contains the use case
assert "authentication" in content
# Removed test_read_nonexistent_doc - it was testing FastMCP's URI resolution, not our code.
# Error handling in read_laravel_doc should be tested directly in unit tests.
# Removed test_invalid_tool_parameters - it was testing FastMCP's parameter validation.
# Our tool parameter validation should be tested in unit tests.
@pytest.mark.asyncio
async def test_list_all_tools(self, mcp_server):
"""Test listing all available tools."""
async with Client(mcp_server) as client:
# Get list of tools
tools = await client.list_tools()
# Verify some expected tools are present
tool_names = [tool.name for tool in tools]
assert "list_laravel_docs" in tool_names
assert "search_laravel_docs" in tool_names
assert "update_laravel_docs" in tool_names
assert "get_laravel_package_recommendations" in tool_names
@pytest.mark.asyncio
async def test_list_all_resources(self, mcp_server):
"""Test listing all available resource templates."""
async with Client(mcp_server) as client:
# Get list of resource templates (not static resources)
templates = await client.session.list_resource_templates()
assert templates is not None
# Check if any resource templates are registered
template_uris = [t.uriTemplate for t in templates.resourceTemplates]
print(f"Registered resource templates: {template_uris}")
# We should have at least the laravel:// resource templates
assert any("laravel://" in str(uri) for uri in template_uris)
assert any("laravel-external://" in str(uri) for uri in template_uris)
class TestMCPExternalDocumentation:
"""Test external documentation tools through MCP protocol."""
@pytest.mark.asyncio
async def test_list_laravel_services(self, mcp_server):
"""Test listing Laravel services."""
async with Client(mcp_server) as client:
# List services
result = await client.call_tool("list_laravel_services", {})
# Verify result
assert result is not None
# FastMCP 2.10+ returns CallToolResult with .content attribute containing list of TextContent
if hasattr(result, 'content') and isinstance(result.content, list):
content = result.content[0].text if result.content else ""
else:
content = str(result)
# TOON format: services list with count
assert "services" in content or "count" in content
@pytest.mark.asyncio
async def test_search_external_docs_empty_query(self, mcp_server):
"""Test searching external docs with empty query."""
async with Client(mcp_server) as client:
# Search with empty query
result = await client.call_tool("search_external_laravel_docs", {"query": ""})
# Verify error message
assert result is not None
# FastMCP 2.10+ returns CallToolResult with .content attribute containing list of TextContent
if hasattr(result, 'content') and isinstance(result.content, list):
content = result.content[0].text if result.content else ""
else:
content = str(result)
assert "Search query cannot be empty" in content
class TestMCPPackageTools:
"""Test package-related tools through MCP protocol."""
@pytest.mark.asyncio
async def test_get_package_info(self, mcp_server):
"""Test getting package information."""
async with Client(mcp_server) as client:
# Get package info
result = await client.call_tool("get_laravel_package_info", {
"package_name": "laravel/sanctum"
})
# Verify result
assert result is not None
# FastMCP 2.10+ returns CallToolResult with .content attribute containing list of TextContent
if hasattr(result, 'content') and isinstance(result.content, list):
content = result.content[0].text if result.content else ""
else:
content = str(result)
assert "Laravel Sanctum" in content
@pytest.mark.asyncio
async def test_get_package_categories(self, mcp_server):
"""Test getting packages by category."""
async with Client(mcp_server) as client:
# Get packages in authentication category
result = await client.call_tool("get_laravel_package_categories", {
"category": "authentication"
})
# Verify result
assert result is not None
# FastMCP 2.10+ returns CallToolResult with .content attribute containing list of TextContent
if hasattr(result, 'content') and isinstance(result.content, list):
content = result.content[0].text if result.content else ""
else:
content = str(result)
# TOON format: context contains category
assert "authentication" in content
@pytest.mark.asyncio
async def test_get_features_for_package(self, mcp_server):
"""Test getting features for a package."""
async with Client(mcp_server) as client:
# Get features for Laravel Cashier
result = await client.call_tool("get_features_for_laravel_package", {
"package": "laravel/cashier"
})
# Verify result
assert result is not None
# FastMCP 2.10+ returns CallToolResult with .content attribute containing list of TextContent
if hasattr(result, 'content') and isinstance(result.content, list):
content = result.content[0].text if result.content else ""
else:
content = str(result)
# TOON format: package name should be present
assert "laravel/cashier" in content
@pytest.mark.integration
class TestMCPServerLifecycle:
"""Test MCP server lifecycle operations."""
@pytest.mark.asyncio
async def test_multiple_client_connections(self, mcp_server):
"""Test multiple clients connecting to the same server."""
# First client
async with Client(mcp_server) as client1:
result1 = await client1.call_tool("list_laravel_docs", {"version": "12.x"})
assert result1 is not None
# Second client (after first closes)
async with Client(mcp_server) as client2:
result2 = await client2.call_tool("list_laravel_docs", {"version": "12.x"})
assert result2 is not None
@pytest.mark.asyncio
async def test_concurrent_tool_calls(self, mcp_server):
"""Test calling multiple tools concurrently."""
async with Client(mcp_server) as client:
# Call multiple tools concurrently
results = await asyncio.gather(
client.call_tool("list_laravel_docs", {}),
client.call_tool("laravel_docs_info", {}),
client.call_tool("list_laravel_services", {})
)
# Verify all results
assert len(results) == 3
assert all(result is not None for result in results)