Millionaire-456 commited on
Commit
9d445ba
Β·
verified Β·
1 Parent(s): a4caceb

Upload 4 files

Browse files
app.py CHANGED
@@ -1,95 +1,11 @@
1
  #!/usr/bin/env python3
2
  """
3
- πŸ† TopCoder MCP Agent - Hugging Face Spaces Deployment
4
- FIXED VERSION - Enhanced error handling and debugging
5
  """
6
 
7
- import os
8
- import sys
9
- import logging
10
- from pathlib import Path
11
-
12
- # Add current directory to Python path
13
- sys.path.insert(0, str(Path(__file__).parent))
14
-
15
- # Configure logging for Hugging Face Spaces
16
- logging.basicConfig(
17
- level=logging.INFO,
18
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
19
- )
20
-
21
- logger = logging.getLogger(__name__)
22
-
23
- def create_app():
24
- """Create and return the Gradio app"""
25
- try:
26
- logger.info("πŸš€ Starting TopCoder MCP Agent for Hugging Face Spaces")
27
- logger.info("πŸ”§ FIXED VERSION with enhanced debugging")
28
-
29
- # Try the simple app first to avoid dict key errors
30
- from simple_mcp_app import create_simple_app
31
- app = create_simple_app()
32
-
33
- logger.info("βœ… App created successfully")
34
- return app
35
-
36
- except Exception as e:
37
- logger.error(f"❌ Failed to create app: {e}")
38
- # Fallback to basic debug version
39
- try:
40
- logger.info("πŸ”„ Trying fallback debug version...")
41
- from mcp_debug_gradio import create_debug_app
42
- app = create_debug_app()
43
- logger.info("βœ… Debug app created successfully")
44
- return app
45
- except Exception as fallback_error:
46
- logger.error(f"❌ Simple app also failed: {fallback_error}")
47
- # Try the debug version as last resort
48
- try:
49
- from mcp_debug_gradio import create_debug_app
50
- app = create_debug_app()
51
- logger.info("βœ… Debug app created as fallback")
52
- return app
53
- except Exception as debug_error:
54
- logger.error(f"❌ All fallbacks failed: {debug_error}")
55
- raise e
56
-
57
- def main():
58
- """Main function for Hugging Face Spaces"""
59
- try:
60
- # Create the app
61
- app = create_app()
62
-
63
- # Launch for Hugging Face Spaces
64
- app.launch(
65
- server_name="0.0.0.0",
66
- server_port=int(os.environ.get("PORT", 7860)),
67
- show_error=True,
68
- # Hugging Face Spaces settings
69
- share=False # Don't need share=True in HF Spaces
70
- )
71
-
72
- except Exception as e:
73
- logger.error(f"❌ Failed to start application: {e}")
74
-
75
- # Emergency fallback - create a simple diagnostic interface
76
- import gradio as gr
77
-
78
- def show_error():
79
- return f"❌ Application failed to start: {str(e)}\n\nCheck the logs for more details."
80
-
81
- emergency_app = gr.Interface(
82
- fn=show_error,
83
- inputs=[],
84
- outputs=gr.Textbox(label="Error Details"),
85
- title="🚨 TopCoder MCP Agent - Startup Error"
86
- )
87
-
88
- emergency_app.launch(
89
- server_name="0.0.0.0",
90
- server_port=int(os.environ.get("PORT", 7860)),
91
- show_error=True
92
- )
93
 
94
  if __name__ == "__main__":
95
- main()
 
1
  #!/usr/bin/env python3
2
  """
3
+ Entry point for TopCoder Elite AI Mentor - Championship Edition
4
+ This file serves as the main entry point for Hugging Face Spaces deployment.
5
  """
6
 
7
+ # Import the complete application
8
+ from complete_mcp_gradio_app import main
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
  if __name__ == "__main__":
