waroca commited on
Commit
add9f72
·
verified ·
1 Parent(s): f1b8a40

Upload folder using huggingface_hub

Browse files
Files changed (2) hide show
  1. README.md +54 -5
  2. server.py +138 -1
README.md CHANGED
@@ -1,10 +1,59 @@
1
  ---
2
- title: Datapass Server
3
- emoji: 🌖
4
- colorFrom: pink
5
- colorTo: gray
6
  sdk: docker
 
7
  pinned: false
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: DataPass Server
3
+ emoji: 🎫
4
+ colorFrom: indigo
5
+ colorTo: purple
6
  sdk: docker
7
+ app_file: Dockerfile
8
  pinned: false
9
  ---
10
 
11
+ # DataPass MCP Server
12
+
13
+ **Your pass to private data.**
14
+
15
+ This is the MCP (Model Context Protocol) server that powers DataPass. It handles:
16
+
17
+ - Token validation for dataset access
18
+ - SQL and natural language query execution via DuckDB
19
+ - Stripe payment integration
20
+ - Subscription ledger management
21
+
22
+ ## MCP Endpoint
23
+
24
+ Connect your MCP client (Claude Desktop, Cursor, etc.) to:
25
+
26
+ ```
27
+ sse: https://your-space.hf.space/sse
28
+ ```
29
+
30
+ ## Available MCP Tools
31
+
32
+ ### Public Tools
33
+ - `get_dataset_catalog` - Browse available datasets
34
+
35
+ ### Authenticated Tools (require DataPass token)
36
+ - `get_my_datasets` - View your subscribed datasets
37
+ - `query_dataset` - Run SQL queries on datasets
38
+ - `query_dataset_natural_language` - Ask questions in plain English
39
+ - `get_dataset_sample` - Preview dataset structure
40
+
41
+ ## Configuration
42
+
43
+ This Space requires the following **Secrets** (Settings → Variables and secrets):
44
+
45
+ | Secret | Description |
46
+ |--------|-------------|
47
+ | `HF_TOKEN` | HF token with write access to ledger dataset |
48
+ | `ADMIN_API_SECRET` | Shared secret for admin API authentication |
49
+ | `LEDGER_DATASET_ID` | Dataset ID for subscription ledger (e.g., `username/datapass-ledger`) |
50
+ | `STRIPE_SECRET_KEY` | Stripe API key for payments |
51
+ | `STRIPE_WEBHOOK_SECRET` | Stripe webhook signing secret |
52
+ | `FRONTEND_URL` | URL to DataPass frontend (for landing page redirect) |
53
+
54
+ ## Security Model
55
+
56
+ - Dataset files never leave Hugging Face
57
+ - Every request validates the DataPass token
58
+ - Query results are capped (no `SELECT *` dumps)
59
+ - Access automatically expires after subscription period
server.py CHANGED
@@ -1,6 +1,6 @@
1
  from mcp.server.fastmcp import FastMCP, Context
2
  from fastapi import Request
3
- from fastapi.responses import JSONResponse
4
  from dotenv import load_dotenv
5
  import os
6
  import json
@@ -618,6 +618,143 @@ async def api_admin_detect_format(request: Request):
618
  return JSONResponse(result)
619
 
620
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
621
  # =============================================================================
622
  # App Initialization
623
  # =============================================================================
 
1
  from mcp.server.fastmcp import FastMCP, Context
2
  from fastapi import Request
3
+ from fastapi.responses import JSONResponse, HTMLResponse
4
  from dotenv import load_dotenv
5
  import os
6
  import json
 
618
  return JSONResponse(result)
619
 
620
 
