feat(mcp): Consolidate context tools into unified 'context' tool

Merge set_context, where_am_i, and init into a single 'context' tool
with action parameter (set, show, init).

- set: Set the workspace root directory (default when workspace_root provided)
- show: Show current workspace context (default when no args)
- init: Initialize beads in the current workspace directory

This reduces tool count from 3 to 1 while maintaining all functionality.

Closes beads-eub

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-19 23:30:22 -08:00
parent 2dc0c9b16e
commit e1d40733c6
5 changed files with 136 additions and 49 deletions

View File

@@ -85,7 +85,7 @@ async def mcp_client(bd_executable, temp_db, monkeypatch):
# Create test client
async with Client(mcp) as client:
# Automatically set context for the tests
await client.call_tool("set_context", {"workspace_root": workspace_root})
await client.call_tool("context", {"workspace_root": workspace_root})
yield client
# Reset connection pool and context after test
@@ -598,21 +598,69 @@ async def test_blocked_tool(mcp_client):
@pytest.mark.asyncio
async def test_init_tool(mcp_client, bd_executable):
"""Test init tool.
Note: This test validates that init can be called successfully via MCP.
The actual database is created in the workspace from mcp_client fixture,
not in a separate temp directory, because the init tool uses the connection
pool which is keyed by workspace path.
async def test_context_init_action(bd_executable):
"""Test context tool with init action.
Note: This test validates that context(action='init') can be called successfully via MCP.
Uses a fresh temp directory without an existing database.
"""
import os
import tempfile
import shutil
from beads_mcp import tools
from beads_mcp.server import mcp
# Call init tool (will init in the current workspace from mcp_client fixture)
result = await mcp_client.call_tool("init", {"prefix": "test-init"})
# Reset connection pool and context
tools._connection_pool.clear()
os.environ.pop("BEADS_CONTEXT_SET", None)
os.environ.pop("BEADS_WORKING_DIR", None)
os.environ.pop("BEADS_DB", None)
os.environ.pop("BEADS_DIR", None)
os.environ["BEADS_NO_DAEMON"] = "1"
# Create a fresh temp directory without any beads database
temp_dir = tempfile.mkdtemp(prefix="beads_init_test_")
try:
async with Client(mcp) as client:
# First set context to the fresh directory
await client.call_tool("context", {"workspace_root": temp_dir})
# Call context tool with init action
result = await client.call_tool("context", {"action": "init", "prefix": "test-init"})
output = result.content[0].text
# Verify output contains success message
assert "bd initialized successfully!" in output
assert "test-init" in output
finally:
tools._connection_pool.clear()
shutil.rmtree(temp_dir, ignore_errors=True)
os.environ.pop("BEADS_CONTEXT_SET", None)
os.environ.pop("BEADS_WORKING_DIR", None)
@pytest.mark.asyncio
async def test_context_show_action(mcp_client, temp_db):
"""Test context tool with show action.
Verifies that context(action='show') returns workspace information.
"""
# Call context tool with show action (default when no args)
result = await mcp_client.call_tool("context", {"action": "show"})
output = result.content[0].text
# Verify output contains success message
assert "bd initialized successfully!" in output
assert "test-init" in output
# Verify output contains workspace info
assert "Workspace root:" in output
assert "Database:" in output
@pytest.mark.asyncio
async def test_context_default_show(mcp_client, temp_db):
"""Test context tool defaults to show when no args provided."""
# Call context tool with no args - should default to show
result = await mcp_client.call_tool("context", {})
output = result.content[0].text
# Verify output contains workspace info (same as show action)
assert "Workspace root:" in output
assert "Database:" in output

View File

@@ -107,7 +107,7 @@ async def test_get_client_no_workspace_found():
# Verify error message is helpful
error_msg = str(exc_info.value)
assert "No beads workspace found" in error_msg
assert "set_context" in error_msg
assert "context" in error_msg
assert ".beads/" in error_msg
finally:
current_workspace.reset(token)

View File

@@ -295,7 +295,7 @@ async def test_mcp_works_with_separate_databases(git_worktree_with_separate_dbs,
# Create MCP client
async with Client(mcp) as client:
# Set context to worktree
await client.call_tool("set_context", {"workspace_root": str(worktree)})
await client.call_tool("context", {"workspace_root": str(worktree)})
# Create issue via MCP
result = await client.call_tool(