chore: remove trailing spaces and ensure newline
This commit is contained in:
@@ -37,11 +37,11 @@ logger = logging.getLogger('mcp_cli_server')
|
||||
|
||||
class MCPServer:
|
||||
"""MCP server implementing the Model Context Protocol for Cursor."""
|
||||
|
||||
|
||||
def __init__(self):
|
||||
self.tools = self._get_available_tools()
|
||||
logger.info("MCP CLI Server initialized")
|
||||
|
||||
|
||||
def _get_available_tools(self) -> List[Dict[str, Any]]:
|
||||
"""Get list of available tools for Basecamp."""
|
||||
return [
|
||||
@@ -58,7 +58,7 @@ class MCPServer:
|
||||
"name": "get_project",
|
||||
"description": "Get details for a specific project",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"project_id": {"type": "string", "description": "The project ID"}
|
||||
},
|
||||
@@ -124,34 +124,34 @@ class MCPServer:
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def _get_basecamp_client(self) -> Optional[BasecampClient]:
|
||||
"""Get authenticated Basecamp client."""
|
||||
try:
|
||||
token_data = token_storage.get_token()
|
||||
logger.debug(f"Token data retrieved: {token_data}")
|
||||
|
||||
|
||||
if not token_data or not token_data.get('access_token'):
|
||||
logger.error("No OAuth token available")
|
||||
return None
|
||||
|
||||
|
||||
# Check if token is expired
|
||||
if token_storage.is_token_expired():
|
||||
logger.error("OAuth token has expired")
|
||||
return None
|
||||
|
||||
|
||||
# Get account_id from token data first, then fall back to env var
|
||||
account_id = token_data.get('account_id') or os.getenv('BASECAMP_ACCOUNT_ID')
|
||||
|
||||
|
||||
# Set a default user agent if none is provided
|
||||
user_agent = os.getenv('USER_AGENT') or "Basecamp MCP Server (cursor@example.com)"
|
||||
|
||||
|
||||
if not account_id:
|
||||
logger.error(f"Missing account_id. Token data: {token_data}, Env BASECAMP_ACCOUNT_ID: {os.getenv('BASECAMP_ACCOUNT_ID')}")
|
||||
return None
|
||||
|
||||
|
||||
logger.debug(f"Creating Basecamp client with account_id: {account_id}, user_agent: {user_agent}")
|
||||
|
||||
|
||||
return BasecampClient(
|
||||
access_token=token_data['access_token'],
|
||||
account_id=account_id,
|
||||
@@ -161,7 +161,7 @@ class MCPServer:
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating Basecamp client: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def handle_request(self, request: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Handle an MCP request."""
|
||||
method = request.get("method")
|
||||
@@ -169,9 +169,9 @@ class MCPServer:
|
||||
method_lower = method.lower() if isinstance(method, str) else ''
|
||||
params = request.get("params", {})
|
||||
request_id = request.get("id")
|
||||
|
||||
|
||||
logger.info(f"Handling request: {method}")
|
||||
|
||||
|
||||
try:
|
||||
if method_lower == "initialize":
|
||||
return {
|
||||
@@ -188,12 +188,12 @@ class MCPServer:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
elif method_lower == "initialized":
|
||||
# This is a notification, no response needed
|
||||
logger.info("Received initialized notification")
|
||||
return None
|
||||
|
||||
|
||||
elif method_lower in ("tools/list", "listtools"):
|
||||
return {
|
||||
"jsonrpc": "2.0",
|
||||
@@ -202,13 +202,13 @@ class MCPServer:
|
||||
"tools": self.tools
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
elif method_lower in ("tools/call", "toolscall"):
|
||||
tool_name = params.get("name")
|
||||
arguments = params.get("arguments", {})
|
||||
|
||||
|
||||
result = self._execute_tool(tool_name, arguments)
|
||||
|
||||
|
||||
return {
|
||||
"jsonrpc": "2.0",
|
||||
"id": request_id,
|
||||
@@ -221,7 +221,7 @@ class MCPServer:
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
elif method_lower in ("listofferings", "list_offerings", "loffering"):
|
||||
# Respond to Cursor's ListOfferings UI request
|
||||
offerings = []
|
||||
@@ -238,7 +238,7 @@ class MCPServer:
|
||||
"offerings": offerings
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
elif method_lower == "ping":
|
||||
# Handle ping requests
|
||||
return {
|
||||
@@ -246,7 +246,7 @@ class MCPServer:
|
||||
"id": request_id,
|
||||
"result": {}
|
||||
}
|
||||
|
||||
|
||||
else:
|
||||
return {
|
||||
"jsonrpc": "2.0",
|
||||
@@ -256,7 +256,7 @@ class MCPServer:
|
||||
"message": f"Method not found: {method}"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error handling request: {e}")
|
||||
return {
|
||||
@@ -267,7 +267,7 @@ class MCPServer:
|
||||
"message": f"Internal error: {str(e)}"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def _execute_tool(self, tool_name: str, arguments: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Execute a tool and return the result."""
|
||||
client = self._get_basecamp_client()
|
||||
@@ -283,7 +283,7 @@ class MCPServer:
|
||||
"error": "Authentication required",
|
||||
"message": "Please authenticate with Basecamp first. Visit http://localhost:8000 to log in."
|
||||
}
|
||||
|
||||
|
||||
try:
|
||||
if tool_name == "get_projects":
|
||||
projects = client.get_projects()
|
||||
@@ -292,7 +292,7 @@ class MCPServer:
|
||||
"projects": projects,
|
||||
"count": len(projects)
|
||||
}
|
||||
|
||||
|
||||
elif tool_name == "get_project":
|
||||
project_id = arguments.get("project_id")
|
||||
project = client.get_project(project_id)
|
||||
@@ -300,7 +300,7 @@ class MCPServer:
|
||||
"status": "success",
|
||||
"project": project
|
||||
}
|
||||
|
||||
|
||||
elif tool_name == "get_todolists":
|
||||
project_id = arguments.get("project_id")
|
||||
todolists = client.get_todolists(project_id)
|
||||
@@ -309,7 +309,7 @@ class MCPServer:
|
||||
"todolists": todolists,
|
||||
"count": len(todolists)
|
||||
}
|
||||
|
||||
|
||||
elif tool_name == "get_todos":
|
||||
todolist_id = arguments.get("todolist_id")
|
||||
todos = client.get_todos(todolist_id)
|
||||
@@ -318,14 +318,14 @@ class MCPServer:
|
||||
"todos": todos,
|
||||
"count": len(todos)
|
||||
}
|
||||
|
||||
|
||||
elif tool_name == "search_basecamp":
|
||||
query = arguments.get("query")
|
||||
project_id = arguments.get("project_id")
|
||||
|
||||
|
||||
search = BasecampSearch(client=client)
|
||||
results = {}
|
||||
|
||||
|
||||
if project_id:
|
||||
# Search within specific project
|
||||
results["todolists"] = search.search_todolists(query, project_id)
|
||||
@@ -335,13 +335,13 @@ class MCPServer:
|
||||
results["projects"] = search.search_projects(query)
|
||||
results["todos"] = search.search_todos(query)
|
||||
results["messages"] = search.search_messages(query)
|
||||
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"query": query,
|
||||
"results": results
|
||||
}
|
||||
|
||||
|
||||
elif tool_name == "get_comments":
|
||||
recording_id = arguments.get("recording_id")
|
||||
bucket_id = arguments.get("bucket_id")
|
||||
@@ -351,7 +351,7 @@ class MCPServer:
|
||||
"comments": comments,
|
||||
"count": len(comments)
|
||||
}
|
||||
|
||||
|
||||
elif tool_name == "get_campfire_lines":
|
||||
project_id = arguments.get("project_id")
|
||||
campfire_id = arguments.get("campfire_id")
|
||||
@@ -361,13 +361,13 @@ class MCPServer:
|
||||
"campfire_lines": lines,
|
||||
"count": len(lines)
|
||||
}
|
||||
|
||||
|
||||
else:
|
||||
return {
|
||||
"error": "Unknown tool",
|
||||
"message": f"Tool '{tool_name}' is not supported"
|
||||
}
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error executing tool {tool_name}: {e}")
|
||||
# Check if it's a 401 error (token expired during API call)
|
||||
@@ -380,24 +380,24 @@ class MCPServer:
|
||||
"error": "Execution error",
|
||||
"message": str(e)
|
||||
}
|
||||
|
||||
|
||||
def run(self):
|
||||
"""Run the MCP server, reading from stdin and writing to stdout."""
|
||||
logger.info("Starting MCP CLI server")
|
||||
|
||||
|
||||
for line in sys.stdin:
|
||||
try:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
|
||||
|
||||
request = json.loads(line)
|
||||
response = self.handle_request(request)
|
||||
|
||||
|
||||
# Write response to stdout (only if there's a response)
|
||||
if response is not None:
|
||||
print(json.dumps(response), flush=True)
|
||||
|
||||
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"Invalid JSON received: {e}")
|
||||
error_response = {
|
||||
@@ -409,11 +409,11 @@ class MCPServer:
|
||||
}
|
||||
}
|
||||
print(json.dumps(error_response), flush=True)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Unexpected error: {e}")
|
||||
error_response = {
|
||||
"jsonrpc": "2.0",
|
||||
"jsonrpc": "2.0",
|
||||
"id": None,
|
||||
"error": {
|
||||
"code": -32603,
|
||||
@@ -424,4 +424,4 @@ class MCPServer:
|
||||
|
||||
if __name__ == "__main__":
|
||||
server = MCPServer()
|
||||
server.run()
|
||||
server.run()
|
||||
|
||||
Reference in New Issue
Block a user