621
+ # =============================================================================
622
+ # Landing Page
623
+ # =============================================================================
624
+
625
+ FRONTEND_URL = os.getenv("FRONTEND_URL", "https://huggingface.co/spaces/waroca/datapass")
626
+
627
+ @mcp.custom_route("/", methods=["GET"])
628
+ async def landing_page(request: Request):
629
+ """Landing page with DataPass info and redirect to frontend."""
630
+ html = f"""
631
+ <!DOCTYPE html>
632
+ <html lang="en">
633
+ <head>
634
+ <meta charset="UTF-8">
635
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
636
+ <title>DataPass - MCP Server</title>
637
+ <style>
638
+ * {{ margin: 0; padding: 0; box-sizing: border-box; }}
639
+ body {{
640
+ font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
641
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
642
+ min-height: 100vh;
643
+ display: flex;
644
+ align-items: center;
645
+ justify-content: center;
646
+ color: #fff;
647
+ }}
648
+ .container {{
649
+ text-align: center;
650
+ padding: 2rem;
651
+ max-width: 600px;
652
+ }}
653
+ .logo {{
654
+ font-size: 4rem;
655
+ margin-bottom: 1rem;
656
+ }}
657
+ h1 {{
658
+ font-size: 2.5rem;
659
+ font-weight: 700;
660
+ margin-bottom: 0.5rem;
661
+ letter-spacing: -0.02em;
662
+ }}
663
+ .tagline {{
664
+ font-size: 1.25rem;
665
+ color: rgba(255,255,255,0.7);
666
+ margin-bottom: 2rem;
667
+ }}
668
+ .card {{
669
+ background: rgba(255,255,255,0.1);
670
+ backdrop-filter: blur(10px);
671
+ border: 1px solid rgba(255,255,255,0.2);
672
+ border-radius: 16px;
673
+ padding: 2rem;
674
+ margin-bottom: 2rem;
675
+ }}
676
+ .card h2 {{
677
+ font-size: 1.125rem;
678
+ margin-bottom: 1rem;
679
+ color: rgba(255,255,255,0.9);
680
+ }}
681
+ .features {{
682
+ display: grid;
683
+ grid-template-columns: 1fr 1fr;
684
+ gap: 1rem;
685
+ text-align: left;
686
+ }}
687
+ .feature {{
688
+ display: flex;
689
+ align-items: center;
690
+ gap: 0.5rem;
691
+ font-size: 0.9rem;
692
+ color: rgba(255,255,255,0.8);
693
+ }}
694
+ .btn {{
695
+ display: inline-block;
696
+ background: #fff;
697
+ color: #1a1a2e;
698
+ padding: 0.875rem 2rem;
699
+ border-radius: 50px;
700
+ text-decoration: none;
701
+ font-weight: 600;
702
+ font-size: 1rem;
703
+ transition: transform 0.2s, box-shadow 0.2s;
704
+ }}
705
+ .btn:hover {{
706
+ transform: translateY(-2px);
707
+ box-shadow: 0 10px 30px rgba(0,0,0,0.3);
708
+ }}
709
+ .mcp-info {{
710
+ margin-top: 2rem;
711
+ padding: 1rem;
712
+ background: rgba(0,0,0,0.2);
713
+ border-radius: 8px;
714
+ font-size: 0.875rem;
715
+ color: rgba(255,255,255,0.6);
716
+ }}
717
+ code {{
718
+ background: rgba(255,255,255,0.1);
719
+ padding: 0.125rem 0.375rem;
720
+ border-radius: 4px;
721
+ font-family: 'SF Mono', monospace;
722
+ }}
723
+ @media (max-width: 480px) {{
724
+ .features {{ grid-template-columns: 1fr; }}
725
+ h1 {{ font-size: 2rem; }}
726
+ }}
727
+ </style>
728
+ </head>
729
+ <body>
730
+ <div class="container">
731
+ <div class="logo">🎫</div>
732
+ <h1>DataPass</h1>
733
+ <p class="tagline">Your pass to private data.</p>
734
+
735
+ <div class="card">
736
+ <h2>Query private datasets without downloading them</h2>
737
+ <div class="features">
738
+ <div class="feature">🔒 Data stays on HF</div>
739
+ <div class="feature">💬 Natural language queries</div>
740
+ <div class="feature">⚡ SQL via DuckDB</div>
741
+ <div class="feature">🎫 Time-limited access</div>
742
+ </div>
743
+ </div>
744
+
745
+ <a href="{FRONTEND_URL}" class="btn">Get a DataPass →</a>
746
+
747
+ <div class="mcp-info">
748
+ <strong>MCP Server Endpoint:</strong><br>
749
+ <code>sse: {request.url.scheme}://{request.url.netloc}/sse</code>
750
+ </div>
751
+ </div>
752
+ </body>
753
+ </html>
754
+ """
755
+ return HTMLResponse(content=html)
756
+
757
+
758
  # =============================================================================
759
  # App Initialization
760
  # =============================================================================