galcan commited on
Commit
28d475f
·
1 Parent(s): 102fdc3
Files changed (1) hide show
  1. app.py +215 -0
app.py CHANGED
@@ -416,5 +416,220 @@ async def mcp_sse():
416
  }
417
  )
418
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
419
  if __name__ == "__main__":
420
  uvicorn.run(app, host="0.0.0.0", port=7860)
 
416
  }
417
  )
418
 
419
+ @app.post("/mcp/stream")
420
+ async def mcp_post(request: dict):
421
+ """Handle MCP requests via POST (for mcp-remote bidirectional communication)"""
422
+ try:
423
+ method = request.get("method")
424
+ params = request.get("params", {})
425
+ request_id = request.get("id")
426
+
427
+ if method == "tools/list":
428
+ return {
429
+ "jsonrpc": "2.0",
430
+ "id": request_id,
431
+ "result": {
432
+ "tools": [
433
+ {
434
+ "name": "search_docs",
435
+ "title": "Search Documentation",
436
+ "description": "Search through MCP documentation chunks",
437
+ "inputSchema": {
438
+ "type": "object",
439
+ "properties": {
440
+ "query": {
441
+ "type": "string",
442
+ "description": "Search query for MCP documentation"
443
+ },
444
+ "limit": {
445
+ "type": "integer",
446
+ "description": "Maximum number of results",
447
+ "default": 5
448
+ }
449
+ },
450
+ "required": ["query"]
451
+ }
452
+ },
453
+ {
454
+ "name": "get_chunk",
455
+ "title": "Get Documentation Chunk",
456
+ "description": "Get a specific documentation chunk by ID",
457
+ "inputSchema": {
458
+ "type": "object",
459
+ "properties": {
460
+ "chunk_id": {
461
+ "type": "string",
462
+ "description": "Chunk ID to retrieve"
463
+ }
464
+ },
465
+ "required": ["chunk_id"]
466
+ }
467
+ },
468
+ {
469
+ "name": "list_docs",
470
+ "title": "List Documents",
471
+ "description": "List all available documents",
472
+ "inputSchema": {
473
+ "type": "object",
474
+ "properties": {}
475
+ }
476
+ }
477
+ ]
478
+ }
479
+ }
480
+
481
+ elif method == "tools/call":
482
+ tool_name = params.get("name")
483
+ arguments = params.get("arguments", {})
484
+
485
+ if tool_name == "search_docs":
486
+ query = arguments.get("query", "").lower()
487
+ limit = arguments.get("limit", 5)
488
+
489
+ results = []
490
+ for chunk in chunks_data or []:
491
+ text = chunk.get('text', '').lower()
492
+ title = chunk.get('title', '').lower()
493
+
494
+ score = 0
495
+ if query in text:
496
+ score += text.count(query) * 2
497
+ if query in title:
498
+ score += title.count(query) * 5
499
+
500
+ if score > 0:
501
+ results.append({
502
+ "chunk_id": chunk.get('chunk_id'),
503
+ "title": chunk.get('title'),
504
+ "text": chunk.get('text'),
505
+ "url": chunk.get('url'),
506
+ "filename": chunk.get('filename'),
507
+ "score": score
508
+ })
509
+
510
+ results = sorted(results, key=lambda x: x['score'], reverse=True)[:limit]
511
+
512
+ if results:
513
+ response_text = f"Found {len(results)} results for '{query}':\n\n"
514
+ for i, result in enumerate(results, 1):
515
+ response_text += f"{i}. **{result['title']}** (Score: {result['score']})\n"
516
+ response_text += f" {result['text'][:200]}...\n"
517
+ response_text += f" Source: {result['filename']}\n\n"
518
+ else:
519
+ response_text = f"No results found for '{query}'"
520
+
521
+ return {
522
+ "jsonrpc": "2.0",
523
+ "id": request_id,
524
+ "result": {
525
+ "content": [
526
+ {
527
+ "type": "text",
528
+ "text": response_text
529
+ }
530
+ ]
531
+ }
532
+ }
533
+
534
+ elif tool_name == "get_chunk":
535
+ chunk_id = arguments.get("chunk_id", "")
536
+
537
+ for chunk in chunks_data or []:
538
+ if chunk.get('chunk_id') == chunk_id:
539
+ response_text = f"**{chunk.get('title', 'Untitled')}**\n\n"
540
+ response_text += f"{chunk.get('text', '')}\n\n"
541
+ response_text += f"Source: {chunk.get('filename', 'Unknown')}\n"
542
+ response_text += f"URL: {chunk.get('url', 'N/A')}"
543
+
544
+ return {
545
+ "jsonrpc": "2.0",
546
+ "id": request_id,
547
+ "result": {
548
+ "content": [
549
+ {
550
+ "type": "text",
551
+ "text": response_text
552
+ }
553
+ ]
554
+ }
555
+ }
556
+
557
+ return {
558
+ "jsonrpc": "2.0",
559
+ "id": request_id,
560
+ "result": {
561
+ "content": [
562
+ {
563
+ "type": "text",
564
+ "text": f"Chunk {chunk_id} not found"
565
+ }
566
+ ]
567
+ }
568
+ }
569
+
570
+ elif tool_name == "list_docs":
571
+ if not docs_data:
572
+ return {
573
+ "jsonrpc": "2.0",
574
+ "id": request_id,
575
+ "result": {
576
+ "content": [
577
+ {
578
+ "type": "text",
579
+ "text": "No documents available"
580
+ }
581
+ ]
582
+ }
583
+ }
584
+
585
+ response_text = "Available documents:\n\n"
586
+ for doc in docs_data:
587
+ response_text += f"- **{doc.get('title', 'Untitled')}**\n"
588
+ response_text += f" ID: {doc.get('id', 'Unknown')}\n"
589
+ response_text += f" URL: {doc.get('url', 'N/A')}\n\n"
590
+
591
+ return {
592
+ "jsonrpc": "2.0",
593
+ "id": request_id,
594
+ "result": {
595
+ "content": [
596
+ {
597
+ "type": "text",
598
+ "text": response_text
599
+ }
600
+ ]
601
+ }
602
+ }
603
+
604
+ else:
605
+ return {
606
+ "jsonrpc": "2.0",
607
+ "id": request_id,
608
+ "error": {
609
+ "code": -32601,
610
+ "message": f"Unknown tool: {tool_name}"
611
+ }
612
+ }
613
+
614
+ else:
615
+ return {
616
+ "jsonrpc": "2.0",
617
+ "id": request_id,
618
+ "error": {
619
+ "code": -32601,
620
+ "message": f"Unknown method: {method}"
621
+ }
622
+ }
623
+
624
+ except Exception as e:
625
+ return {
626
+ "jsonrpc": "2.0",
627
+ "id": request.get("id"),
628
+ "error": {
629
+ "code": -32603,
630
+ "message": f"Internal error: {str(e)}"
631
+ }
632
+ }
633
+
634
  if __name__ == "__main__":
635
  uvicorn.run(app, host="0.0.0.0", port=7860)