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"]
|
"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",
|
"name": "get_comments",
|
||||||
"description": "Get comments for a Basecamp item",
|
"description": "Get comments for a Basecamp item",
|
||||||
@@ -369,6 +380,16 @@ class MCPServer:
|
|||||||
"results": results
|
"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":
|
elif tool_name == "get_comments":
|
||||||
recording_id = arguments.get("recording_id")
|
recording_id = arguments.get("recording_id")
|
||||||
project_id = arguments.get("project_id")
|
project_id = arguments.get("project_id")
|
||||||
|
|||||||
@@ -556,3 +556,50 @@ class BasecampSearch:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error searching campfire lines: {str(e)}")
|
logger.error(f"Error searching campfire lines: {str(e)}")
|
||||||
return []
|
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
|
# Check that expected tools are present
|
||||||
tool_names = [tool["name"] for tool in tools]
|
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:
|
for expected_tool in expected_tools:
|
||||||
assert expected_tool in tool_names
|
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:
|
if proc.poll() is None:
|
||||||
proc.terminate()
|
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():
|
def test_cli_server_invalid_method():
|
||||||
"""Test that the CLI server handles invalid methods."""
|
"""Test that the CLI server handles invalid methods."""
|
||||||
request = {
|
request = {
|
||||||
|
|||||||
Reference in New Issue
Block a user