Merge pull request #7 from georgeantonopoulos/codex/add-missing-basecamp-api-functionality
Implement global search across Basecamp
This commit is contained in:
@@ -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")
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
Reference in New Issue
Block a user