diff --git a/docs/PROVIDERS.md b/docs/PROVIDERS.md index d57bb9d77..b98e832f6 100644 --- a/docs/PROVIDERS.md +++ b/docs/PROVIDERS.md @@ -172,16 +172,6 @@ except Exception as exc: raise ``` -**MCP tool integration:** - -Use `create_mcp_client()` from `think/utils.py` to connect to the MCP server: -```python -from think.utils import create_mcp_client - -async with create_mcp_client(config["mcp_server_url"]) as mcp: - # mcp.session provides call_tool(), list_tools(), etc. -``` - **Conversation continuation:** When `continue_from` is provided, load conversation history using: diff --git a/tests/test_planner.py b/tests/test_planner.py index 437591220..55f165d49 100644 --- a/tests/test_planner.py +++ b/tests/test_planner.py @@ -57,43 +57,3 @@ def test_planner_main(tmp_path, monkeypatch, capsys): mod.main() out = capsys.readouterr().out.strip() assert out == "ok" - - -def test_load_prompt_with_mcp_tools(monkeypatch): - """Test that _load_prompt includes MCP tools when available.""" - sys.modules.pop("think.planner", None) - - # Import and patch - mod = importlib.import_module("think.planner") - - async def fake_get_mcp_tools(): - return "\n## Available Tools\n\n**test_tool**: A test tool for testing" - - monkeypatch.setattr(mod, "_get_mcp_tools", fake_get_mcp_tools) - - # Test the function - prompt = mod._load_prompt() - - # Check that tools section was added - assert "## Available Tools" in prompt - assert "**test_tool**: A test tool for testing" in prompt - - -def test_load_prompt_without_mcp_tools(monkeypatch): - """Test that _load_prompt works when MCP tools are not available.""" - sys.modules.pop("think.planner", None) - - # Import and patch - mod = importlib.import_module("think.planner") - - async def unavailable_tools(): - raise RuntimeError("MCP not available") - - monkeypatch.setattr(mod, "_get_mcp_tools", unavailable_tools) - - # Test the function - prompt = mod._load_prompt() - - # Check that it still returns the base prompt without tools - assert "You are a strategic research planner" in prompt - assert "## Available Tools" not in prompt diff --git a/tests/test_utils_mcp_http.py b/tests/test_utils_mcp_http.py deleted file mode 100644 index eaf5803f8..000000000 --- a/tests/test_utils_mcp_http.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python3 -# SPDX-License-Identifier: AGPL-3.0-only -# Copyright (c) 2026 sol pbc - -"""Tests for HTTP MCP integration in utils.py.""" - -import sys -from unittest.mock import patch - -import pytest - -from think.utils import create_mcp_client - - -class TestCreateMCPClientHTTP: - """Test HTTP MCP client creation and URI handling.""" - - def setup_method(self): - """Clean up any stubbed fastmcp module from other tests.""" - if "fastmcp" in sys.modules: - del sys.modules["fastmcp"] - if "fastmcp.fastmcp" in sys.modules: - del sys.modules["fastmcp.fastmcp"] - - def test_with_explicit_url(self): - """Client uses explicitly provided URL.""" - with patch("fastmcp.Client") as mock_client: - result = create_mcp_client(" http://127.0.0.1:6270/mcp/ ") - - mock_client.assert_called_once_with( - "http://127.0.0.1:6270/mcp/", timeout=15.0 - ) - assert result == mock_client.return_value - - def test_empty_url_error(self): - """Error is raised when provided URL is empty or whitespace.""" - with pytest.raises(RuntimeError, match="MCP server URL not provided"): - create_mcp_client(" ") diff --git a/think/planner.py b/think/planner.py index f17535d83..1f76af1d6 100644 --- a/think/planner.py +++ b/think/planner.py @@ -2,8 +2,6 @@ # Copyright (c) 2026 sol pbc import argparse -import asyncio -import logging import os import sys from pathlib import Path @@ -12,49 +10,10 @@ from .prompts import load_prompt from .utils import setup_cli -async def _get_mcp_tools() -> str: - """Return formatted MCP tools information for the prompt.""" - - try: - from think.mcp import mcp - - tools = await mcp.get_tools() - if not tools: - return "" - - lines = [ - "", - "## Available Tools", - "", - "The following tools are available for use in your plans:", - "", - ] - - for name in sorted(tools.keys()): - tool = tools[name] - description = tool.description or "No description available" - lines.append(f"**{name}**: {description}") - - return "\n".join(lines) - except Exception as exc: - logging.debug("Failed to fetch MCP tools: %s", exc) - return "" - - def _load_prompt() -> str: """Return system instruction text for planning.""" prompt_content = load_prompt("planner", base_dir=Path(__file__).parent) - base_prompt = prompt_content.text - - # Try to add MCP tools information - try: - tools_info = asyncio.run(_get_mcp_tools()) - if tools_info: - return base_prompt + "\n" + tools_info - except Exception as exc: - logging.debug("Failed to load MCP tools for prompt: %s", exc) - - return base_prompt + return prompt_content.text def generate_plan(request: str) -> str: diff --git a/think/providers/shared.py b/think/providers/shared.py index cd68b7ba7..cac0d33c7 100644 --- a/think/providers/shared.py +++ b/think/providers/shared.py @@ -159,52 +159,9 @@ class JSONEventCallback: pass -# --------------------------------------------------------------------------- -# MCP Tool Result Extraction -# --------------------------------------------------------------------------- - - -def extract_tool_result(result: Any) -> Any: - """Extract content from MCP CallToolResult. - - Handles: - - CallToolResult with content list of TextContent objects - - CallToolResult with single content - - Direct result values (dict, string, etc.) - - Parameters - ---------- - result - Raw result from MCP tool call. - - Returns - ------- - Any - Normalized result suitable for event logging and LLM responses. - """ - if not hasattr(result, "content"): - return result - - content = result.content - if not isinstance(content, list): - return content - - # Extract text from TextContent objects - extracted = [] - for item in content: - if hasattr(item, "text"): - extracted.append(item.text) - else: - extracted.append(item) - - # Return single item directly, otherwise list - return extracted[0] if len(extracted) == 1 else extracted - - __all__ = [ "Event", "GenerateResult", "JSONEventCallback", "ThinkingEvent", - "extract_tool_result", ] diff --git a/think/utils.py b/think/utils.py index fb2121f96..0fca2134a 100644 --- a/think/utils.py +++ b/think/utils.py @@ -535,18 +535,6 @@ def setup_cli(parser: argparse.ArgumentParser, *, parse_known: bool = False): return (args, extra) if parse_known else args -def create_mcp_client(http_uri: str) -> Any: - """Return a FastMCP HTTP client for solstone tools.""" - - http_uri = http_uri.strip() - if not http_uri: - raise RuntimeError("MCP server URL not provided") - - from fastmcp import Client - - return Client(http_uri, timeout=15.0) - - def parse_time_range(text: str) -> Optional[tuple[str, str, str]]: """Return ``(day, start, end)`` from a natural language time range.