Skip to main content
Glama

Jinni: Bring Your Project Into Context

by smat-dev
test_integration_mcp.py17 kB
import pytest from pathlib import Path from mcp import types # MCP Client SDK types from conftest import run_mcp_tool_call # Import async helper import asyncio # --- MCP Tests (Asynchronous) --- @pytest.mark.asyncio async def test_mcp_read_context_basic(test_environment: Path): """Test basic MCP read_context respecting .contextfiles (dynamic rules).""" test_dir = test_environment tool_name = "read_context" # Provide mandatory targets (project root) and rules (empty list) arguments = { "project_root": str(test_dir), "targets": [str(test_dir)], "rules": [] } result = await run_mcp_tool_call(tool_name, arguments) assert isinstance(result, types.CallToolResult) assert not result.isError assert len(result.content) == 1 and isinstance(result.content[0], types.TextContent) stdout_text = result.content[0].text # Assertions based on the fixture's _create_test_structure rules and dynamic application # Root .contextfiles: file_root.txt, *.md, src/, dir_c/, dir_e/, dir_f/, !*.log, !*.tmp # dir_a .contextfiles: *.txt, !file_a1.txt, important.log, !local.md # dir_c .contextfiles: !*.data # dir_e .contextfiles: !last_rule.txt # Defaults exclude: .*, build/, etc. # Check included content assert "```path=file_root.txt" in stdout_text assert "```path=README.md" in stdout_text assert "```path=main.py" in stdout_text # Included by default '*' assert "```path=src/app.py" in stdout_text # Included via root src/ assert "```path=src/utils.py" in stdout_text # Included via root src/ assert "```path=src/nested/deep.py" in stdout_text # Included via root src/ assert "```path=dir_a/important.log" in stdout_text # Included by local override of root !*.log assert "```path=dir_c/file_c1.txt" in stdout_text # Included via root dir_c/ assert "```path=dir_f/file_f.txt" in stdout_text # Included via root dir_f/ assert "```path=docs/index.md" in stdout_text # Included via root *.md assert "```path=docs/config/options.md" in stdout_text # Included via root *.md # Check excluded content assert "root.log" not in stdout_text # Excluded by root !*.log assert "temp.tmp" not in stdout_text # Excluded by root !*.tmp assert "dir_a/file_a1.txt" not in stdout_text # Excluded locally by !file_a1.txt assert "```path=dir_b/file_b1.py" in stdout_text # Included by default '*' assert "```path=dir_d/file_d.txt" in stdout_text # Included by default '*' assert "dir_e/last_rule.txt" not in stdout_text # Excluded locally by !last_rule.txt assert "build/" not in stdout_text # Excluded by default @pytest.mark.asyncio async def test_mcp_read_context_list_only(test_environment: Path): """Test MCP read_context with list_only=True.""" test_dir = test_environment tool_name = "read_context" # Provide mandatory targets (project root) and rules (empty list) arguments = { "project_root": str(test_dir), "targets": [str(test_dir)], "rules": [], "list_only": True } result = await run_mcp_tool_call(tool_name, arguments) assert isinstance(result, types.CallToolResult) assert not result.isError assert len(result.content) == 1 and isinstance(result.content[0], types.TextContent) stdout_text = result.content[0].text actual_files = sorted([line.strip() for line in stdout_text.strip().splitlines()]) expected_files = sorted([ "README.md", "file_root.txt", "main.py", # Included by default '*' "dir_a/important.log", "dir_b/file_b1.py", # Included by default '*' "dir_b/sub_dir_b/include_me.txt", # Included by default '*' "dir_c/file_c1.txt", # .contextfiles should be excluded "dir_d/file_d.txt", # Included by default '*' "dir_f/file_f.txt", # .contextfiles should be excluded "lib/somelib.py", # Included by default '*' now that it exists "docs/config/options.md", # Included via *.md "docs/index.md", # Included via *.md "src/app.py", # Included via src/, .hidden_in_src excluded "src/nested/deep.py", # Included via src/ "src/nested/other.txt", # Add missing file "src/utils.py", # Included via src/ ]) assert actual_files == expected_files, f"Expected {expected_files}, got {actual_files}" @pytest.mark.asyncio async def test_mcp_read_context_inline_rules(test_environment: Path): """Test MCP read_context with inline rules overriding file rules.""" test_dir = test_environment tool_name = "read_context" # Provide mandatory targets (project root) and specific rules arguments = { "project_root": str(test_dir), "targets": [str(test_dir)], # Mandatory targets "rules": [ "**/*.py", "!src/app.py", "lib/**" ] # Rely on **/*.py for traversal } result = await run_mcp_tool_call(tool_name, arguments) assert isinstance(result, types.CallToolResult) assert not result.isError assert len(result.content) == 1 and isinstance(result.content[0], types.TextContent) stdout_text = result.content[0].text # Assertions based on contextfiles + inline rules (override) + defaults: assert "```path=main.py" in stdout_text # Included by inline **/*.py assert "```path=src/utils.py" in stdout_text # Included by inline **/*.py assert "```path=src/nested/deep.py" in stdout_text # Included by inline **/*.py assert "```path=lib/somelib.py" in stdout_text # Included by inline **/*.py assert "```path=src/app.py" not in stdout_text # Excluded by inline !src/app.py # With the new design, .contextfiles are still respected when using inline rules assert "```path=file_root.txt" in stdout_text # Included by .contextfiles assert "```path=README.md" in stdout_text # Included by .contextfiles (*.md) assert "```path=dir_a/important.log" in stdout_text # Included by .contextfiles in dir_a @pytest.mark.asyncio async def test_mcp_debug_explain(test_environment: Path): """Test MCP read_context with debug_explain=True.""" # This test mainly verifies the tool call succeeds and returns expected content. # Verifying server-side logging isn't feasible with stdio_client directly. test_dir = test_environment tool_name = "read_context" # Provide mandatory targets (project root) and rules (empty list) arguments = { "project_root": str(test_dir), "targets": [str(test_dir)], "rules": [], "debug_explain": True } result = await run_mcp_tool_call(tool_name, arguments) # Check stdout content (same as basic MCP test) assert isinstance(result, types.CallToolResult) assert not result.isError assert len(result.content) == 1 and isinstance(result.content[0], types.TextContent) stdout_text = result.content[0].text # Basic check for some expected content assert "```path=file_root.txt" in stdout_text assert "```path=src/app.py" in stdout_text assert "```path=dir_b/file_b1.py" in stdout_text # dir_b included by default rules @pytest.mark.asyncio async def test_mcp_read_context_target_file(test_environment: Path): """Test MCP read_context targeting a single file.""" test_dir = test_environment target_file = test_dir / "src" / "app.py" tool_name = "read_context" # Target the specific file within the project root, provide mandatory rules arguments = { "project_root": str(test_dir), "targets": [str(target_file)], "rules": [] } result = await run_mcp_tool_call(tool_name, arguments) assert isinstance(result, types.CallToolResult) assert not result.isError assert len(result.content) == 1 and isinstance(result.content[0], types.TextContent) stdout_text = result.content[0].text # Should only contain the targeted file (as it's explicitly targeted) assert "```path=src/app.py" in stdout_text # Path relative to project_root assert "print('app')" in stdout_text # Ensure no other files are present assert "```path=main.py" not in stdout_text assert "```path=README.md" not in stdout_text @pytest.mark.asyncio async def test_mcp_read_context_target_list_files(test_environment: Path): """Test MCP read_context targeting a list of specific files.""" test_dir = test_environment target_files = [ str(test_dir / "src" / "app.py"), str(test_dir / "README.md") ] tool_name = "read_context" arguments = { "project_root": str(test_dir), "targets": target_files, "rules": [] } result = await run_mcp_tool_call(tool_name, arguments) assert isinstance(result, types.CallToolResult) assert not result.isError assert len(result.content) == 1 and isinstance(result.content[0], types.TextContent) stdout_text = result.content[0].text # Should contain only the targeted files assert "```path=src/app.py" in stdout_text # Path relative to project_root assert "print('app')" in stdout_text assert "```path=README.md" in stdout_text assert "# Readme" in stdout_text # Corrected content check # Ensure no other files are present assert "```path=main.py" not in stdout_text assert "```path=file_root.txt" not in stdout_text @pytest.mark.asyncio async def test_mcp_read_context_target_list_mixed(test_environment: Path): """Test MCP read_context targeting a list with a file and a directory.""" test_dir = test_environment targets = [ str(test_dir / "file_root.txt"), str(test_dir / "src") # Target the directory ] tool_name = "read_context" arguments = { "project_root": str(test_dir), "targets": targets, "rules": [] } result = await run_mcp_tool_call(tool_name, arguments) assert isinstance(result, types.CallToolResult) assert not result.isError assert len(result.content) == 1 and isinstance(result.content[0], types.TextContent) stdout_text = result.content[0].text # Should contain the targeted file and files within the targeted directory # respecting default exclusions within that directory assert "```path=file_root.txt" in stdout_text assert "Root file content" in stdout_text # Corrected content check again assert "```path=src/app.py" in stdout_text assert "print('app')" in stdout_text assert "```path=src/utils.py" in stdout_text assert "def helper(): pass" in stdout_text # Corrected content check for utils.py assert "```path=src/nested/deep.py" in stdout_text assert "# Deep" in stdout_text # Corrected content check for deep.py # Ensure hidden file in src is still excluded by default rules assert ".hidden_in_src" not in stdout_text # Ensure files outside the targets are not present assert "```path=main.py" not in stdout_text assert "```path=README.md" not in stdout_text @pytest.mark.asyncio async def test_mcp_target_dir_uses_project_rules(test_environment: Path): """Test MCP targeting a dir within project uses project root rules.""" test_dir = test_environment # Root rule excludes utils.py (test_dir / ".contextfiles").write_text("!**/utils.py", encoding='utf-8') # Local rule in src excludes data.json (test_dir / "src" / ".contextfiles").write_text("!data.json", encoding='utf-8') tool_name = "read_context" arguments = { "project_root": str(test_dir), "targets": [str(test_dir / "src")], # Target the src directory "rules": [] # Use default rules discovery } result = await run_mcp_tool_call(tool_name, arguments) assert isinstance(result, types.CallToolResult) assert not result.isError assert len(result.content) == 1 and isinstance(result.content[0], types.TextContent) stdout_text = result.content[0].text # Expected behavior: src is within project root, so rules start from project root # Expected includes: # - app.py (included by default *) # - nested/deep.py (included by default *) # - nested/other.txt (included by default *) # Expected excludes: # - utils.py (excluded by root !**/utils.py) # - data.json (excluded by src/.contextfiles !data.json) # - .hidden_in_src (excluded by default !.*) # Check included assert "```path=src/app.py" in stdout_text assert "```path=src/nested/deep.py" in stdout_text assert "```path=src/nested/other.txt" in stdout_text # Check excluded assert "```path=src/utils.py" not in stdout_text # Excluded by root rule assert "```path=src/data.json" not in stdout_text # Excluded by local rule assert "```path=src/.hidden_in_src" not in stdout_text # Excluded by default rule # Check files outside target are not included assert "```path=main.py" not in stdout_text assert "```path=README.md" not in stdout_text @pytest.mark.asyncio async def test_mcp_read_context_target_list_empty_defaults_to_root(test_environment: Path): """Test MCP read_context targeting an empty list defaults to project root.""" test_dir = test_environment tool_name = "read_context" # Target an empty list - should default to processing the root arguments = { "project_root": str(test_dir), "targets": [], "rules": [] } # Provide mandatory rules result = await run_mcp_tool_call(tool_name, arguments) assert isinstance(result, types.CallToolResult) assert not result.isError # Should not be an error assert len(result.content) == 1 and isinstance(result.content[0], types.TextContent) stdout_text = result.content[0].text # Assertions should match the basic test (processing the root) assert "```path=file_root.txt" in stdout_text assert "```path=README.md" in stdout_text assert "```path=main.py" in stdout_text @pytest.mark.asyncio async def test_mcp_read_context_target_list_invalid(test_environment: Path): """Test MCP read_context targeting a list with an invalid path.""" test_dir = test_environment targets = [ str(test_dir / "src" / "app.py"), str(test_dir / "non_existent_file.txt") # Invalid path ] tool_name = "read_context" arguments = { "project_root": str(test_dir), "targets": targets, "rules": [] } result = await run_mcp_tool_call(tool_name, arguments) assert isinstance(result, types.CallToolResult) assert result.isError # Expecting an error for invalid path assert len(result.content) == 1 and isinstance(result.content[0], types.TextContent) # Check the error message assert "does not exist" in result.content[0].text assert "non_existent_file.txt" in result.content[0].text # No need to check stdout_text as it's an error result # --- Tests for mandatory arguments --- @pytest.mark.asyncio async def test_mcp_read_context_missing_targets_raises_error(test_environment: Path): """Test MCP read_context raises error when mandatory 'targets' is missing.""" test_dir = test_environment tool_name = "read_context" # Call without the 'targets' argument - should raise error arguments = { "project_root": str(test_dir), "rules": [] } # Provide mandatory rules result = await run_mcp_tool_call(tool_name, arguments) assert isinstance(result, types.CallToolResult) assert result.isError # Should be an error assert len(result.content) == 1 and isinstance(result.content[0], types.TextContent) error_text = result.content[0].text # Check that the error message mentions the missing field assert "targets" in error_text.lower() and ("required" in error_text.lower() or "missing" in error_text.lower()) @pytest.mark.asyncio async def test_mcp_read_context_missing_rules_raises_error(test_environment: Path): """Test MCP read_context raises error when mandatory 'rules' is missing.""" test_dir = test_environment tool_name = "read_context" # Call without the 'rules' argument - should raise error arguments = { "project_root": str(test_dir), "targets": [str(test_dir)] } # Provide mandatory targets result = await run_mcp_tool_call(tool_name, arguments) assert isinstance(result, types.CallToolResult) assert result.isError # Should be an error assert len(result.content) == 1 and isinstance(result.content[0], types.TextContent) error_text = result.content[0].text # Check that the error message mentions the missing field assert "rules" in error_text.lower() and ("required" in error_text.lower() or "missing" in error_text.lower()) @pytest.mark.asyncio async def test_mcp_nul_in_target_triggers_valueerror(test_environment: Path): """Test that a target with an embedded NUL triggers a clean ValueError and does not crash.""" test_dir = test_environment bad_target = str(test_dir) + "\x00bad" tool_name = "read_context" arguments = { "project_root": str(test_dir), "targets": [bad_target], "rules": [] } from mcp import types result = await run_mcp_tool_call(tool_name, arguments) assert isinstance(result, types.CallToolResult) assert result.isError assert "Embedded NUL" in result.content[0].text or "\x00" in result.content[0].text

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/smat-dev/jinni'

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