Add global search tool

This commit is contained in:
George Antonopoulos
2025-06-18 08:17:55 +01:00
parent 0d64f6028c
commit 301cc0f961
3 changed files with 118 additions and 1 deletions

View File

@@ -100,6 +100,17 @@ class MCPServer:
"required": ["query"]
}
},
{
"name": "global_search",
"description": "Search projects, todos and campfire messages across all projects",
"inputSchema": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "Search query"}
},
"required": ["query"]
}
},
{
"name": "get_comments",
"description": "Get comments for a Basecamp item",
@@ -369,6 +380,16 @@ class MCPServer:
"results": results
}
elif tool_name == "global_search":
query = arguments.get("query")
search = BasecampSearch(client=client)
results = search.global_search(query)
return {
"status": "success",
"query": query,
"results": results
}
elif tool_name == "get_comments":
recording_id = arguments.get("recording_id")
project_id = arguments.get("project_id")

View File

@@ -556,3 +556,50 @@ class BasecampSearch:
except Exception as e:
logger.error(f"Error searching campfire lines: {str(e)}")
return []
def search_all_campfire_lines(self, query=None):
"""Search campfire chat lines across all projects."""
all_lines = []
try:
projects = self.client.get_projects()
for project in projects:
project_id = project["id"]
try:
campfires = self.client.get_campfires(project_id)
for campfire in campfires:
campfire_id = campfire["id"]
lines = self.client.get_campfire_lines(project_id, campfire_id)
for line in lines:
line["project"] = {"id": project_id, "name": project.get("name")}
line["campfire"] = {"id": campfire_id, "title": campfire.get("title")}
all_lines.append(line)
except Exception as e:
logger.error(f"Error getting campfire lines for project {project_id}: {str(e)}")
if query and all_lines:
q = query.lower()
filtered = []
for line in all_lines:
content = line.get("content", "") or ""
creator_name = ""
if line.get("creator"):
creator_name = line["creator"].get("name", "")
if q in content.lower() or (creator_name and q in creator_name.lower()):
filtered.append(line)
return filtered
return all_lines
except Exception as e:
logger.error(f"Error searching all campfire lines: {str(e)}")
return []
def global_search(self, query=None):
"""Search projects, todos and campfire lines at once."""
return {
"projects": self.search_projects(query),
"todos": self.search_todos(query),
"campfire_lines": self.search_all_campfire_lines(query),
}

View File

@@ -102,7 +102,7 @@ def test_cli_server_tools_list():
# Check that expected tools are present
tool_names = [tool["name"] for tool in tools]
expected_tools = ["get_projects", "search_basecamp", "get_todos"]
expected_tools = ["get_projects", "search_basecamp", "get_todos", "global_search"]
for expected_tool in expected_tools:
assert expected_tool in tool_names
@@ -175,6 +175,55 @@ def test_cli_server_tool_call_no_auth(mock_get_token):
if proc.poll() is None:
proc.terminate()
@patch.object(token_storage, 'get_token')
def test_cli_server_global_search_call_no_auth(mock_get_token):
"""Test global search tool call without authentication."""
init_request = {
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {}
}
tool_request = {
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "global_search",
"arguments": {"query": "test"}
}
}
proc = subprocess.Popen(
[sys.executable, "mcp_server_cli.py"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
try:
input_data = json.dumps(init_request) + "\n" + json.dumps(tool_request) + "\n"
stdout, stderr = proc.communicate(
input=input_data,
timeout=10
)
lines = stdout.strip().split('\n')
assert len(lines) >= 2
tool_response = json.loads(lines[1])
assert tool_response["jsonrpc"] == "2.0"
assert tool_response["id"] == 2
assert "result" in tool_response
assert "content" in tool_response["result"]
finally:
if proc.poll() is None:
proc.terminate()
def test_cli_server_invalid_method():
"""Test that the CLI server handles invalid methods."""
request = {