11
+ main()
complete_mcp_gradio_app.py ADDED
@@ -0,0 +1,934 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Complete TopCoder AI Agent with MCP Integration
4
+ A ready-to-deploy Gradio application that uses MCP to enhance competitive programming assistance.
5
+
6
+ This file combines everything you need:
7
+ 1. Modern MCP client implementation
8
+ 2. AI agent with multi-pattern analysis
9
+ 3. Gradio interface
10
+ 4. Error handling and logging
11
+ 5. Ready for Hugging Face Spaces deployment
12
+ """
13
+
14
+ import asyncio
15
+ import aiohttp
16
+ import json
17
+ import uuid
18
+ import datetime
19
+ import logging
20
+ import gradio as gr
21
+ import time
22
+ from typing import Dict, Any, Optional, List, Tuple
23
+ from dataclasses import dataclass
24
+
25
+ # Configure logging
26
+ logging.basicConfig(
27
+ level=logging.INFO,
28
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
29
+ )
30
+ logger = logging.getLogger(__name__)
31
+
32
+ @dataclass
33
+ class MCPResponse:
34
+ """Structured response from MCP server"""
35
+ success: bool
36
+ data: Any = None
37
+ error: str = None
38
+
39
+ class TopcoderMCPClient:
40
+ """
41
+ Production-ready MCP Client for TopCoder API
42
+ Uses Streamable HTTP transport per MCP 2025 specification
43
+ """
44
+
45
+ def __init__(self, base_url: str = "https://api.topcoder-dev.com/v6/mcp"):
46
+ self.base_url = base_url
47
+ self.session_id = str(uuid.uuid4())
48
+ self.request_id = 1
49
+ self.initialized = False
50
+ self.timeout = aiohttp.ClientTimeout(total=30)
51
+
52
+ self.headers = {
53
+ "Content-Type": "application/json",
54
+ "Accept": "application/json, text/event-stream",
55
+ "User-Agent": "TopCoder-MCP-Agent/1.0",
56
+ "Cache-Control": "no-cache"
57
+ }
58
+
59
+ def _get_next_id(self) -> int:
60
+ """Generate next request ID"""
61
+ self.request_id += 1
62
+ return self.request_id
63
+
64
+ def _create_request(self, method: str, params: Dict[str, Any] = None) -> Dict[str, Any]:
65
+ """Create JSON-RPC 2.0 request"""
66
+ request = {
67
+ "jsonrpc": "2.0",
68
+ "method": method,
69
+ "id": self._get_next_id()
70
+ }
71
+ if params is not None:
72
+ request["params"] = params
73
+ return request
74
+
75
+ async def _send_request(self, request: Dict[str, Any], endpoint: str = "mcp") -> MCPResponse:
76
+ """Send request using Streamable HTTP transport"""
77
+ urls_to_try = [
78
+ f"{self.base_url}/{endpoint}",
79
+ f"{self.base_url}/sse",
80
+ f"{self.base_url}/stream"
81
+ ]
82
+
83
+ for url in urls_to_try:
84
+ try:
85
+ logger.info(f"πŸ”„ Trying {url} for method: {request['method']}")
86
+
87
+ async with aiohttp.ClientSession(timeout=self.timeout) as session:
88
+ async with session.post(url, json=request, headers=self.headers) as response:
89
+
90
+ if response.status == 200:
91
+ content_type = response.headers.get('content-type', '').lower()
92
+
93
+ if 'text/event-stream' in content_type:
94
+ return await self._parse_sse_response(response)
95
+ elif 'application/json' in content_type:
96
+ data = await response.json()
97
+ if 'error' in data:
98
+ logger.warning(f"MCP Error: {data['error']}")
99
+ continue # Try next URL
100
+ return MCPResponse(success=True, data=data.get('result'))
101
+ else:
102
+ text = await response.text()
103
+ return MCPResponse(success=True, data=text)
104
+ else:
105
+ error_text = await response.text()
106
+ logger.warning(f"HTTP {response.status} from {url}: {error_text}")
107
+ continue # Try next URL
108
+
109
+ except Exception as e:
110
+ logger.warning(f"Error with {url}: {e}")
111
+ continue
112
+
113
+ return MCPResponse(success=False, error="All MCP endpoints failed")
114
+
115
+ async def _parse_sse_response(self, response) -> MCPResponse:
116
+ """Parse Server-Sent Events response"""
117
+ try:
118
+ text = await response.text()
119
+ logger.debug(f"SSE Response: {text}")
120
+
121
+ lines = text.strip().split('\n')
122
+ for line in lines:
123
+ line = line.strip()
124
+ if line.startswith('data: '):
125
+ data_content = line[6:]
126
+ if data_content and data_content != '[DONE]':
127
+ try:
128
+ data = json.loads(data_content)
129
+ if 'error' in data:
130
+ return MCPResponse(success=False, error=data['error'].get('message', 'SSE error'))
131
+ return MCPResponse(success=True, data=data.get('result'))
132
+ except json.JSONDecodeError:
133
+ continue
134
+
135
+ return MCPResponse(success=False, error="No valid data in SSE response")
136
+
137
+ except Exception as e:
138
+ return MCPResponse(success=False, error=f"SSE parsing error: {e}")
139
+
140
+ async def initialize(self) -> MCPResponse:
141
+ """Initialize MCP session"""
142
+ if self.initialized:
143
+ return MCPResponse(success=True, data="Already initialized")
144
+
145
+ params = {
146
+ "protocolVersion": "2025-03-26",
147
+ "capabilities": {"tools": {}},
148
+ "clientInfo": {
149
+ "name": "topcoder-ai-agent",
150
+ "version": "1.0.0"
151
+ }
152
+ }
153
+
154
+ request = self._create_request("initialize", params)
155
+ response = await self._send_request(request)
156
+
157
+ if response.success:
158
+ self.initialized = True
159
+ logger.info("βœ… MCP session initialized")
160
+
161
+ return response
162
+
163
+ async def list_tools(self) -> MCPResponse:
164
+ """List available MCP tools"""
165
+ if not self.initialized:
166
+ init_response = await self.initialize()
167
+ if not init_response.success:
168
+ return init_response
169
+
170
+ request = self._create_request("tools/list")
171
+ return await self._send_request(request)
172
+
173
+ async def call_tool(self, tool_name: str, arguments: Dict[str, Any] = None) -> MCPResponse:
174
+ """Call an MCP tool"""
175
+ if not self.initialized:
176
+ init_response = await self.initialize()
177
+ if not init_response.success:
178
+ return init_response
179
+
180
+ params = {"name": tool_name}
181
+ if arguments:
182
+ params["arguments"] = arguments
183
+
184
+ request = self._create_request("tools/call", params)
185
+ return await self._send_request(request)
186
+
187
+ async def query_challenges(self, **kwargs) -> MCPResponse:
188
+ """Query TopCoder challenges"""
189
+ params = {
190
+ "status": kwargs.get("status", "Completed"),
191
+ "perPage": kwargs.get("per_page", 5),
192
+ "page": kwargs.get("page", 1),
193
+ "sortBy": kwargs.get("sort_by", "startDate"),
194
+ "sortOrder": kwargs.get("sort_order", "desc")
195
+ }
196
+
197
+ for key, value in kwargs.items():
198
+ if key not in ["status", "per_page", "page", "sort_by", "sort_order"] and value is not None:
199
+ params[key] = value
200
+
201
+ return await self.call_tool("query-tc-challenges", params)
202
+
203
+
204
+ class TopCoderAIAgent:
205
+ """AI Agent for competitive programming assistance using MCP"""
206
+
207
+ def __init__(self, use_real_mcp: bool = True):
208
+ self.use_real_mcp = use_real_mcp
209
+ self.mcp_client = TopcoderMCPClient() if use_real_mcp else None
210
+ self.initialized = False
211
+
212
+ # Pattern recognition database
213
+ self.pattern_keywords = {
214
+ "Dynamic Programming": ["maximum", "minimum", "optimal", "best", "dp", "subproblem", "memoization"],
215
+ "Array Manipulation": ["array", "sequence", "subarray", "elements", "indices"],
216
+ "Graph Theory": ["graph", "tree", "node", "edge", "vertex", "path", "cycle"],
217
+ "String Processing": ["string", "substring", "pattern", "text", "character"],
218
+ "Sorting": ["sort", "order", "arrange", "sorted", "ascending", "descending"],
219
+ "Binary Search": ["search", "find", "binary", "sorted", "log", "divide"],
220
+ "Greedy": ["greedy", "optimal", "local", "choice", "maximize", "minimize"],
221
+ "Math": ["mathematical", "number", "prime", "factorial", "modulo", "gcd"]
222
+ }
223
+
224
+ async def initialize(self):
225
+ """Initialize the AI agent"""
226
+ if not self.initialized:
227
+ logger.info("πŸš€ Initializing TopCoder AI Agent...")
228
+
229
+ if self.use_real_mcp and self.mcp_client:
230
+ response = await self.mcp_client.initialize()
231
+ if not response.success:
232
+ logger.warning(f"MCP initialization failed: {response.error}")
233
+ logger.info("πŸ”„ Falling back to simulated mode...")
234
+ self.use_real_mcp = False
235
+
236
+ self.initialized = True
237
+ logger.info("βœ… AI Agent initialized")
238
+
239
+ async def analyze_problem_patterns(self, problem_statement: str) -> Dict[str, Any]:
240
+ """Analyze problem to identify algorithmic patterns"""
241
+ await self._ensure_initialized()
242
+
243
+ problem_lower = problem_statement.lower()
244
+ identified_patterns = []
245
+ confidence_scores = {}
246
+
247
+ # Pattern recognition using keyword matching
248
+ for pattern, keywords in self.pattern_keywords.items():
249
+ matches = sum(1 for keyword in keywords if keyword in problem_lower)
250
+ if matches > 0:
251
+ confidence = min(95, 60 + (matches * 10)) # Base 60% + 10% per keyword
252
+ identified_patterns.append(pattern)
253
+ confidence_scores[pattern] = confidence
254
+
255
+ # Get similar challenges from MCP if available
256
+ similar_challenges = []
257
+ if self.use_real_mcp and self.mcp_client:
258
+ try:
259
+ challenges_response = await self.mcp_client.query_challenges(
260
+ status="Completed",
261
+ per_page=3
262
+ )
263
+ if challenges_response.success:
264
+ similar_challenges = challenges_response.data or []
265
+ except Exception as e:
266
+ logger.warning(f"Failed to fetch similar challenges: {e}")
267
+
268
+ # Fallback: simulate similar challenges
269
+ if not similar_challenges:
270
+ similar_challenges = [
271
+ {
272
+ "name": "Dynamic Programming Challenge",
273
+ "id": "30154649",
274
+ "difficulty": "Hard",
275
+ "topics": ["Dynamic Programming", "Array Manipulation"]
276
+ },
277
+ {
278
+ "name": "Graph Theory Contest",
279
+ "id": "30154650",
280
+ "difficulty": "Medium",
281
+ "topics": ["Graph Theory", "BFS"]
282
+ }
283
+ ]
284
+
285
+ return {
286
+ "identified_patterns": identified_patterns,
287
+ "confidence_scores": confidence_scores,
288
+ "similar_challenges": similar_challenges,
289
+ "analysis_method": "Keyword-based pattern recognition with MCP data"
290
+ }
291
+
292
+ async def generate_solution_code(self, problem_statement: str, patterns: List[str], language: str) -> Dict[str, Any]:
293
+ """Generate optimized code solution"""
294
+ await self._ensure_initialized()
295
+
296
+ primary_pattern = patterns[0] if patterns else "Array Manipulation"
297
+
298
+ # Code templates for different patterns and languages
299
+ templates = {
300
+ "Dynamic Programming": {
301
+ "Python": '''def solve_problem(arr):
302
+ """
303
+ Dynamic Programming solution for maximum sum of non-adjacent elements
304
+ Time Complexity: O(n), Space Complexity: O(n)
305
+ """
306
+ n = len(arr)
307
+ if n == 0:
308
+ return 0
309
+ if n == 1:
310
+ return arr[0]
311
+
312
+ # dp[i] represents maximum sum up to index i
313
+ dp = [0] * n
314
+ dp[0] = max(0, arr[0]) # Can choose not to take first element
315
+ dp[1] = max(dp[0], arr[1])
316
+
317
+ for i in range(2, n):
318
+ dp[i] = max(dp[i-1], dp[i-2] + arr[i])
319
+
320
+ return dp[n-1]
321
+
322
+ # Test the solution
323
+ if __name__ == "__main__":
324
+ test_cases = [
325
+ [2, 1, 4, 5], # Expected: 6 (2+4)
326
+ [5, 1, 3, 2, 4], # Expected: 9 (5+3+1 or 5+4)
327
+ [1, 2, 3], # Expected: 4 (1+3)
328
+ ]
329
+
330
+ for i, test in enumerate(test_cases):
331
+ result = solve_problem(test)
332
+ print(f"Test {i+1}: {test} -> {result}")
333
+ ''',
334
+ "C++": '''#include <vector>
335
+ #include <algorithm>
336
+ #include <iostream>
337
+ using namespace std;
338
+
339
+ class Solution {
340
+ public:
341
+ int solveProblem(vector<int>& arr) {
342
+ /*
343
+ * Dynamic Programming solution for maximum sum of non-adjacent elements
344
+ * Time Complexity: O(n), Space Complexity: O(n)
345
+ */
346
+ int n = arr.size();
347
+ if (n == 0) return 0;
348
+ if (n == 1) return max(0, arr[0]);
349
+
350
+ vector<int> dp(n);
351
+ dp[0] = max(0, arr[0]);
352
+ dp[1] = max(dp[0], arr[1]);
353
+
354
+ for (int i = 2; i < n; i++) {
355
+ dp[i] = max(dp[i-1], dp[i-2] + arr[i]);
356
+ }
357
+
358
+ return dp[n-1];
359
+ }
360
+ };
361
+
362
+ int main() {
363
+ Solution sol;
364
+ vector<vector<int>> testCases = {
365
+ {2, 1, 4, 5}, // Expected: 6
366
+ {5, 1, 3, 2, 4}, // Expected: 9
367
+ {1, 2, 3} // Expected: 4
368
+ };
369
+
370
+ for (int i = 0; i < testCases.size(); i++) {
371
+ int result = sol.solveProblem(testCases[i]);
372
+ cout << "Test " << (i+1) << ": Result = " << result << endl;
373
+ }
374
+
375
+ return 0;
376
+ }'''
377
+ },
378
+ "Array Manipulation": {
379
+ "Python": '''def solve_problem(arr):
380
+ """
381
+ Array manipulation solution using Kadane's algorithm
382
+ Time Complexity: O(n), Space Complexity: O(1)
383
+ """
384
+ if not arr:
385
+ return 0
386
+
387
+ max_sum = float('-inf')
388
+ current_sum = 0
389
+
390
+ for num in arr:
391
+ current_sum = max(num, current_sum + num)
392
+ max_sum = max(max_sum, current_sum)
393
+
394
+ return max_sum
395
+
396
+ # Test cases
397
+ test_cases = [
398
+ [-2, 1, -3, 4, -1, 2, 1, -5, 4], # Expected: 6
399
+ [1, 2, 3, 4, 5], # Expected: 15
400
+ [-1, -2, -3] # Expected: -1
401
+ ]
402
+
403
+ for i, test in enumerate(test_cases):
404
+ result = solve_problem(test)
405
+ print(f"Test {i+1}: {test} -> {result}")
406
+ ''',
407
+ "C++": '''#include <vector>
408
+ #include <algorithm>
409
+ #include <iostream>
410
+ #include <climits>
411
+ using namespace std;
412
+
413
+ class Solution {
414
+ public:
415
+ int solveProblem(vector<int>& arr) {
416
+ if (arr.empty()) return 0;
417
+
418
+ int maxSum = INT_MIN;
419
+ int currentSum = 0;
420
+
421
+ for (int num : arr) {
422
+ currentSum = max(num, currentSum + num);
423
+ maxSum = max(maxSum, currentSum);
424
+ }
425
+
426
+ return maxSum;
427
+ }
428
+ };'''
429
+ }
430
+ }
431
+
432
+ # Get appropriate template
433
+ lang_key = "Python" if language.lower() == "python" else "C++"
434
+ pattern_templates = templates.get(primary_pattern, templates["Array Manipulation"])
435
+ code = pattern_templates.get(lang_key, pattern_templates["Python"])
436
+
437
+ # Complexity analysis
438
+ complexity_info = {
439
+ "Dynamic Programming": {"time": "O(n)", "space": "O(n)", "explanation": "Single pass with memoization"},
440
+ "Array Manipulation": {"time": "O(n)", "space": "O(1)", "explanation": "Single pass with constant space"},
441
+ "Graph Theory": {"time": "O(V + E)", "space": "O(V)", "explanation": "Graph traversal complexity"},
442
+ "String Processing": {"time": "O(n*m)", "space": "O(n)", "explanation": "String matching complexity"}
443
+ }
444
+
445
+ complexity = complexity_info.get(primary_pattern, complexity_info["Array Manipulation"])
446
+
447
+ return {
448
+ "generated_code": code,
449
+ "language": language,
450
+ "primary_pattern": primary_pattern,
451
+ "complexity_analysis": {
452
+ "time_complexity": complexity["time"],
453
+ "space_complexity": complexity["space"],
454
+ "explanation": complexity["explanation"]
455
+ },
456
+ "optimization_suggestions": [
457
+ f"Space optimization: Consider iterative approach for {primary_pattern}",
458
+ "Edge cases: Add comprehensive input validation",
459
+ "Performance: Consider compiler optimizations for C++",
460
+ "Testing: Add comprehensive test cases for corner cases"
461
+ ]
462
+ }
463
+
464
+ async def get_performance_optimization(self, code: str, pattern: str) -> Dict[str, Any]:
465
+ """Analyze code for performance bottlenecks and optimizations"""
466
+ bottlenecks = []
467
+ optimizations = []
468
+
469
+ # Analyze based on pattern
470
+ if "Dynamic Programming" in pattern:
471
+ bottlenecks.append("Space usage for DP table")
472
+ optimizations.extend([
473
+ "Use space-optimized DP with O(1) space if possible",
474
+ "Consider bottom-up approach to avoid recursion overhead",
475
+ "Implement iterative solution to prevent stack overflow"
476
+ ])
477
+
478
+ if "for" in code.lower() or "while" in code.lower():
479
+ bottlenecks.append("Nested loops may increase time complexity")
480
+ optimizations.append("Consider early termination conditions")
481
+
482
+ if "vector" in code or "list" in code:
483
+ optimizations.append("Pre-allocate data structures when size is known")
484
+
485
+ return {
486
+ "identified_bottlenecks": bottlenecks,
487
+ "optimization_recommendations": optimizations,
488
+ "performance_score": 85, # Simulated score
489
+ "memory_efficiency": "Good" if "O(1)" in code else "Moderate"
490
+ }
491
+
492
+ async def get_learning_path(self, user_level: str, identified_patterns: List[str]) -> Dict[str, Any]:
493
+ """Generate personalized learning recommendations"""
494
+
495
+ learning_paths = {
496
+ "beginner": {
497
+ "priority_topics": [
498
+ "Array and String Basics",
499
+ "Simple Loops and Conditions",
500
+ "Basic Sorting Algorithms"
501
+ ],
502
+ "study_plan": [
503
+ "Week 1-2: Master array operations and string manipulation",
504
+ "Week 3-4: Learn basic sorting (bubble, selection, insertion)",
505
+ "Week 5-6: Introduction to time complexity analysis"
506
+ ],
507
+ "practice_problems": [
508
+ "Two Sum", "Reverse Array", "Find Maximum Element"
509
+ ]
510
+ },
511
+ "intermediate": {
512
+ "priority_topics": [
513
+ "Dynamic Programming Fundamentals",
514
+ "Graph Algorithms (BFS/DFS)",
515
+ "Advanced Data Structures (Trees, Heaps)"
516
+ ],
517
+ "study_plan": [
518
+ "Week 1-2: Master classic DP problems (knapsack, LCS)",
519
+ "Week 3-4: Graph traversal and shortest path algorithms",
520
+ "Week 5-6: Tree algorithms and heap operations"
521
+ ],
522
+ "practice_problems": [
523
+ "Longest Common Subsequence", "Binary Tree Traversal", "Dijkstra's Algorithm"
524
+ ]
525
+ },
526
+ "advanced": {
527
+ "priority_topics": [
528
+ "Advanced Graph Algorithms",
529
+ "Segment Trees and Fenwick Trees",
530
+ "Network Flow and String Algorithms"
531
+ ],
532
+ "study_plan": [
533
+ "Week 1-2: Advanced graph algorithms (MST, network flow)",
534
+ "Week 3-4: Range query data structures",
535
+ "Week 5-6: Advanced string processing (KMP, Z-algorithm)"
536
+ ],
537
+ "practice_problems": [
538
+ "Maximum Flow", "Range Sum Queries", "String Matching"
539
+ ]
540
+ }
541
+ }
542
+
543
+ base_path = learning_paths.get(user_level, learning_paths["intermediate"])
544
+
545
+ # Customize based on identified patterns
546
+ if identified_patterns:
547
+ pattern_focus = {
548
+ "Dynamic Programming": "Focus on DP variations and optimization techniques",
549
+ "Graph Theory": "Emphasize graph traversal and shortest path algorithms",
550
+ "String Processing": "Study pattern matching and string manipulation",
551
+ "Array Manipulation": "Master sliding window and two-pointer techniques"
552
+ }
553
+
554
+ custom_suggestions = []
555
+ for pattern in identified_patterns[:3]: # Top 3 patterns
556
+ if pattern in pattern_focus:
557
+ custom_suggestions.append(pattern_focus[pattern])
558
+
559
+ base_path["custom_focus"] = custom_suggestions
560
+
561
+ return base_path
562
+
563
+ async def process_complete_request(self, problem_statement: str, difficulty: str,
564
+ language: str, skill_level: str) -> Dict[str, Any]:
565
+ """Process complete competitive programming request"""
566
+ start_time = time.time()
567
+
568
+ try:
569
+ # Step 1: Pattern Analysis
570
+ pattern_analysis = await self.analyze_problem_patterns(problem_statement)
571
+
572
+ # Step 2: Code Generation
573
+ identified_patterns = pattern_analysis.get("identified_patterns", [])
574
+ solution = await self.generate_solution_code(problem_statement, identified_patterns, language)
575
+
576
+ # Step 3: Performance Analysis
577
+ performance = await self.get_performance_optimization(
578
+ solution.get("generated_code", ""),
579
+ solution.get("primary_pattern", "")
580
+ )
581
+
582
+ # Step 4: Learning Recommendations
583
+ learning_path = await self.get_learning_path(skill_level, identified_patterns)
584
+
585
+ processing_time = round(time.time() - start_time, 2)
586
+
587
+ return {
588
+ "status": "success",
589
+ "processing_time": processing_time,
590
+ "pattern_analysis": pattern_analysis,
591
+ "solution": solution,
592
+ "performance": performance,
593
+ "learning_path": learning_path,
594
+ "agent_confidence": self._calculate_confidence(pattern_analysis, solution),
595
+ "mcp_integration": "active" if self.use_real_mcp else "simulated"
596
+ }
597
+
598
+ except Exception as e:
599
+ logger.error(f"Error processing request: {e}")
600
+ return {
601
+ "status": "error",
602
+ "error_message": str(e),
603
+ "processing_time": round(time.time() - start_time, 2)
604
+ }
605
+
606
+ def _calculate_confidence(self, pattern_analysis: Dict, solution: Dict) -> float:
607
+ """Calculate agent confidence score"""
608
+ base_confidence = 75.0
609
+
610
+ # Boost confidence if patterns were identified
611
+ patterns = pattern_analysis.get("identified_patterns", [])
612
+ if patterns:
613
+ base_confidence += len(patterns) * 5
614
+
615
+ # Boost confidence if high confidence scores
616
+ confidence_scores = pattern_analysis.get("confidence_scores", {})
617
+ if confidence_scores:
618
+ avg_confidence = sum(confidence_scores.values()) / len(confidence_scores)
619
+ base_confidence += (avg_confidence - 70) * 0.3
620
+
621
+ return min(95.0, max(60.0, base_confidence))
622
+
623
+ async def _ensure_initialized(self):
624
+ """Ensure agent is initialized"""
625
+ if not self.initialized:
626
+ await self.initialize()
627
+
628
+
629
+ # Gradio Interface Implementation
630
+ class GradioInterface:
631
+ """Gradio web interface for the TopCoder AI Agent"""
632
+
633
+ def __init__(self):
634
+ self.agent = TopCoderAIAgent(use_real_mcp=True)
635
+ self.interface_initialized = False
636
+
637
+ async def initialize(self):
638
+ """Initialize the interface and agent"""
639
+ if not self.interface_initialized:
640
+ await self.agent.initialize()
641
+ self.interface_initialized = True
642
+ logger.info("βœ… Gradio interface initialized")
643
+
644
+ def process_problem_request(self, problem_statement: str, difficulty: str,
645
+ language: str, skill_level: str) -> Tuple[str, str, str, str]:
646
+ """Main processing function for Gradio interface"""
647
+ try:
648
+ # Ensure event loop exists
649
+ try:
650
+ loop = asyncio.get_event_loop()
651
+ except RuntimeError:
652
+ loop = asyncio.new_event_loop()
653
+ asyncio.set_event_loop(loop)
654
+
655
+ # Initialize if needed
656
+ if not self.interface_initialized:
657
+ loop.run_until_complete(self.initialize())
658
+
659
+ # Process the request
660
+ result = loop.run_until_complete(
661
+ self.agent.process_complete_request(
662
+ problem_statement, difficulty, language, skill_level
663
+ )
664
+ )
665
+
666
+ return self._format_response(result)
667
+
668
+ except Exception as e:
669
+ logger.error(f"Error in Gradio processing: {e}")
670
+ return self._format_error_response(str(e))
671
+
672
+ def _format_response(self, result: Dict[str, Any]) -> Tuple[str, str, str, str]:
673
+ """Format successful response for Gradio display"""
674
+
675
+ if result.get("status") == "error":
676
+ return self._format_error_response(result.get("error_message", "Unknown error"))
677
+
678
+ # Format Pattern Analysis
679
+ pattern_analysis = result.get("pattern_analysis", {})
680
+ patterns = pattern_analysis.get("identified_patterns", [])
681
+ confidence_scores = pattern_analysis.get("confidence_scores", {})
682
+
683
+ pattern_text = "🎯 **Problem Analysis Results**\n\n"
684
+ pattern_text += f"⚑ Processing Time: {result.get('processing_time', 0)}s\n"
685
+ pattern_text += f"πŸ€– Agent Confidence: {result.get('agent_confidence', 0):.1f}%\n"
686
+ pattern_text += f"πŸ”— MCP Status: {result.get('mcp_integration', 'unknown').title()}\n\n"
687
+
688
+ if patterns:
689
+ pattern_text += "**πŸ” Identified Algorithmic Patterns:**\n"
690
+ for pattern in patterns:
691
+ confidence = confidence_scores.get(pattern, 0)
692
+ pattern_text += f"β€’ {pattern}: {confidence}% confidence\n"
693
+ else:
694
+ pattern_text += "**πŸ” No specific patterns identified**\n"
695
+
696
+ similar_challenges = pattern_analysis.get("similar_challenges", [])
697
+ if similar_challenges:
698
+ pattern_text += "\n**πŸ“š Similar Challenges:**\n"
699
+ for challenge in similar_challenges[:3]:
700
+ name = challenge.get('name', 'Unknown')
701
+ challenge_id = challenge.get('id', '')
702
+ pattern_text += f"β€’ {name} (ID: {challenge_id})\n"
703
+
704
+ # Format Solution Code
705
+ solution = result.get("solution", {})
706
+ code_text = f"**πŸ’» Generated Solution ({solution.get('language', 'Unknown')})**\n\n"
707
+ code_text += f"```{solution.get('language', 'python').lower()}\n"
708
+ code_text += solution.get('generated_code', 'No code generated')
709
+ code_text += "\n```\n\n"
710
+
711
+ complexity = solution.get('complexity_analysis', {})
712
+ code_text += "**⚑ Complexity Analysis:**\n"
713
+ code_text += f"β€’ Time Complexity: {complexity.get('time_complexity', 'N/A')}\n"
714
+ code_text += f"β€’ Space Complexity: {complexity.get('space_complexity', 'N/A')}\n"
715
+ code_text += f"β€’ Explanation: {complexity.get('explanation', 'N/A')}\n"
716
+
717
+ # Format Performance Optimization
718
+ performance = result.get("performance", {})
719
+ optimization_text = "**πŸš€ Performance Analysis**\n\n"
720
+
721
+ bottlenecks = performance.get("identified_bottlenecks", [])
722
+ if bottlenecks:
723
+ optimization_text += "**⚠️ Potential Bottlenecks:**\n"
724
+ for bottleneck in bottlenecks:
725
+ optimization_text += f"β€’ {bottleneck}\n"
726
+ optimization_text += "\n"
727
+
728
+ optimizations = performance.get("optimization_recommendations", [])
729
+ if optimizations:
730
+ optimization_text += "**✨ Optimization Recommendations:**\n"
731
+ for opt in optimizations:
732
+ optimization_text += f"β€’ {opt}\n"
733
+ optimization_text += "\n"
734
+
735
+ perf_score = performance.get("performance_score", 0)
736
+ memory_eff = performance.get("memory_efficiency", "Unknown")
737
+ optimization_text += f"**πŸ“Š Performance Score:** {perf_score}/100\n"
738
+ optimization_text += f"**πŸ’Ύ Memory Efficiency:** {memory_eff}\n"
739
+
740
+ # Format Learning Path
741
+ learning_path = result.get("learning_path", {})
742
+ learning_text = "**πŸŽ“ Personalized Learning Path**\n\n"
743
+
744
+ priority_topics = learning_path.get("priority_topics", [])
745
+ if priority_topics:
746
+ learning_text += "**🎯 Priority Topics:**\n"
747
+ for topic in priority_topics:
748
+ learning_text += f"β€’ {topic}\n"
749
+ learning_text += "\n"
750
+
751
+ study_plan = learning_path.get("study_plan", [])
752
+ if study_plan:
753
+ learning_text += "**πŸ“… Recommended Study Plan:**\n"
754
+ for plan_item in study_plan:
755
+ learning_text += f"β€’ {plan_item}\n"
756
+ learning_text += "\n"
757
+
758
+ practice_problems = learning_path.get("practice_problems", [])
759
+ if practice_problems:
760
+ learning_text += "**πŸ‹οΈ Practice Problems:**\n"
761
+ for problem in practice_problems:
762
+ learning_text += f"β€’ {problem}\n"
763
+ learning_text += "\n"
764
+
765
+ custom_focus = learning_path.get("custom_focus", [])
766
+ if custom_focus:
767
+ learning_text += "**🎯 Custom Focus Areas:**\n"
768
+ for focus in custom_focus:
769
+ learning_text += f"β€’ {focus}\n"
770
+
771
+ return (pattern_text, code_text, optimization_text, learning_text)
772
+
773
+ def _format_error_response(self, error_message: str) -> Tuple[str, str, str, str]:
774
+ """Format error response for Gradio display"""
775
+ error_text = f"❌ **Error Processing Request**\n\n{error_message}\n\n"
776
+ error_text += "πŸ”§ **Troubleshooting Tips:**\n"
777
+ error_text += "β€’ Check your internet connection\n"
778
+ error_text += "β€’ Ensure the problem statement is clear and detailed\n"
779
+ error_text += "β€’ Try with a different difficulty level or language\n"
780
+ error_text += "β€’ The system may be experiencing high load - please try again\n"
781
+
782
+ return (error_text, "", "", "")
783
+
784
+
785
+ def create_gradio_interface():
786
+ """Create and configure the Gradio interface"""
787
+
788
+ interface = GradioInterface()
789
+
790
+ # Custom CSS for better styling
791
+ custom_css = """
792
+ .gradio-container {
793
+ max-width: 1200px !important;
794
+ margin: auto !important;
795
+ }
796
+ .output-markdown {
797
+ font-size: 14px;
798
+ line-height: 1.6;
799
+ }
800
+ .input-group {
801
+ margin-bottom: 1rem;
802
+ }
803
+ """
804
+
805
+ # Create Gradio interface
806
+ with gr.Blocks(
807
+ title="πŸ† TopCoder Elite AI Mentor - Championship Edition",
808
+ theme=gr.themes.Soft(),
809
+ css=custom_css
810
+ ) as demo:
811
+
812
+ gr.Markdown("""
813
+ # πŸ† TopCoder Elite AI Mentor - Championship Edition
814
+
815
+ **Revolutionary Multi-Agent Competitive Programming Assistant**
816
+
817
+ Powered by Model Context Protocol (MCP) integration with TopCoder's live data streams.
818
+ Get intelligent problem analysis, optimized code generation, and personalized learning recommendations.
819
+ """)
820
+
821
+ with gr.Row():
822
+ with gr.Column(scale=1):
823
+ gr.Markdown("### πŸ“ Problem Input")
824
+
825
+ problem_input = gr.Textbox(
826
+ label="Problem Statement",
827
+ placeholder="Paste your competitive programming problem here...",
828
+ lines=8,
829
+ max_lines=15
830
+ )
831
+
832
+ with gr.Row():
833
+ difficulty = gr.Dropdown(
834
+ label="Difficulty Level",
835
+ choices=["Easy", "Medium", "Hard", "Expert"],
836
+ value="Medium"
837
+ )
838
+
839
+ language = gr.Dropdown(
840
+ label="Preferred Language",
841
+ choices=["Python", "C++", "Java", "JavaScript"],
842
+ value="Python"
843
+ )
844
+
845
+ skill_level = gr.Dropdown(
846
+ label="Your Skill Level",
847
+ choices=["beginner", "intermediate", "advanced"],
848
+ value="intermediate"
849
+ )
850
+
851
+ analyze_btn = gr.Button(
852
+ "πŸš€ Launch Championship Analysis",
853
+ variant="primary",
854
+ size="lg"
855
+ )
856
+
857
+ gr.Markdown("---")
858
+
859
+ with gr.Row():
860
+ with gr.Column():
861
+ pattern_analysis_output = gr.Markdown(
862
+ label="🎯 Pattern Analysis",
863
+ value="Ready to analyze your problem..."
864
+ )
865
+
866
+ with gr.Column():
867
+ code_output = gr.Markdown(
868
+ label="πŸ’» Generated Solution",
869
+ value="Code will appear here after analysis..."
870
+ )
871
+
872
+ with gr.Row():
873
+ with gr.Column():
874
+ optimization_output = gr.Markdown(
875
+ label="πŸš€ Performance Optimization",
876
+ value="Optimization suggestions will appear here..."
877
+ )
878
+
879
+ with gr.Column():
880
+ learning_output = gr.Markdown(
881
+ label="πŸŽ“ Learning Recommendations",
882
+ value="Personalized learning path will appear here..."
883
+ )
884
+
885
+ # Connect the button to the processing function
886
+ analyze_btn.click(
887
+ fn=interface.process_problem_request,
888
+ inputs=[problem_input, difficulty, language, skill_level],
889
+ outputs=[pattern_analysis_output, code_output, optimization_output, learning_output],
890
+ show_progress=True
891
+ )
892
+
893
+ gr.Markdown("""
894
+ ---
895
+ ### πŸ”— MCP Integration Status
896
+
897
+ This application connects to TopCoder's Model Context Protocol (MCP) server to provide:
898
+ - Real-time challenge data and statistics
899
+ - Similar problem recommendations
900
+ - Live performance benchmarking
901
+ - Community insights and trends
902
+
903
+ **Built for the TopCoder Learn AI Challenge - Aiming for 1st Place! πŸ₯‡**
904
+ """)
905
+
906
+ return demo
907
+
908
+
909
+ # Main application entry point
910
+ def main():
911
+ """Main application entry point"""
912
+ logger.info("πŸš€ Starting TopCoder Elite AI Mentor...")
913
+
914
+ try:
915
+ # Create and launch Gradio interface
916
+ demo = create_gradio_interface()
917
+
918
+ # Launch with appropriate settings for different environments
919
+ demo.launch(
920
+ server_name="0.0.0.0", # Allow external access
921
+ server_port=7860, # Standard port for Hugging Face Spaces
922
+ share=False, # Set to True for temporary public links
923
+ show_error=True, # Show detailed error messages
924
+ quiet=False, # Show launch logs
925
+ max_threads=10 # Limit concurrent users
926
+ )
927
+
928
+ except Exception as e:
929
+ logger.error(f"Failed to start application: {e}")
930
+ raise
931
+
932
+
933
+ if __name__ == "__main__":
934
+ main()
mcp_integration_example.py ADDED
@@ -0,0 +1,667 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ MCP Integration Example for TopCoder AI Agent
3
+ This module shows how to integrate the MCP client with your AI agent application.
4
+ """
5
+
6
+ import asyncio
7
+ import logging
8
+ from typing import List, Dict, Any, Optional
9
+ from dataclasses import dataclass
10
+
11
+ # Import the MCP client (assuming it's in the same directory)
12
+ # from topcoder_mcp_client import TopcoderMCPClient, MCPResponse, create_mcp_client
13
+
14
+ # For demo purposes, we'll include a simplified version here
15
+ class SimpleMCPClient:
16
+ """Simplified MCP client for demo purposes"""
17
+
18
+ def __init__(self):
19
+ self.initialized = False
20
+ self.available_tools = [
21
+ {
22
+ "name": "query-tc-challenges",
23
+ "description": "Query TopCoder challenges with filters",
24
+ "parameters": {
25
+ "status": "Challenge status (Active, Completed, etc.)",
26
+ "track": "Challenge track (Algorithm, Development, etc.)",
27
+ "perPage": "Number of results per page",
28
+ "page": "Page number"
29
+ }
30
+ },
31
+ {
32
+ "name": "get-tc-challenge",
33
+ "description": "Get detailed information about a specific challenge",
34
+ "parameters": {
35
+ "id": "Challenge ID"
36
+ }
37
+ },
38
+ {
39
+ "name": "search-tc-members",
40
+ "description": "Search TopCoder members",
41
+ "parameters": {
42
+ "query": "Search query",
43
+ "limit": "Maximum number of results"
44
+ }
45
+ }
46
+ ]
47
+
48
+ async def initialize(self):
49
+ """Initialize the MCP connection"""
50
+ # In real implementation, this would connect to the actual MCP server
51
+ self.initialized = True
52
+ return {"success": True, "message": "MCP client initialized"}
53
+
54
+ async def list_tools(self):
55
+ """List available MCP tools"""
56
+ if not self.initialized:
57
+ await self.initialize()
58
+
59
+ return {
60
+ "success": True,
61
+ "tools": self.available_tools
62
+ }
63
+
64
+ async def call_tool(self, tool_name: str, arguments: Dict[str, Any]):
65
+ """Call an MCP tool with arguments"""
66
+ if not self.initialized:
67
+ await self.initialize()
68
+
69
+ # Simulate MCP tool calls with realistic data
70
+ if tool_name == "query-tc-challenges":
71
+ return {
72
+ "success": True,
73
+ "data": [
74
+ {
75
+ "id": "30154649",
76
+ "name": "SRM 850 - Algorithm Challenge",
77
+ "status": "Completed",
78
+ "track": "Algorithm",
79
+ "startDate": "2024-01-15T00:00:00Z",
80
+ "endDate": "2024-01-15T23:59:59Z",
81
+ "prizeMoney": 5000,
82
+ "difficulty": "Hard",
83
+ "technologies": ["Algorithm", "Dynamic Programming", "Graph Theory"],
84
+ "registrants": 156,
85
+ "submissions": 89
86
+ },
87
+ {
88
+ "id": "30154650",
89
+ "name": "F2F Development Challenge",
90
+ "status": "Completed",
91
+ "track": "Development",
92
+ "startDate": "2024-01-10T00:00:00Z",
93
+ "endDate": "2024-01-20T23:59:59Z",
94
+ "prizeMoney": 15000,
95
+ "difficulty": "Medium",
96
+ "technologies": ["React", "Node.js", "PostgreSQL"],
97
+ "registrants": 45,
98
+ "submissions": 23
99
+ }
100
+ ]
101
+ }
102
+
103
+ elif tool_name == "get-tc-challenge":
104
+ challenge_id = arguments.get("id", "30154649")
105
+ return {
106
+ "success": True,
107
+ "data": {
108
+ "id": challenge_id,
109
+ "name": "SRM 850 - Algorithm Challenge",
110
+ "description": "Solve three algorithmic problems of increasing difficulty within 75 minutes.",
111
+ "requirements": [
112
+ "Implement efficient algorithms for array manipulation",
113
+ "Handle edge cases and optimize for time complexity",
114
+ "Provide clean, readable code with proper variable names"
115
+ ],
116
+ "constraints": {
117
+ "timeLimit": "2 seconds per test case",
118
+ "memoryLimit": "256 MB",
119
+ "languages": ["C++", "Java", "Python", "JavaScript"]
120
+ },
121
+ "problemStatement": "Given an array of integers, find the maximum sum of non-adjacent elements...",
122
+ "examples": [
123
+ {
124
+ "input": "[2, 1, 4, 5]",
125
+ "output": "6",
126
+ "explanation": "Select 2 and 4 (non-adjacent) for maximum sum of 6"
127
+ }
128
+ ],
129
+ "algorithmicPatterns": ["Dynamic Programming", "Array Manipulation"],
130
+ "difficulty": "Hard",
131
+ "estimatedSolveTime": "45 minutes"
132
+ }
133
+ }
134
+
135
+ elif tool_name == "search-tc-members":
136
+ return {
137
+ "success": True,
138
+ "data": [
139
+ {
140
+ "handle": "tourist",
141
+ "rating": 3500,
142
+ "rank": "Target",
143
+ "country": "Belarus",
144
+ "wins": 45,
145
+ "challenges": 234
146
+ },
147
+ {
148
+ "handle": "Petr",
149
+ "rating": 3400,
150
+ "rank": "Target",
151
+ "country": "Russia",
152
+ "wins": 38,
153
+ "challenges": 198
154
+ }
155
+ ]
156
+ }
157
+
158
+ else:
159
+ return {
160
+ "success": False,
161
+ "error": f"Unknown tool: {tool_name}"
162
+ }
163
+
164
+
165
+ @dataclass
166
+ class AIAgentContext:
167
+ """Context for AI agent operations"""
168
+ problem_statement: str
169
+ difficulty_level: str
170
+ preferred_language: str
171
+ user_skill_level: str = "intermediate"
172
+ focus_areas: List[str] = None
173
+
174
+
175
+ class TopCoderAIAgent:
176
+ """AI Agent that uses MCP to provide competitive programming assistance"""
177
+
178
+ def __init__(self):
179
+ self.mcp_client = SimpleMCPClient() # In real app: TopcoderMCPClient()
180
+ self.initialized = False
181
+
182
+ async def initialize(self):
183
+ """Initialize the AI agent and MCP connection"""
184
+ if not self.initialized:
185
+ logging.info("πŸš€ Initializing TopCoder AI Agent...")
186
+ result = await self.mcp_client.initialize()
187
+ if result.get("success"):
188
+ self.initialized = True
189
+ logging.info("βœ… AI Agent initialized successfully")
190
+ else:
191
+ raise RuntimeError("Failed to initialize MCP client")
192
+
193
+ async def analyze_problem_patterns(self, problem_statement: str) -> Dict[str, Any]:
194
+ """Analyze problem statement to identify algorithmic patterns"""
195
+ # This would use the MCP to get similar problems and patterns
196
+ await self.ensure_initialized()
197
+
198
+ # Simulate pattern analysis using MCP data
199
+ challenges_response = await self.mcp_client.call_tool(
200
+ "query-tc-challenges",
201
+ {"status": "Completed", "track": "Algorithm", "perPage": 5}
202
+ )
203
+
204
+ if not challenges_response.get("success"):
205
+ return {"error": "Failed to query challenges for pattern analysis"}
206
+
207
+ # Analyze patterns based on problem keywords
208
+ patterns = []
209
+ confidence_scores = {}
210
+
211
+ problem_lower = problem_statement.lower()
212
+
213
+ # Pattern recognition logic
214
+ if any(keyword in problem_lower for keyword in ["maximum", "minimum", "optimal", "best"]):
215
+ patterns.append("Dynamic Programming")
216
+ confidence_scores["Dynamic Programming"] = 85
217
+
218
+ if any(keyword in problem_lower for keyword in ["array", "sequence", "subarray"]):
219
+ patterns.append("Array Manipulation")
220
+ confidence_scores["Array Manipulation"] = 90
221
+
222
+ if any(keyword in problem_lower for keyword in ["graph", "tree", "node", "edge"]):
223
+ patterns.append("Graph Theory")
224
+ confidence_scores["Graph Theory"] = 80
225
+
226
+ if any(keyword in problem_lower for keyword in ["string", "substring", "pattern"]):
227
+ patterns.append("String Processing")
228
+ confidence_scores["String Processing"] = 75
229
+
230
+ if any(keyword in problem_lower for keyword in ["sort", "order", "arrange"]):
231
+ patterns.append("Sorting")
232
+ confidence_scores["Sorting"] = 70
233
+
234
+ return {
235
+ "identified_patterns": patterns,
236
+ "confidence_scores": confidence_scores,
237
+ "similar_challenges": challenges_response.get("data", [])[:3],
238
+ "analysis_method": "Keyword-based pattern recognition with MCP data"
239
+ }
240
+
241
+ async def generate_solution_code(self, context: AIAgentContext, patterns: List[str]) -> Dict[str, Any]:
242
+ """Generate code solution based on problem analysis"""
243
+ await self.ensure_initialized()
244
+
245
+ # This would use MCP to get similar problem solutions
246
+ # For demo, we'll generate based on identified patterns
247
+
248
+ code_templates = {
249
+ "Dynamic Programming": {
250
+ "python": """
251
+ def solve_dp_problem(arr):
252
+ n = len(arr)
253
+ if n == 0:
254
+ return 0
255
+ if n == 1:
256
+ return arr[0]
257
+
258
+ # dp[i] represents maximum sum up to index i
259
+ dp = [0] * n
260
+ dp[0] = arr[0]
261
+ dp[1] = max(arr[0], arr[1])
262
+
263
+ for i in range(2, n):
264
+ dp[i] = max(dp[i-1], dp[i-2] + arr[i])
265
+
266
+ return dp[n-1]
267
+ """,
268
+ "cpp": """
269
+ #include <vector>
270
+ #include <algorithm>
271
+ using namespace std;
272
+
273
+ int solveDPProblem(vector<int>& arr) {
274
+ int n = arr.size();
275
+ if (n == 0) return 0;
276
+ if (n == 1) return arr[0];
277
+
278
+ vector<int> dp(n);
279
+ dp[0] = arr[0];
280
+ dp[1] = max(arr[0], arr[1]);
281
+
282
+ for (int i = 2; i < n; i++) {
283
+ dp[i] = max(dp[i-1], dp[i-2] + arr[i]);
284
+ }
285
+
286
+ return dp[n-1];
287
+ }
288
+ """
289
+ },
290
+ "Array Manipulation": {
291
+ "python": """
292
+ def solve_array_problem(arr):
293
+ n = len(arr)
294
+ max_sum = float('-inf')
295
+ current_sum = 0
296
+
297
+ for i in range(n):
298
+ current_sum = max(arr[i], current_sum + arr[i])
299
+ max_sum = max(max_sum, current_sum)
300
+
301
+ return max_sum
302
+ """,
303
+ "cpp": """
304
+ #include <vector>
305
+ #include <algorithm>
306
+ using namespace std;
307
+
308
+ int solveArrayProblem(vector<int>& arr) {
309
+ int n = arr.size();
310
+ int maxSum = INT_MIN;
311
+ int currentSum = 0;
312
+
313
+ for (int i = 0; i < n; i++) {
314
+ currentSum = max(arr[i], currentSum + arr[i]);
315
+ maxSum = max(maxSum, currentSum);
316
+ }
317
+
318
+ return maxSum;
319
+ }
320
+ """
321
+ }
322
+ }
323
+
324
+ # Select appropriate template based on primary pattern
325
+ primary_pattern = patterns[0] if patterns else "Array Manipulation"
326
+ template = code_templates.get(primary_pattern, code_templates["Array Manipulation"])
327
+
328
+ language_key = "python" if context.preferred_language.lower() == "python" else "cpp"
329
+ code = template.get(language_key, template["python"])
330
+
331
+ return {
332
+ "generated_code": code,
333
+ "language": context.preferred_language,
334
+ "primary_pattern": primary_pattern,
335
+ "complexity_analysis": {
336
+ "time_complexity": "O(n)",
337
+ "space_complexity": "O(n)" if primary_pattern == "Dynamic Programming" else "O(1)",
338
+ "explanation": f"Solution uses {primary_pattern.lower()} approach for efficient computation"
339
+ },
340
+ "optimization_suggestions": [
341
+ f"Consider space optimization for {primary_pattern}",
342
+ "Add input validation and edge case handling",
343
+ "Consider iterative vs recursive approaches"
344
+ ]
345
+ }
346
+
347
+ async def get_learning_recommendations(self, context: AIAgentContext, performance_data: Dict[str, Any]) -> Dict[str, Any]:
348
+ """Generate personalized learning recommendations"""
349
+ await self.ensure_initialized()
350
+
351
+ # Use MCP to get member statistics and challenges
352
+ member_data = await self.mcp_client.call_tool(
353
+ "search-tc-members",
354
+ {"query": "top_performers", "limit": 5}
355
+ )
356
+
357
+ recommendations = {
358
+ "priority_topics": [],
359
+ "practice_challenges": [],
360
+ "study_plan": [],
361
+ "skill_gaps": []
362
+ }
363
+
364
+ # Analyze skill level and recommend accordingly
365
+ if context.user_skill_level == "beginner":
366
+ recommendations["priority_topics"] = [
367
+ "Array and String Manipulation",
368
+ "Basic Sorting and Searching",
369
+ "Simple Dynamic Programming"
370
+ ]
371
+ recommendations["study_plan"] = [
372
+ "Week 1-2: Master basic array operations",
373
+ "Week 3-4: Learn sorting algorithms",
374
+ "Week 5-6: Introduction to DP concepts"
375
+ ]
376
+ elif context.user_skill_level == "intermediate":
377
+ recommendations["priority_topics"] = [
378
+ "Advanced Dynamic Programming",
379
+ "Graph Algorithms (BFS/DFS)",
380
+ "Tree Traversal and Manipulation"
381
+ ]
382
+ recommendations["study_plan"] = [
383
+ "Week 1-2: Complex DP patterns",
384
+ "Week 3-4: Graph theory fundamentals",
385
+ "Week 5-6: Tree algorithms and structures"
386
+ ]
387
+ else: # advanced
388
+ recommendations["priority_topics"] = [
389
+ "Advanced Graph Algorithms",
390
+ "Segment Trees and Fenwick Trees",
391
+ "Network Flow and Matching"
392
+ ]
393
+
394
+ # Add practice challenges based on MCP data
395
+ if member_data.get("success"):
396
+ challenges = member_data.get("data", [])
397
+ recommendations["practice_challenges"] = [
398
+ f"Study solutions from top performer: {member['handle']}"
399
+ for member in challenges[:3]
400
+ ]
401
+
402
+ return recommendations
403
+
404
+ async def ensure_initialized(self):
405
+ """Ensure the agent is initialized"""
406
+ if not self.initialized:
407
+ await self.initialize()
408
+
409
+ async def process_competitive_programming_request(self, context: AIAgentContext) -> Dict[str, Any]:
410
+ """Main method to process a competitive programming request"""
411
+ await self.ensure_initialized()
412
+
413
+ logging.info(f"🎯 Processing request for: {context.problem_statement[:100]}...")
414
+
415
+ # Step 1: Analyze problem patterns
416
+ pattern_analysis = await self.analyze_problem_patterns(context.problem_statement)
417
+
418
+ if "error" in pattern_analysis:
419
+ return {"error": "Failed to analyze problem patterns", "details": pattern_analysis["error"]}
420
+
421
+ # Step 2: Generate solution code
422
+ identified_patterns = pattern_analysis.get("identified_patterns", [])
423
+ solution = await self.generate_solution_code(context, identified_patterns)
424
+
425
+ # Step 3: Get learning recommendations
426
+ performance_data = {
427
+ "patterns_identified": len(identified_patterns),
428
+ "confidence_level": max(pattern_analysis.get("confidence_scores", {}).values()) if pattern_analysis.get("confidence_scores") else 0
429
+ }
430
+ learning_recs = await self.get_learning_recommendations(context, performance_data)
431
+
432
+ # Step 4: Compile comprehensive response
433
+ return {
434
+ "analysis": {
435
+ "patterns": pattern_analysis,
436
+ "processing_time": "2.3 seconds",
437
+ "mcp_queries": 3
438
+ },
439
+ "solution": solution,
440
+ "learning": learning_recs,
441
+ "status": "success",
442
+ "agent_confidence": 92.5
443
+ }
444
+
445
+
446
+ # Integration with Gradio UI
447
+ class MCPGradioInterface:
448
+ """Gradio interface that uses MCP-powered AI agent"""
449
+
450
+ def __init__(self):
451
+ self.agent = TopCoderAIAgent()
452
+ self.initialized = False
453
+
454
+ async def initialize_interface(self):
455
+ """Initialize the interface and underlying agent"""
456
+ if not self.initialized:
457
+ await self.agent.initialize()
458
+ self.initialized = True
459
+ logging.info("βœ… Gradio interface initialized with MCP connection")
460
+
461
+ def process_problem_sync(self, problem_statement: str, difficulty: str, language: str, skill_level: str = "intermediate"):
462
+ """Synchronous wrapper for async processing (required for Gradio)"""
463
+ try:
464
+ # Create event loop if needed
465
+ try:
466
+ loop = asyncio.get_event_loop()
467
+ except RuntimeError:
468
+ loop = asyncio.new_event_loop()
469
+ asyncio.set_event_loop(loop)
470
+
471
+ # Ensure interface is initialized
472
+ if not self.initialized:
473
+ loop.run_until_complete(self.initialize_interface())
474
+
475
+ # Create context
476
+ context = AIAgentContext(
477
+ problem_statement=problem_statement,
478
+ difficulty_level=difficulty,
479
+ preferred_language=language,
480
+ user_skill_level=skill_level
481
+ )
482
+
483
+ # Process the request
484
+ result = loop.run_until_complete(
485
+ self.agent.process_competitive_programming_request(context)
486
+ )
487
+
488
+ return self.format_gradio_response(result)
489
+
490
+ except Exception as e:
491
+ logging.error(f"Error processing problem: {e}")
492
+ return self.format_error_response(str(e))
493
+
494
+ def format_gradio_response(self, result: Dict[str, Any]) -> tuple:
495
+ """Format response for Gradio interface"""
496
+ if "error" in result:
497
+ return (
498
+ f"❌ Error: {result['error']}",
499
+ "",
500
+ "",
501
+ ""
502
+ )
503
+
504
+ # Format pattern analysis
505
+ analysis = result.get("analysis", {})
506
+ patterns = analysis.get("patterns", {})
507
+ identified_patterns = patterns.get("identified_patterns", [])
508
+ confidence_scores = patterns.get("confidence_scores", {})
509
+
510
+ pattern_text = "🎯 **Identified Patterns:**\n"
511
+ for pattern in identified_patterns:
512
+ confidence = confidence_scores.get(pattern, 0)
513
+ pattern_text += f"- {pattern}: {confidence}% confidence\n"
514
+
515
+ if patterns.get("similar_challenges"):
516
+ pattern_text += "\nπŸ“š **Similar Challenges:**\n"
517
+ for challenge in patterns["similar_challenges"]:
518
+ pattern_text += f"- {challenge.get('name', 'Unknown')}\n"
519
+
520
+ # Format solution code
521
+ solution = result.get("solution", {})
522
+ code_text = f"```{solution.get('language', 'python')}\n{solution.get('generated_code', 'No code generated')}\n```"
523
+
524
+ complexity_analysis = solution.get("complexity_analysis", {})
525
+ code_text += f"\n\n⚑ **Complexity Analysis:**\n"
526
+ code_text += f"- Time: {complexity_analysis.get('time_complexity', 'N/A')}\n"
527
+ code_text += f"- Space: {complexity_analysis.get('space_complexity', 'N/A')}\n"
528
+ code_text += f"- Explanation: {complexity_analysis.get('explanation', 'N/A')}\n"
529
+
530
+ # Format optimization suggestions
531
+ optimizations = solution.get("optimization_suggestions", [])
532
+ optimization_text = "πŸš€ **Optimization Suggestions:**\n"
533
+ for i, suggestion in enumerate(optimizations, 1):
534
+ optimization_text += f"{i}. {suggestion}\n"
535
+
536
+ # Format learning recommendations
537
+ learning = result.get("learning", {})
538
+ learning_text = "πŸŽ“ **Learning Recommendations:**\n\n"
539
+
540
+ priority_topics = learning.get("priority_topics", [])
541
+ if priority_topics:
542
+ learning_text += "**Priority Topics:**\n"
543
+ for topic in priority_topics:
544
+ learning_text += f"- {topic}\n"
545
+ learning_text += "\n"
546
+
547
+ study_plan = learning.get("study_plan", [])
548
+ if study_plan:
549
+ learning_text += "**Study Plan:**\n"
550
+ for plan_item in study_plan:
551
+ learning_text += f"- {plan_item}\n"
552
+ learning_text += "\n"
553
+
554
+ practice_challenges = learning.get("practice_challenges", [])
555
+ if practice_challenges:
556
+ learning_text += "**Practice Recommendations:**\n"
557
+ for challenge in practice_challenges:
558
+ learning_text += f"- {challenge}\n"
559
+
560
+ return (
561
+ pattern_text,
562
+ code_text,
563
+ optimization_text,
564
+ learning_text
565
+ )
566
+
567
+ def format_error_response(self, error_message: str) -> tuple:
568
+ """Format error response for Gradio"""
569
+ error_text = f"❌ **Error Processing Request**\n\n{error_message}\n\nπŸ”§ **Troubleshooting:**\n- Check your internet connection\n- Verify the problem statement is clear\n- Try a different difficulty level"
570
+ return (error_text, "", "", "")
571
+
572
+
573
+ # Example usage for integration with your main application
574
+ async def example_integration():
575
+ """Example of how to integrate MCP client with your AI agent"""
576
+
577
+ # Initialize the AI agent
578
+ agent = TopCoderAIAgent()
579
+ await agent.initialize()
580
+
581
+ # Example problem from competitive programming
582
+ example_context = AIAgentContext(
583
+ problem_statement="""
584
+ Given an array of integers, find the maximum sum of non-adjacent elements.
585
+ For example, given [2, 1, 4, 5], the maximum sum would be 6 (2 + 4).
586
+
587
+ Constraints:
588
+ - Array length: 1 ≀ n ≀ 10^5
589
+ - Element values: -10^9 ≀ arr[i] ≀ 10^9
590
+ """,
591
+ difficulty_level="Medium",
592
+ preferred_language="Python",
593
+ user_skill_level="intermediate"
594
+ )
595
+
596
+ # Process the request
597
+ result = await agent.process_competitive_programming_request(example_context)
598
+
599
+ # Display results
600
+ print("🎯 PROBLEM ANALYSIS:")
601
+ analysis = result.get("analysis", {})
602
+ patterns = analysis.get("patterns", {})
603
+ print(f"Patterns identified: {patterns.get('identified_patterns', [])}")
604
+ print(f"Confidence scores: {patterns.get('confidence_scores', {})}")
605
+
606
+ print("\nπŸ’» GENERATED SOLUTION:")
607
+ solution = result.get("solution", {})
608
+ print(f"Language: {solution.get('language', 'Unknown')}")
609
+ print("Code:")
610
+ print(solution.get("generated_code", "No code generated"))
611
+
612
+ print("\nπŸŽ“ LEARNING RECOMMENDATIONS:")
613
+ learning = result.get("learning", {})
614
+ print(f"Priority topics: {learning.get('priority_topics', [])}")
615
+ print(f"Study plan: {learning.get('study_plan', [])}")
616
+
617
+ return result
618
+
619
+
620
+ # Utility function for testing MCP connection
621
+ async def test_mcp_integration():
622
+ """Test the MCP integration without running the full agent"""
623
+ try:
624
+ # Test with simplified client
625
+ client = SimpleMCPClient()
626
+ await client.initialize()
627
+
628
+ # Test tool listing
629
+ tools_response = await client.list_tools()
630
+ print(f"βœ… Available tools: {len(tools_response.get('tools', []))}")
631
+
632
+ # Test challenge query
633
+ challenges_response = await client.call_tool(
634
+ "query-tc-challenges",
635
+ {"status": "Completed", "perPage": 2}
636
+ )
637
+
638
+ if challenges_response.get("success"):
639
+ challenges = challenges_response.get("data", [])
640
+ print(f"βœ… Retrieved {len(challenges)} challenges")
641
+ for challenge in challenges:
642
+ print(f" - {challenge.get('name', 'Unknown')}")
643
+
644
+ return True
645
+
646
+ except Exception as e:
647
+ print(f"❌ MCP integration test failed: {e}")
648
+ return False
649
+
650
+
651
+ # Main execution for testing
652
+ if __name__ == "__main__":
653
+ async def main():
654
+ print("πŸ”§ Testing MCP Integration...")
655
+
656
+ # Test basic MCP functionality
657
+ mcp_test_passed = await test_mcp_integration()
658
+
659
+ if mcp_test_passed:
660
+ print("\nπŸš€ Running full AI agent example...")
661
+ # Run the full example
662
+ await example_integration()
663
+ else:
664
+ print("❌ MCP integration test failed, skipping full example")
665
+
666
+ # Run the tests
667
+ asyncio.run(main())
topcoder_mcp_client.py ADDED
@@ -0,0 +1,376 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import aiohttp
3
+ import json
4
+ import uuid
5
+ import datetime
6
+ import logging
7
+ from typing import Dict, Any, Optional, List
8
+ from dataclasses import dataclass
9
+
10
+ # Set up logging
11
+ logging.basicConfig(level=logging.INFO)
12
+ logger = logging.getLogger(__name__)
13
+
14
+ @dataclass
15
+ class MCPResponse:
16
+ """Structured response from MCP server"""
17
+ success: bool
18
+ data: Any = None
19
+ error: str = None
20
+
21
+ class TopcoderMCPClient:
22
+ """
23
+ Modern MCP Client for TopCoder API using Streamable HTTP transport
24
+ Based on MCP specification 2025-03-26
25
+ """
26
+
27
+ def __init__(self, base_url: str = "https://api.topcoder-dev.com/v6/mcp"):
28
+ self.base_url = base_url
29
+ self.session_id = str(uuid.uuid4())
30
+ self.request_id = 1
31
+ self.initialized = False
32
+
33
+ # Session configuration
34
+ self.timeout = aiohttp.ClientTimeout(total=30)
35
+ self.headers = {
36
+ "Content-Type": "application/json",
37
+ "Accept": "application/json, text/event-stream",
38
+ "User-Agent": "TopCoder-MCP-Client/1.0"
39
+ }
40
+
41
+ def _get_next_id(self) -> int:
42
+ """Generate next request ID"""
43
+ self.request_id += 1
44
+ return self.request_id
45
+
46
+ def _create_request(self, method: str, params: Dict[str, Any] = None) -> Dict[str, Any]:
47
+ """Create a JSON-RPC 2.0 request"""
48
+ request = {
49
+ "jsonrpc": "2.0",
50
+ "method": method,
51
+ "id": self._get_next_id()
52
+ }
53
+
54
+ if params is not None:
55
+ request["params"] = params
56
+
57
+ return request
58
+
59
+ async def _send_request(self, request: Dict[str, Any], endpoint: str = "mcp") -> MCPResponse:
60
+ """Send request using Streamable HTTP transport"""
61
+ try:
62
+ url = f"{self.base_url}/{endpoint}"
63
+ logger.info(f"Sending request to {url}: {request['method']}")
64
+ logger.debug(f"Request payload: {json.dumps(request, indent=2)}")
65
+
66
+ async with aiohttp.ClientSession(timeout=self.timeout) as session:
67
+ async with session.post(
68
+ url,
69
+ json=request,
70
+ headers=self.headers
71
+ ) as response:
72
+
73
+ logger.info(f"Response status: {response.status}")
74
+ logger.debug(f"Response headers: {dict(response.headers)}")
75
+
76
+ if response.status != 200:
77
+ error_text = await response.text()
78
+ logger.error(f"HTTP Error {response.status}: {error_text}")
79
+ return MCPResponse(
80
+ success=False,
81
+ error=f"HTTP {response.status}: {error_text}"
82
+ )
83
+
84
+ # Handle different content types
85
+ content_type = response.headers.get('content-type', '').lower()
86
+
87
+ if 'text/event-stream' in content_type:
88
+ # Handle SSE response
89
+ return await self._parse_sse_response(response)
90
+ elif 'application/json' in content_type:
91
+ # Handle JSON response
92
+ data = await response.json()
93
+ logger.debug(f"JSON response: {json.dumps(data, indent=2)}")
94
+
95
+ if 'error' in data:
96
+ return MCPResponse(
97
+ success=False,
98
+ error=data['error'].get('message', 'Unknown MCP error')
99
+ )
100
+
101
+ return MCPResponse(success=True, data=data.get('result'))
102
+ else:
103
+ # Handle plain text response
104
+ text = await response.text()
105
+ logger.debug(f"Text response: {text}")
106
+ return MCPResponse(success=True, data=text)
107
+
108
+ except asyncio.TimeoutError:
109
+ logger.error("Request timeout")
110
+ return MCPResponse(success=False, error="Request timeout")
111
+ except aiohttp.ClientError as e:
112
+ logger.error(f"Client error: {e}")
113
+ return MCPResponse(success=False, error=f"Client error: {e}")
114
+ except Exception as e:
115
+ logger.error(f"Unexpected error: {e}")
116
+ return MCPResponse(success=False, error=f"Unexpected error: {e}")
117
+
118
+ async def _parse_sse_response(self, response) -> MCPResponse:
119
+ """Parse Server-Sent Events response"""
120
+ try:
121
+ text = await response.text()
122
+ logger.debug(f"SSE Response: {text}")
123
+
124
+ # Parse SSE format
125
+ lines = text.strip().split('\n')
126
+ data_lines = []
127
+
128
+ for line in lines:
129
+ line = line.strip()
130
+ if line.startswith('data: '):
131
+ data_content = line[6:] # Remove 'data: ' prefix
132
+ if data_content and data_content != '[DONE]':
133
+ try:
134
+ data = json.loads(data_content)
135
+ data_lines.append(data)
136
+ except json.JSONDecodeError:
137
+ logger.warning(f"Invalid JSON in SSE data: {data_content}")
138
+
139
+ if data_lines:
140
+ # Return the last complete response
141
+ last_response = data_lines[-1]
142
+ if 'error' in last_response:
143
+ return MCPResponse(
144
+ success=False,
145
+ error=last_response['error'].get('message', 'SSE error')
146
+ )
147
+ return MCPResponse(success=True, data=last_response.get('result'))
148
+
149
+ return MCPResponse(success=False, error="No valid data in SSE response")
150
+
151
+ except Exception as e:
152
+ logger.error(f"Error parsing SSE response: {e}")
153
+ return MCPResponse(success=False, error=f"SSE parsing error: {e}")
154
+
155
+ async def initialize(self) -> MCPResponse:
156
+ """Initialize MCP session"""
157
+ if self.initialized:
158
+ return MCPResponse(success=True, data="Already initialized")
159
+
160
+ logger.info("Initializing MCP session...")
161
+
162
+ params = {
163
+ "protocolVersion": "2025-03-26", # Use latest protocol version
164
+ "capabilities": {
165
+ "tools": {}
166
+ },
167
+ "clientInfo": {
168
+ "name": "topcoder-mcp-client",
169
+ "version": "1.0.0"
170
+ }
171
+ }
172
+
173
+ request = self._create_request("initialize", params)
174
+ response = await self._send_request(request)
175
+
176
+ if response.success:
177
+ self.initialized = True
178
+ logger.info("MCP session initialized successfully")
179
+
180
+ # Extract server capabilities if available
181
+ if response.data and isinstance(response.data, dict):
182
+ server_info = response.data.get('serverInfo', {})
183
+ capabilities = response.data.get('capabilities', {})
184
+ logger.info(f"Server: {server_info.get('name', 'Unknown')} v{server_info.get('version', 'Unknown')}")
185
+ logger.info(f"Server capabilities: {list(capabilities.keys())}")
186
+
187
+ return response
188
+
189
+ async def list_tools(self) -> MCPResponse:
190
+ """List available MCP tools"""
191
+ if not self.initialized:
192
+ init_response = await self.initialize()
193
+ if not init_response.success:
194
+ return init_response
195
+
196
+ logger.info("Listing available tools...")
197
+ request = self._create_request("tools/list")
198
+ return await self._send_request(request)
199
+
200
+ async def list_resources(self) -> MCPResponse:
201
+ """List available MCP resources"""
202
+ if not self.initialized:
203
+ init_response = await self.initialize()
204
+ if not init_response.success:
205
+ return init_response
206
+
207
+ logger.info("Listing available resources...")
208
+ request = self._create_request("resources/list")
209
+ return await self._send_request(request)
210
+
211
+ async def call_tool(self, tool_name: str, arguments: Dict[str, Any] = None) -> MCPResponse:
212
+ """Call an MCP tool"""
213
+ if not self.initialized:
214
+ init_response = await self.initialize()
215
+ if not init_response.success:
216
+ return init_response
217
+
218
+ logger.info(f"Calling tool: {tool_name}")
219
+
220
+ params = {
221
+ "name": tool_name
222
+ }
223
+
224
+ if arguments:
225
+ params["arguments"] = arguments
226
+
227
+ request = self._create_request("tools/call", params)
228
+ return await self._send_request(request)
229
+
230
+ async def query_challenges(self, **kwargs) -> MCPResponse:
231
+ """Query TopCoder challenges using MCP"""
232
+ # Common challenge query parameters
233
+ params = {
234
+ "status": kwargs.get("status", "Completed"),
235
+ "perPage": kwargs.get("per_page", 10),
236
+ "page": kwargs.get("page", 1),
237
+ "sortBy": kwargs.get("sort_by", "startDate"),
238
+ "sortOrder": kwargs.get("sort_order", "desc")
239
+ }
240
+
241
+ # Add additional parameters if provided
242
+ for key, value in kwargs.items():
243
+ if key not in ["status", "per_page", "page", "sort_by", "sort_order"] and value is not None:
244
+ params[key] = value
245
+
246
+ return await self.call_tool("query-tc-challenges", params)
247
+
248
+ async def get_challenge_details(self, challenge_id: str) -> MCPResponse:
249
+ """Get details for a specific challenge"""
250
+ return await self.call_tool("get-tc-challenge", {"id": challenge_id})
251
+
252
+ async def search_members(self, query: str, limit: int = 10) -> MCPResponse:
253
+ """Search TopCoder members"""
254
+ return await self.call_tool("search-tc-members", {"query": query, "limit": limit})
255
+
256
+ async def ping(self) -> MCPResponse:
257
+ """Ping the MCP server"""
258
+ request = self._create_request("ping")
259
+ return await self._send_request(request)
260
+
261
+
262
+ # Utility functions for easy usage
263
+ async def create_mcp_client(base_url: str = None) -> TopcoderMCPClient:
264
+ """Create and initialize MCP client"""
265
+ if base_url is None:
266
+ base_url = "https://api.topcoder-dev.com/v6/mcp"
267
+
268
+ client = TopcoderMCPClient(base_url)
269
+ init_response = await client.initialize()
270
+
271
+ if not init_response.success:
272
+ logger.error(f"Failed to initialize MCP client: {init_response.error}")
273
+ raise RuntimeError(f"MCP initialization failed: {init_response.error}")
274
+
275
+ return client
276
+
277
+
278
+ # Example usage and testing
279
+ async def test_mcp_connection():
280
+ """Test MCP connection and basic functionality"""
281
+ try:
282
+ logger.info("πŸ”— Testing TopCoder MCP Connection...")
283
+
284
+ # Create and initialize client
285
+ client = await create_mcp_client()
286
+
287
+ # Test ping
288
+ logger.info("πŸ“‘ Testing ping...")
289
+ ping_response = await client.ping()
290
+ if ping_response.success:
291
+ logger.info("βœ… Ping successful")
292
+ else:
293
+ logger.warning(f"⚠️ Ping failed: {ping_response.error}")
294
+
295
+ # List available tools
296
+ logger.info("πŸ”§ Listing available tools...")
297
+ tools_response = await client.list_tools()
298
+ if tools_response.success and tools_response.data:
299
+ tools = tools_response.data.get("tools", [])
300
+ logger.info(f"βœ… Found {len(tools)} tools:")
301
+ for tool in tools[:5]: # Show first 5 tools
302
+ logger.info(f" - {tool.get('name', 'Unknown')}: {tool.get('description', 'No description')}")
303
+ else:
304
+ logger.warning(f"⚠️ Failed to list tools: {tools_response.error}")
305
+
306
+ # List available resources
307
+ logger.info("πŸ“š Listing available resources...")
308
+ resources_response = await client.list_resources()
309
+ if resources_response.success and resources_response.data:
310
+ resources = resources_response.data.get("resources", [])
311
+ logger.info(f"βœ… Found {len(resources)} resources:")
312
+ for resource in resources[:5]: # Show first 5 resources
313
+ logger.info(f" - {resource.get('uri', 'Unknown')}: {resource.get('description', 'No description')}")
314
+ else:
315
+ logger.warning(f"⚠️ Failed to list resources: {resources_response.error}")
316
+
317
+ # Query challenges
318
+ logger.info("πŸ† Querying challenges...")
319
+ challenges_response = await client.query_challenges(
320
+ status="Completed",
321
+ per_page=3,
322
+ page=1
323
+ )
324
+
325
+ if challenges_response.success and challenges_response.data:
326
+ challenges = challenges_response.data
327
+ if isinstance(challenges, list):
328
+ logger.info(f"βœ… Retrieved {len(challenges)} challenges:")
329
+ for challenge in challenges:
330
+ if isinstance(challenge, dict):
331
+ name = challenge.get('name', 'Unknown')
332
+ challenge_id = challenge.get('id', 'Unknown')
333
+ logger.info(f" - {name} (ID: {challenge_id})")
334
+ else:
335
+ logger.info(f"βœ… Challenge query response: {challenges}")
336
+ else:
337
+ logger.warning(f"⚠️ Failed to query challenges: {challenges_response.error}")
338
+
339
+ logger.info("πŸŽ‰ MCP connection test completed!")
340
+ return client
341
+
342
+ except Exception as e:
343
+ logger.error(f"❌ MCP connection test failed: {e}")
344
+ raise
345
+
346
+
347
+ # Main execution
348
+ if __name__ == "__main__":
349
+ async def main():
350
+ # Test the MCP client
351
+ client = await test_mcp_connection()
352
+
353
+ # Example: Get more specific challenge data
354
+ print("\n" + "="*50)
355
+ print("ADDITIONAL EXAMPLES:")
356
+ print("="*50)
357
+
358
+ # Search for algorithm challenges
359
+ logger.info("πŸ” Searching for algorithm challenges...")
360
+ algo_response = await client.query_challenges(
361
+ status="Completed",
362
+ track="Algorithm",
363
+ per_page=2
364
+ )
365
+
366
+ if algo_response.success:
367
+ logger.info("βœ… Algorithm challenges found")
368
+
369
+ # Test error handling with invalid tool
370
+ logger.info("πŸ§ͺ Testing error handling...")
371
+ invalid_response = await client.call_tool("invalid-tool-name")
372
+ if not invalid_response.success:
373
+ logger.info(f"βœ… Error handling works: {invalid_response.error}")
374
+
375
+ # Run the async main function
376
+ asyncio.run(main())