Files changed (1) hide show
  1. app.py +249 -306
app.py CHANGED
@@ -11,8 +11,11 @@ import hashlib
11
  from pathlib import Path
12
  import requests
13
  from io import BytesIO
 
 
 
14
 
15
- class HFSecretsFileStorageMCP:
16
  def __init__(self):
17
  # Lade Secrets aus Space Environment
18
  self.hf_token = os.environ.get("HF_TOKEN", "")
@@ -20,29 +23,24 @@ class HFSecretsFileStorageMCP:
20
  self.org_name = os.environ.get("ORG_NAME", "mcp-filestorage")
21
  self.db_path = "user_management.db"
22
 
23
- # Fallback auf lokale Variablen falls Secrets nicht verfügbar
24
- if not self.hf_token:
25
- print("⚠️ HF_TOKEN nicht in Secrets gefunden, verwende leeren Token")
26
- if not self.dataset_name or self.dataset_name == "mcp-filestorage/default-files":
27
- print("⚠️ DATASET_NAME nicht in Secrets gefunden, erstelle lokalen Speicher")
28
-
29
  self.api = HfApi(token=self.hf_token if self.hf_token else None)
30
  self.init_database()
31
  self.init_storage()
32
-
 
33
  def init_database(self):
34
  """Initialisiert SQLite-Datenbank für Benutzerverwaltung"""
35
  conn = sqlite3.connect(self.db_path)
36
  cursor = conn.cursor()
37
 
38
- # Benutzer-Tabelle
39
  cursor.execute('''
40
  CREATE TABLE IF NOT EXISTS users (
41
  id INTEGER PRIMARY KEY AUTOINCREMENT,
42
  user_uuid TEXT UNIQUE NOT NULL,
 
43
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
44
  last_access DATETIME DEFAULT CURRENT_TIMESTAMP,
45
- folder_count INTEGER DEFAULT 0,
46
  file_count INTEGER DEFAULT 0,
47
  is_active BOOLEAN DEFAULT 1,
48
  storage_type TEXT DEFAULT 'hf_dataset'
@@ -76,12 +74,10 @@ class HFSecretsFileStorageMCP:
76
  """Initialisiert Speicher (Dataset oder lokal)"""
77
  try:
78
  if self.dataset_name and self.dataset_name != "mcp-filestorage/default-files":
79
- # Versuche Dataset zu laden
80
  self.dataset = load_dataset(self.dataset_name, split='train', token=self.hf_token)
81
  print(f"✅ Dataset geladen: {self.dataset_name}")
82
  self.storage_mode = 'dataset'
83
  else:
84
- # Lokaler Speicher als Fallback
85
  self.storage_mode = 'local'
86
  self.local_storage_path = Path("user_storage")
87
  self.local_storage_path.mkdir(exist_ok=True)
@@ -93,24 +89,38 @@ class HFSecretsFileStorageMCP:
93
  self.local_storage_path.mkdir(exist_ok=True)
94
 
95
  def register_user(self) -> Dict[str, Any]:
96
- """Registriert neuen Benutzer mit UUID"""
97
  user_uuid = str(uuid.uuid4())
 
98
 
99
  conn = sqlite3.connect(self.db_path)
100
  cursor = conn.cursor()
101
 
102
  try:
103
- cursor.execute('INSERT INTO users (user_uuid) VALUES (?)', (user_uuid,))
 
 
 
104
  conn.commit()
105
 
106
  # Erstelle Benutzerordner
107
  self._create_user_folder(user_uuid)
108
 
 
 
 
 
 
 
 
109
  return {
110
  'success': True,
111
  'user_uuid': user_uuid,
 
 
112
  'message': f'Benutzer {user_uuid} registriert'
113
  }
 
114
  except Exception as e:
115
  return {
116
  'success': False,
@@ -118,12 +128,35 @@ class HFSecretsFileStorageMCP:
118
  }
119
  finally:
120
  conn.close()
121
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  def _create_user_folder(self, user_uuid: str):
123
  """Erstellt Benutzerordner je nach Speicher-Modus"""
124
  if self.storage_mode == 'dataset':
125
  try:
126
- # Erstelle Ordner-Struktur im Dataset
127
  folder_path = f"users/{user_uuid}"
128
  readme_content = f"# User Folder: {user_uuid}\nCreated: {datetime.now().isoformat()}\n"
129
 
@@ -144,6 +177,11 @@ class HFSecretsFileStorageMCP:
144
  def create_file(self, user_uuid: str, file_path: str, content: bytes,
145
  metadata: Optional[Dict] = None) -> Dict[str, Any]:
146
  """Erstellt neue Datei für Benutzer"""
 
 
 
 
 
147
  file_uuid = str(uuid.uuid4())
148
  file_hash = hashlib.sha256(content).hexdigest()
149
 
@@ -151,9 +189,9 @@ class HFSecretsFileStorageMCP:
151
  cursor = conn.cursor()
152
 
153
  try:
154
- # Speichere Datei je nach Modus
155
  storage_path = f"users/{user_uuid}/{file_path}"
156
 
 
157
  if self.storage_mode == 'dataset':
158
  try:
159
  self.api.upload_file(
@@ -186,24 +224,29 @@ class HFSecretsFileStorageMCP:
186
 
187
  conn.commit()
188
 
 
 
 
 
189
  return {
190
  'success': True,
191
  'file_uuid': file_uuid,
192
  'file_path': file_path,
193
- 'storage_path': storage_path,
194
- 'storage_mode': self.storage_mode
195
  }
196
 
197
  except Exception as e:
198
- return {
199
- 'success': False,
200
- 'error': str(e)
201
- }
202
  finally:
203
  conn.close()
204
-
205
  def read_file(self, user_uuid: str, file_path: str) -> Dict[str, Any]:
206
  """Liest Datei eines Benutzers"""
 
 
 
 
 
207
  conn = sqlite3.connect(self.db_path)
208
  cursor = conn.cursor()
209
 
@@ -219,11 +262,11 @@ class HFSecretsFileStorageMCP:
219
 
220
  file_uuid, filename, storage_path = result
221
 
222
- # Erstelle Download-URL je nach Speicher-Modus
223
  if self.storage_mode == 'dataset':
224
  file_url = f"https://huggingface.co/datasets/{self.dataset_name}/resolve/main/{storage_path}"
225
  else:
226
- # Lokaler Speicher - konvertiere zu Base64 für Download
227
  local_file_path = self.local_storage_path / user_uuid / file_path
228
  if local_file_path.exists():
229
  import base64
@@ -232,10 +275,9 @@ class HFSecretsFileStorageMCP:
232
  else:
233
  return {'success': False, 'error': 'Datei nicht im lokalen Speicher gefunden'}
234
 
235
- # Aktualisiere Zugriffszeit
236
- cursor.execute('UPDATE users SET last_access = ? WHERE user_uuid = ?',
237
- (datetime.now(), user_uuid))
238
- conn.commit()
239
 
240
  return {
241
  'success': True,
@@ -243,16 +285,20 @@ class HFSecretsFileStorageMCP:
243
  'file_path': file_path,
244
  'file_url': file_url,
245
  'filename': filename,
246
- 'storage_mode': self.storage_mode
247
  }
248
 
249
  except Exception as e:
250
  return {'success': False, 'error': str(e)}
251
  finally:
252
  conn.close()
253
-
254
  def update_file(self, user_uuid: str, file_path: str, content: bytes) -> Dict[str, Any]:
255
  """Aktualisiert vorhandene Datei"""
 
 
 
 
256
  conn = sqlite3.connect(self.db_path)
257
  cursor = conn.cursor()
258
 
@@ -298,20 +344,29 @@ class HFSecretsFileStorageMCP:
298
 
299
  conn.commit()
300
 
 
 
 
 
301
  return {
302
  'success': True,
303
  'file_uuid': file_uuid,
304
  'version': current_version + 1,
305
- 'file_path': file_path
 
306
  }
307
 
308
  except Exception as e:
309
  return {'success': False, 'error': str(e)}
310
  finally:
311
  conn.close()
312
-
313
  def delete_file(self, user_uuid: str, file_path: str) -> Dict[str, Any]:
314
  """Löscht Datei (Soft Delete)"""
 
 
 
 
315
  conn = sqlite3.connect(self.db_path)
316
  cursor = conn.cursor()
317
 
@@ -333,19 +388,28 @@ class HFSecretsFileStorageMCP:
333
 
334
  conn.commit()
335
 
 
 
 
 
336
  return {
337
  'success': True,
338
  'message': f'Datei {file_path} gelöscht',
339
- 'file_path': file_path
 
340
  }
341
 
342
  except Exception as e:
343
  return {'success': False, 'error': str(e)}
344
  finally:
345
  conn.close()
346
-
347
  def list_files(self, user_uuid: str, folder_path: str = "") -> List[Dict[str, Any]]:
348
  """Listet alle Dateien eines Benutzers auf"""
 
 
 
 
349
  conn = sqlite3.connect(self.db_path)
350
  cursor = conn.cursor()
351
 
@@ -379,70 +443,51 @@ class HFSecretsFileStorageMCP:
379
  'file_size': row[6]
380
  })
381
 
382
- return files
 
 
383
 
384
- finally:
385
- conn.close()
386
-
387
- def get_user_stats(self, user_uuid: str) -> Dict[str, Any]:
388
- """Gibt Statistiken für einen Benutzer zurück"""
389
- conn = sqlite3.connect(self.db_path)
390
- cursor = conn.cursor()
391
-
392
- try:
393
- cursor.execute('''
394
- SELECT created_at, file_count, last_access
395
- FROM users WHERE user_uuid = ?
396
- ''', (user_uuid,))
397
-
398
- result = cursor.fetchone()
399
- if not result:
400
- return {'error': 'Benutzer nicht gefunden'}
401
-
402
- cursor.execute('''
403
- SELECT COUNT(*) FROM file_metadata WHERE user_uuid = ? AND is_deleted = 0
404
- ''', (user_uuid,))
405
-
406
- active_files = cursor.fetchone()[0]
407
-
408
- return {
409
- 'user_uuid': user_uuid,
410
- 'created_at': result[0],
411
- 'total_files': active_files,
412
- 'last_access': result[2],
413
- 'storage_mode': self.storage_mode
414
- }
415
 
416
  finally:
417
  conn.close()
418
 
 
 
 
 
 
 
 
419
  # Globale Instanz
420
- hf_secrets_storage = HFSecretsFileStorageMCP()
421
 
422
- # ===== MCP TOOLS =====
423
 
424
  @gr.mcp.tool()
425
- def register_user_secret() -> str:
426
  """
427
- Registriert einen neuen Benutzer und gibt eine UUID zurück.
428
 
429
  Returns:
430
- Erfolgsmeldung mit Benutzer-UUID
431
  """
432
- result = hf_secrets_storage.register_user()
433
 
434
  if result['success']:
435
  return f"""✅ Benutzer erfolgreich registriert!
 
436
  🆔 Ihre UUID: {result['user_uuid']}
437
- 📁 Ihr persönlicher Ordner: users/{result['user_uuid']}/
438
- 🔐 Dies ist Ihr Zugangsschlüssel - bitte sicher aufbewahren!
439
- ☁️ Speicher-Modus: {hf_secrets_storage.storage_mode}"""
 
440
  else:
441
  return f"❌ Fehler: {result.get('error', 'Unbekannter Fehler')}"
442
 
443
  @gr.mcp.tool()
444
- def create_user_file_secret(user_uuid: str, file_path: str, content: str,
445
- metadata: Optional[str] = None) -> str:
446
  """
447
  Erstellt eine neue Datei für einen Benutzer.
448
 
@@ -458,19 +503,19 @@ def create_user_file_secret(user_uuid: str, file_path: str, content: str,
458
  content_bytes = content.encode('utf-8')
459
  meta_dict = json.loads(metadata) if metadata else None
460
 
461
- result = hf_secrets_storage.create_file(user_uuid, file_path, content_bytes, meta_dict)
462
 
463
  if result['success']:
464
  return f"""✅ Datei erfolgreich erstellt!
465
  📁 Datei-UUID: {result['file_uuid']}
466
  📄 Pfad: {result['file_path']}
467
- ☁️ Gespeichert in: {result['storage_mode']}
468
- 🗃️ Dataset: {hf_secrets_storage.dataset_name if hf_secrets_storage.storage_mode == 'dataset' else 'Lokaler Speicher'}"""
469
  else:
470
  return f"❌ Fehler: {result.get('error', 'Unbekannter Fehler')}"
471
 
472
  @gr.mcp.tool()
473
- def read_user_file_secret(user_uuid: str, file_path: str) -> str:
474
  """
475
  Liest eine Datei eines Benutzers.
476
 
@@ -481,20 +526,20 @@ def read_user_file_secret(user_uuid: str, file_path: str) -> str:
481
  Returns:
482
  Datei-Details und Zugriffs-URL
483
  """
484
- result = hf_secrets_storage.read_file(user_uuid, file_path)
485
 
486
  if result['success']:
487
  return f"""📄 Datei erfolgreich gelesen!
488
  🆔 Datei-UUID: {result['file_uuid']}
489
  🔗 Direkt-Link: {result['file_url']}
490
  📁 Dateiname: {result['filename']}
491
- ☁️ Speicher-Modus: {result['storage_mode']}
492
- ⚠️ Hinweis: Verwenden Sie den Direkt-Link zum Herunterladen"""
493
  else:
494
  return f"❌ Fehler: {result.get('error', 'Datei nicht gefunden')}"
495
 
496
  @gr.mcp.tool()
497
- def update_user_file_secret(user_uuid: str, file_path: str, content: str) -> str:
498
  """
499
  Aktualisiert eine vorhandene Datei.
500
 
@@ -507,19 +552,19 @@ def update_user_file_secret(user_uuid: str, file_path: str, content: str) -> str
507
  Erfolgsmeldung mit Versions-Info
508
  """
509
  content_bytes = content.encode('utf-8')
510
- result = hf_secrets_storage.update_file(user_uuid, file_path, content_bytes)
511
 
512
  if result['success']:
513
  return f"""✅ Datei erfolgreich aktualisiert!
514
  🆔 Datei-UUID: {result['file_uuid']}
515
  🔢 Neue Version: {result['version']}
516
  📁 Pfad: {result['file_path']}
517
- ☁️ Gespeichert in: {hf_secrets_storage.storage_mode}-Modus"""
518
  else:
519
  return f"❌ Fehler: {result.get('error', 'Unbekannter Fehler')}"
520
 
521
  @gr.mcp.tool()
522
- def delete_user_file_secret(user_uuid: str, file_path: str) -> str:
523
  """
524
  Löscht eine Datei eines Benutzers.
525
 
@@ -530,18 +575,18 @@ def delete_user_file_secret(user_uuid: str, file_path: str) -> str:
530
  Returns:
531
  Erfolgsmeldung
532
  """
533
- result = hf_secrets_storage.delete_file(user_uuid, file_path)
534
 
535
  if result['success']:
536
  return f"""🗑️ Datei erfolgreich gelöscht!
537
  📁 Pfad: {result['file_path']}
538
- ☁️ Die Datei wurde aus dem {hf_secrets_storage.storage_mode} entfernt
539
- 🔄 Sie können die Datei bei Bedarf wiederherstellen"""
540
  else:
541
  return f"❌ Fehler: {result.get('error', 'Unbekannter Fehler')}"
542
 
543
  @gr.mcp.tool()
544
- def list_user_files_secret(user_uuid: str, folder_path: str = "") -> str:
545
  """
546
  Listet alle Dateien eines Benutzers auf.
547
 
@@ -552,13 +597,13 @@ def list_user_files_secret(user_uuid: str, folder_path: str = "") -> str:
552
  Returns:
553
  Liste aller Dateien des Benutzers
554
  """
555
- files = hf_secrets_storage.list_files(user_uuid, folder_path)
556
 
557
  if not files:
558
  return "📁 Keine Dateien gefunden!"
559
 
560
  output = f"📊 Gefunden: {len(files)} Dateien für Benutzer {user_uuid}\n"
561
- output += f"☁️ Speicher-Modus: {hf_secrets_storage.storage_mode}\n\n"
562
 
563
  for file in files:
564
  output += f"""📄 {file['filename']} (Version {file['version']})
@@ -572,99 +617,60 @@ def list_user_files_secret(user_uuid: str, folder_path: str = "") -> str:
572
 
573
  return output
574
 
575
- @gr.mcp.tool()
576
- def get_user_stats_secret(user_uuid: str) -> str:
577
- """
578
- Gibt Statistiken für einen Benutzer zurück.
579
-
580
- Args:
581
- user_uuid: Die UUID des Benutzers
582
-
583
- Returns:
584
- Benutzer-Statistiken
585
- """
586
- stats = hf_secrets_storage.get_user_stats(user_uuid)
587
-
588
- if 'error' in stats:
589
- return f"❌ Fehler: {stats['error']}"
590
-
591
- return f"""📊 Benutzer-Statistiken für {user_uuid}:
592
- 📅 Registriert: {stats['created_at']}
593
- 📁 Aktive Dateien: {stats['total_files']}
594
- 🔄 Letzter Zugriff: {stats['last_access']}
595
- ☁️ Speicher-Modus: {stats['storage_mode']}
596
- 🗃️ Dataset: {hf_secrets_storage.dataset_name}"""
597
-
598
- # ===== GRADIO INTERFACE =====
599
 
600
- def register_user_ui():
601
- """UI-Funktion für Benutzerregistrierung"""
602
- result = hf_secrets_storage.register_user()
603
-
604
- if result['success']:
605
- return f"""✅ Benutzer registriert!
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
606
 
607
- 🆔 UUID: {result['user_uuid']}
608
- 🔐 Speichern Sie diese UUID! Sie ist Ihr persönlicher Zugangsschlüssel.
609
- 📁 Ihr Ordner: users/{result['user_uuid']}/
610
- ☁️ Speicher-Modus: {hf_secrets_storage.storage_mode}"""
611
- else:
612
- return f"❌ Fehler: {result.get('error', 'Unbekannter Fehler')}"
613
-
614
- def create_file_ui(user_uuid, file_path, content, metadata):
615
- """UI-Funktion zum Erstellen von Dateien"""
616
- if not user_uuid or not file_path or not content:
617
- return "❌ Bitte füllen Sie UUID, Dateipfad und Inhalt aus!"
618
-
619
- content_bytes = content.encode('utf-8')
620
- meta_dict = json.loads(metadata) if metadata else None
621
-
622
- result = hf_secrets_storage.create_file(user_uuid, file_path, content_bytes, meta_dict)
623
-
624
- if result['success']:
625
- return f"""✅ Datei erstellt!
626
-
627
- 📁 Pfad: {result['file_path']}
628
- 🆔 UUID: {result['file_uuid']}
629
- ☁️ Modus: {result['storage_mode']}"""
630
- else:
631
- return f"❌ Fehler: {result.get('error', 'Unbekannter Fehler')}"
632
-
633
- def list_files_ui(user_uuid, folder_path):
634
- """UI-Funktion zum Auflisten von Dateien"""
635
- if not user_uuid:
636
- return "❌ Bitte geben Sie Ihre UUID ein!"
637
-
638
- files = hf_secrets_storage.list_files(user_uuid, folder_path)
639
-
640
- if not files:
641
- return "📁 Keine Dateien gefunden!"
642
-
643
- output = f"📊 {len(files)} Dateien gefunden:\n\n"
644
- for file in files:
645
- output += f"📄 {file['filename']} ({file['file_path']}) - Version {file['version']}\n"
646
-
647
- return output
648
-
649
- # Gradio Interface mit Space-Anpassungen
650
- with gr.Blocks(title="🤗 HF Secrets Filestorage MCP", theme=gr.themes.Soft()) as demo:
651
  gr.Markdown("""
652
  # 🗃️ Hugging Face Dataset Filestorage MCP Server
653
- ### 🔐 Sichere Cloud-Speicher mit Space Secrets
654
 
655
- Jeder Benutzer bekommt einen eigenen Ordner mit UUID und kann Dateien CRUD-Operationen durchführen.
656
- Alle Dateien werden sicher in Hugging Face Datasets gespeichert - konfiguriert über Space Secrets!
657
  """)
658
 
659
  with gr.Tab("📝 Registrieren"):
660
  with gr.Row():
661
  with gr.Column():
662
- gr.Markdown("### 🎯 Neuen Benutzer erstellen")
663
  register_btn = gr.Button("🎯 Neuen Benutzer erstellen", variant="primary", size="lg")
664
  with gr.Column():
665
- register_output = gr.Textbox(label="Registrierungsergebnis", lines=6, max_lines=8)
666
 
667
- register_btn.click(register_user_ui, outputs=register_output)
668
 
669
  with gr.Tab("📤 Datei erstellen"):
670
  with gr.Row():
@@ -680,96 +686,11 @@ with gr.Blocks(title="🤗 HF Secrets Filestorage MCP", theme=gr.themes.Soft())
680
  create_output = gr.Textbox(label="Ergebnis", lines=8, max_lines=10)
681
 
682
  create_btn.click(
683
- create_file_ui,
684
  inputs=[create_user_uuid, create_file_path, create_content, create_metadata],
685
  outputs=create_output
686
  )
687
 
688
- with gr.Tab("📖 Datei lesen"):
689
- with gr.Row():
690
- with gr.Column():
691
- gr.Markdown("### 📖 Datei aus dem Speicher lesen")
692
- read_user_uuid = gr.Textbox(label="🆔 Ihre UUID", placeholder="Ihre UUID eingeben")
693
- read_file_path = gr.Textbox(label="📁 Dateipfad", placeholder="z.B. meine-dokumente/test.txt")
694
- read_btn = gr.Button("📖 Datei lesen", variant="secondary")
695
-
696
- with gr.Column():
697
- read_output = gr.Textbox(label="Datei-Details", lines=8, max_lines=10)
698
-
699
- def read_file_wrapper(user_uuid, file_path):
700
- if not user_uuid or not file_path:
701
- return "❌ Bitte geben Sie UUID und Dateipfad ein!"
702
-
703
- result = hf_secrets_storage.read_file(user_uuid, file_path)
704
- if result['success']:
705
- return f"""📄 Datei gefunden!
706
-
707
- 📁 Name: {result['filename']}
708
- 🔗 URL: {result['file_url']}
709
- 🆔 UUID: {result['file_uuid']}
710
- ☁️ Modus: {result['storage_mode']}"""
711
- else:
712
- return f"❌ Fehler: {result.get('error', 'Datei nicht gefunden')}"
713
-
714
- read_btn.click(read_file_wrapper, inputs=[read_user_uuid, read_file_path], outputs=read_output)
715
-
716
- with gr.Tab("✏️ Datei aktualisieren"):
717
- with gr.Row():
718
- with gr.Column():
719
- gr.Markdown("### ✏️ Bestehende Datei bearbeiten")
720
- update_user_uuid = gr.Textbox(label="🆔 Ihre UUID", placeholder="Ihre UUID eingeben")
721
- update_file_path = gr.Textbox(label="📁 Dateipfad", placeholder="z.B. meine-dokumente/test.txt")
722
- update_content = gr.TextArea(label="📝 Neuer Inhalt", placeholder="Neuer Inhalt hier eingeben...", lines=6)
723
- update_btn = gr.Button("✏️ Datei aktualisieren", variant="secondary")
724
-
725
- with gr.Column():
726
- update_output = gr.Textbox(label="Update-Ergebnis", lines=6)
727
-
728
- def update_file_wrapper(user_uuid, file_path, content):
729
- if not user_uuid or not file_path or not content:
730
- return "❌ Bitte füllen Sie alle Felder aus!"
731
-
732
- content_bytes = content.encode('utf-8')
733
- result = hf_secrets_storage.update_file(user_uuid, file_path, content_bytes)
734
-
735
- if result['success']:
736
- return f"""✅ Datei aktualisiert!
737
-
738
- 📁 Pfad: {result['file_path']}
739
- 🔄 Version: {result['version']}
740
- 🆔 UUID: {result['file_uuid']}"""
741
- else:
742
- return f"❌ Fehler: {result.get('error', 'Unbekannter Fehler')}"
743
-
744
- update_btn.click(update_file_wrapper, inputs=[update_user_uuid, update_file_path, update_content], outputs=update_output)
745
-
746
- with gr.Tab("🗑️ Datei löschen"):
747
- with gr.Row():
748
- with gr.Column():
749
- gr.Markdown("### 🗑️ Datei endgültig löschen")
750
- delete_user_uuid = gr.Textbox(label="🆔 Ihre UUID", placeholder="Ihre UUID eingeben")
751
- delete_file_path = gr.Textbox(label="📁 Dateipfad", placeholder="z.B. meine-dokumente/test.txt")
752
- delete_btn = gr.Button("🗑️ Datei löschen", variant="stop")
753
-
754
- with gr.Column():
755
- delete_output = gr.Textbox(label="Lösch-Ergebnis", lines=4)
756
-
757
- def delete_file_wrapper(user_uuid, file_path):
758
- if not user_uuid or not file_path:
759
- return "❌ Bitte geben Sie UUID und Dateipfad ein!"
760
-
761
- result = hf_secrets_storage.delete_file(user_uuid, file_path)
762
-
763
- if result['success']:
764
- return f"""🗑️ Datei gelöscht!
765
-
766
- 📁 Pfad: {result['file_path']}
767
- ☁️ Aus dem {hf_secrets_storage.storage_mode} entfernt"""
768
- else:
769
- return f"❌ Fehler: {result.get('error', 'Unbekannter Fehler')}"
770
-
771
- delete_btn.click(delete_file_wrapper, inputs=[delete_user_uuid, delete_file_path], outputs=delete_output)
772
-
773
  with gr.Tab("📋 Meine Dateien"):
774
  with gr.Row():
775
  with gr.Column():
@@ -781,78 +702,100 @@ with gr.Blocks(title="🤗 HF Secrets Filestorage MCP", theme=gr.themes.Soft())
781
  with gr.Column():
782
  list_output = gr.Textbox(label="Datei-Liste", lines=15, max_lines=20)
783
 
784
- list_btn.click(list_files_ui, inputs=[list_user_uuid, list_folder], outputs=list_output)
785
 
786
- with gr.Tab("ℹ️ Space-Info"):
787
  with gr.Row():
788
  with gr.Column():
789
- gr.Markdown("### ℹ️ Informationen über diesen Space")
790
- info_btn = gr.Button("ℹ️ Space-Details anzeigen", variant="secondary")
 
791
 
792
  with gr.Column():
793
- info_output = gr.Textbox(label="Space-Informationen", lines=10)
794
 
795
- def get_space_info():
796
- dataset_info = hf_secrets_storage.dataset_name if hf_secrets_storage.dataset_name != "mcp-filestorage/default-files" else "Standard-Dataset"
797
- token_info = " Konfiguriert" if hf_secrets_storage.hf_token else "⚠️ Nicht konfiguriert"
798
-
799
- return f"""🤗 Hugging Face Dataset Filestorage MCP Server
 
 
 
800
 
801
- 🗃️ Dataset: {dataset_info}
802
- 🔐 HF Token: {token_info}
803
- ☁️ Speicher-Modus: {hf_secrets_storage.storage_mode}
804
- 🚀 MCP Endpoint: /gradio_api/mcp/sse
805
- 🛡️ Space Secrets: Für sichere Konfiguration
806
 
807
- 🔧 Um Secrets zu konfigurieren:
808
- 1. Gehe zu Space Settings → Secrets
809
- 2. Füge hinzu:
810
- - HF_TOKEN: Dein Hugging Face Token
811
- - DATASET_NAME: Organisation/dataset-name"""
 
 
 
 
 
 
 
812
 
813
- info_btn.click(get_space_info, outputs=info_output)
814
 
815
  gr.Markdown("""
816
  ---
817
- ### 🚀 MCP Server Integration
818
 
819
- **MCP Endpoint:** `/gradio_api/mcp/sse`
820
 
821
  **Verfügbare MCP Tools:**
822
- - 📝 `register_user_secret` - Neuen Benutzer registrieren
823
- - 📤 `create_user_file_secret` - Datei für Benutzer erstellen
824
- - 📖 `read_user_file_secret` - Datei lesen und Download-Link erhalten
825
- - ✏️ `update_user_file_secret` - Datei-Inhalt aktualisieren
826
- - 🗑️ `delete_user_file_secret` - Datei löschen
827
- - 📋 `list_user_files_secret` - Alle Dateien eines Benutzers auflisten
828
- - 📊 `get_user_stats_secret` - Benutzer-Statistiken abrufen
829
-
830
- **Konfiguration für MCP Clients:**
831
  ```json
832
  {
833
  "mcpServers": {
834
- "hf-secrets-filestorage": {
835
- "url": "https://dein-space-name.hf.space/gradio_api/mcp/sse"
836
  }
837
  }
838
  }
839
  ```
840
 
841
- ### 🔧 Space Secrets Konfiguration
842
-
843
- Um diesen Space vollständig zu nutzen, konfiguriere diese Secrets:
844
- - `HF_TOKEN`: Dein Hugging Face Token für Dataset-Zugriff
845
- - `DATASET_NAME`: Name des Zieldatasets (Format: "organisation/dataset-name")
846
- - `ORG_NAME`: (Optional) Organisation name
847
-
848
- Ohne Secrets funktioniert der Space im lokalen Speicher-Modus.
849
  """)
850
 
851
- # MCP Server aktivieren
 
 
 
 
 
 
 
 
 
 
 
 
 
852
  if __name__ == "__main__":
 
853
  demo.launch(
854
  mcp_server=True,
855
  server_name="0.0.0.0",
856
  server_port=7860,
857
- share=False # Wird von Hugging Face Space verwaltet
 
 
 
 
 
 
 
 
858
  )
 
11
  from pathlib import Path
12
  import requests
13
  from io import BytesIO
14
+ from flask import Flask, request, jsonify
15
+ from flask_cors import CORS
16
+ import threading
17
 
18
+ class HFUUIDSSEFileStorageMCP:
19
  def __init__(self):
20
  # Lade Secrets aus Space Environment
21
  self.hf_token = os.environ.get("HF_TOKEN", "")
 
23
  self.org_name = os.environ.get("ORG_NAME", "mcp-filestorage")
24
  self.db_path = "user_management.db"
25
 
 
 
 
 
 
 
26
  self.api = HfApi(token=self.hf_token if self.hf_token else None)
27
  self.init_database()
28
  self.init_storage()
29
+ self.user_sessions = {} # Speichert aktive Benutzer-Sessions
30
+
31
  def init_database(self):
32
  """Initialisiert SQLite-Datenbank für Benutzerverwaltung"""
33
  conn = sqlite3.connect(self.db_path)
34
  cursor = conn.cursor()
35
 
36
+ # Benutzer-Tabelle mit SSE-Endpunkt
37
  cursor.execute('''
38
  CREATE TABLE IF NOT EXISTS users (
39
  id INTEGER PRIMARY KEY AUTOINCREMENT,
40
  user_uuid TEXT UNIQUE NOT NULL,
41
+ sse_endpoint TEXT UNIQUE,
42
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
43
  last_access DATETIME DEFAULT CURRENT_TIMESTAMP,
 
44
  file_count INTEGER DEFAULT 0,
45
  is_active BOOLEAN DEFAULT 1,
46
  storage_type TEXT DEFAULT 'hf_dataset'
 
74
  """Initialisiert Speicher (Dataset oder lokal)"""
75
  try:
76
  if self.dataset_name and self.dataset_name != "mcp-filestorage/default-files":
 
77
  self.dataset = load_dataset(self.dataset_name, split='train', token=self.hf_token)
78
  print(f"✅ Dataset geladen: {self.dataset_name}")
79
  self.storage_mode = 'dataset'
80
  else:
 
81
  self.storage_mode = 'local'
82
  self.local_storage_path = Path("user_storage")
83
  self.local_storage_path.mkdir(exist_ok=True)
 
89
  self.local_storage_path.mkdir(exist_ok=True)
90
 
91
  def register_user(self) -> Dict[str, Any]:
92
+ """Registriert neuen Benutzer mit UUID und erstellt SSE-Endpunkt"""
93
  user_uuid = str(uuid.uuid4())
94
+ sse_endpoint = f"user-{user_uuid[:8]}" # Kurze UUID für Endpoint
95
 
96
  conn = sqlite3.connect(self.db_path)
97
  cursor = conn.cursor()
98
 
99
  try:
100
+ cursor.execute('''
101
+ INSERT INTO users (user_uuid, sse_endpoint) VALUES (?, ?)
102
+ ''', (user_uuid, sse_endpoint))
103
+
104
  conn.commit()
105
 
106
  # Erstelle Benutzerordner
107
  self._create_user_folder(user_uuid)
108
 
109
+ # Registriere Benutzer-Session
110
+ self.user_sessions[user_uuid] = {
111
+ 'sse_endpoint': sse_endpoint,
112
+ 'created_at': datetime.now(),
113
+ 'last_access': datetime.now()
114
+ }
115
+
116
  return {
117
  'success': True,
118
  'user_uuid': user_uuid,
119
+ 'sse_endpoint': sse_endpoint,
120
+ 'full_sse_url': f"/gradio_api/mcp/user/{user_uuid}/sse",
121
  'message': f'Benutzer {user_uuid} registriert'
122
  }
123
+
124
  except Exception as e:
125
  return {
126
  'success': False,
 
128
  }
129
  finally:
130
  conn.close()
131
+
132
+ def get_user_by_uuid(self, user_uuid: str) -> Optional[Dict[str, Any]]:
133
+ """Holt Benutzer-Informationen anhand der UUID"""
134
+ conn = sqlite3.connect(self.db_path)
135
+ cursor = conn.cursor()
136
+
137
+ try:
138
+ cursor.execute('''
139
+ SELECT user_uuid, sse_endpoint, created_at, file_count
140
+ FROM users WHERE user_uuid = ? AND is_active = 1
141
+ ''', (user_uuid,))
142
+
143
+ result = cursor.fetchone()
144
+ if result:
145
+ return {
146
+ 'user_uuid': result[0],
147
+ 'sse_endpoint': result[1],
148
+ 'created_at': result[2],
149
+ 'file_count': result[3]
150
+ }
151
+ return None
152
+
153
+ finally:
154
+ conn.close()
155
+
156
  def _create_user_folder(self, user_uuid: str):
157
  """Erstellt Benutzerordner je nach Speicher-Modus"""
158
  if self.storage_mode == 'dataset':
159
  try:
 
160
  folder_path = f"users/{user_uuid}"
161
  readme_content = f"# User Folder: {user_uuid}\nCreated: {datetime.now().isoformat()}\n"
162
 
 
177
  def create_file(self, user_uuid: str, file_path: str, content: bytes,
178
  metadata: Optional[Dict] = None) -> Dict[str, Any]:
179
  """Erstellt neue Datei für Benutzer"""
180
+ # Verifiziere Benutzer
181
+ user = self.get_user_by_uuid(user_uuid)
182
+ if not user:
183
+ return {'success': False, 'error': 'Ungültige Benutzer-UUID'}
184
+
185
  file_uuid = str(uuid.uuid4())
186
  file_hash = hashlib.sha256(content).hexdigest()
187
 
 
189
  cursor = conn.cursor()
190
 
191
  try:
 
192
  storage_path = f"users/{user_uuid}/{file_path}"
193
 
194
+ # Speichere Datei
195
  if self.storage_mode == 'dataset':
196
  try:
197
  self.api.upload_file(
 
224
 
225
  conn.commit()
226
 
227
+ # Aktualisiere Session
228
+ if user_uuid in self.user_sessions:
229
+ self.user_sessions[user_uuid]['last_access'] = datetime.now()
230
+
231
  return {
232
  'success': True,
233
  'file_uuid': file_uuid,
234
  'file_path': file_path,
235
+ 'user_uuid': user_uuid
 
236
  }
237
 
238
  except Exception as e:
239
+ return {'success': False, 'error': str(e)}
 
 
 
240
  finally:
241
  conn.close()
242
+
243
  def read_file(self, user_uuid: str, file_path: str) -> Dict[str, Any]:
244
  """Liest Datei eines Benutzers"""
245
+ # Verifiziere Benutzer
246
+ user = self.get_user_by_uuid(user_uuid)
247
+ if not user:
248
+ return {'success': False, 'error': 'Ungültige Benutzer-UUID'}
249
+
250
  conn = sqlite3.connect(self.db_path)
251
  cursor = conn.cursor()
252
 
 
262
 
263
  file_uuid, filename, storage_path = result
264
 
265
+ # Erstelle Download-URL
266
  if self.storage_mode == 'dataset':
267
  file_url = f"https://huggingface.co/datasets/{self.dataset_name}/resolve/main/{storage_path}"
268
  else:
269
+ # Lokaler Speicher
270
  local_file_path = self.local_storage_path / user_uuid / file_path
271
  if local_file_path.exists():
272
  import base64
 
275
  else:
276
  return {'success': False, 'error': 'Datei nicht im lokalen Speicher gefunden'}
277
 
278
+ # Aktualisiere Session
279
+ if user_uuid in self.user_sessions:
280
+ self.user_sessions[user_uuid]['last_access'] = datetime.now()
 
281
 
282
  return {
283
  'success': True,
 
285
  'file_path': file_path,
286
  'file_url': file_url,
287
  'filename': filename,
288
+ 'user_uuid': user_uuid
289
  }
290
 
291
  except Exception as e:
292
  return {'success': False, 'error': str(e)}
293
  finally:
294
  conn.close()
295
+
296
  def update_file(self, user_uuid: str, file_path: str, content: bytes) -> Dict[str, Any]:
297
  """Aktualisiert vorhandene Datei"""
298
+ user = self.get_user_by_uuid(user_uuid)
299
+ if not user:
300
+ return {'success': False, 'error': 'Ungültige Benutzer-UUID'}
301
+
302
  conn = sqlite3.connect(self.db_path)
303
  cursor = conn.cursor()
304
 
 
344
 
345
  conn.commit()
346
 
347
+ # Aktualisiere Session
348
+ if user_uuid in self.user_sessions:
349
+ self.user_sessions[user_uuid]['last_access'] = datetime.now()
350
+
351
  return {
352
  'success': True,
353
  'file_uuid': file_uuid,
354
  'version': current_version + 1,
355
+ 'file_path': file_path,
356
+ 'user_uuid': user_uuid
357
  }
358
 
359
  except Exception as e:
360
  return {'success': False, 'error': str(e)}
361
  finally:
362
  conn.close()
363
+
364
  def delete_file(self, user_uuid: str, file_path: str) -> Dict[str, Any]:
365
  """Löscht Datei (Soft Delete)"""
366
+ user = self.get_user_by_uuid(user_uuid)
367
+ if not user:
368
+ return {'success': False, 'error': 'Ungültige Benutzer-UUID'}
369
+
370
  conn = sqlite3.connect(self.db_path)
371
  cursor = conn.cursor()
372
 
 
388
 
389
  conn.commit()
390
 
391
+ # Aktualisiere Session
392
+ if user_uuid in self.user_sessions:
393
+ self.user_sessions[user_uuid]['last_access'] = datetime.now()
394
+
395
  return {
396
  'success': True,
397
  'message': f'Datei {file_path} gelöscht',
398
+ 'file_path': file_path,
399
+ 'user_uuid': user_uuid
400
  }
401
 
402
  except Exception as e:
403
  return {'success': False, 'error': str(e)}
404
  finally:
405
  conn.close()
406
+
407
  def list_files(self, user_uuid: str, folder_path: str = "") -> List[Dict[str, Any]]:
408
  """Listet alle Dateien eines Benutzers auf"""
409
+ user = self.get_user_by_uuid(user_uuid)
410
+ if not user:
411
+ return []
412
+
413
  conn = sqlite3.connect(self.db_path)
414
  cursor = conn.cursor()
415
 
 
443
  'file_size': row[6]
444
  })
445
 
446
+ # Aktualisiere Session
447
+ if user_uuid in self.user_sessions:
448
+ self.user_sessions[user_uuid]['last_access'] = datetime.now()
449
 
450
+ return files
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
451
 
452
  finally:
453
  conn.close()
454
 
455
+ def get_user_sse_endpoint(self, user_uuid: str) -> Optional[str]:
456
+ """Gibt den SSE-Endpunkt für einen Benutzer zurück"""
457
+ user = self.get_user_by_uuid(user_uuid)
458
+ if user:
459
+ return f"/gradio_api/mcp/user/{user_uuid}/sse"
460
+ return None
461
+
462
  # Globale Instanz
463
+ hf_uuid_sse_storage = HFUUIDSSEFileStorageMCP()
464
 
465
+ # ===== BENUTZERSPEZIFISCHE MCP TOOLS =====
466
 
467
  @gr.mcp.tool()
468
+ def register_user_uuid_sse() -> str:
469
  """
470
+ Registriert einen neuen Benutzer und erstellt einen persönlichen SSE-Endpunkt.
471
 
472
  Returns:
473
+ Benutzer-UUID und persönlicher SSE-Endpunkt
474
  """
475
+ result = hf_uuid_sse_storage.register_user()
476
 
477
  if result['success']:
478
  return f"""✅ Benutzer erfolgreich registriert!
479
+
480
  🆔 Ihre UUID: {result['user_uuid']}
481
+ 🌐 Ihr persönlicher SSE-Endpunkt: {result['full_sse_url']}
482
+ 📁 Ihr Ordner: users/{result['user_uuid']}/
483
+ 🔐 Speichern Sie diese UUID! Sie ist Ihr persönlicher Zugangsschlüssel!
484
+ 🚀 Verwenden Sie Ihren SSE-Endpunkt für MCP-Integration!"""
485
  else:
486
  return f"❌ Fehler: {result.get('error', 'Unbekannter Fehler')}"
487
 
488
  @gr.mcp.tool()
489
+ def create_user_file_uuid(user_uuid: str, file_path: str, content: str,
490
+ metadata: Optional[str] = None) -> str:
491
  """
492
  Erstellt eine neue Datei für einen Benutzer.
493
 
 
503
  content_bytes = content.encode('utf-8')
504
  meta_dict = json.loads(metadata) if metadata else None
505
 
506
+ result = hf_uuid_sse_storage.create_file(user_uuid, file_path, content_bytes, meta_dict)
507
 
508
  if result['success']:
509
  return f"""✅ Datei erfolgreich erstellt!
510
  📁 Datei-UUID: {result['file_uuid']}
511
  📄 Pfad: {result['file_path']}
512
+ 👤 Benutzer: {result['user_uuid']}
513
+ 🌐 Ihr SSE-Endpunkt: /gradio_api/mcp/user/{user_uuid}/sse"""
514
  else:
515
  return f"❌ Fehler: {result.get('error', 'Unbekannter Fehler')}"
516
 
517
  @gr.mcp.tool()
518
+ def read_user_file_uuid(user_uuid: str, file_path: str) -> str:
519
  """
520
  Liest eine Datei eines Benutzers.
521
 
 
526
  Returns:
527
  Datei-Details und Zugriffs-URL
528
  """
529
+ result = hf_uuid_sse_storage.read_file(user_uuid, file_path)
530
 
531
  if result['success']:
532
  return f"""📄 Datei erfolgreich gelesen!
533
  🆔 Datei-UUID: {result['file_uuid']}
534
  🔗 Direkt-Link: {result['file_url']}
535
  📁 Dateiname: {result['filename']}
536
+ 👤 Benutzer: {result['user_uuid']}
537
+ 🌐 Ihr SSE-Endpunkt: /gradio_api/mcp/user/{user_uuid}/sse"""
538
  else:
539
  return f"❌ Fehler: {result.get('error', 'Datei nicht gefunden')}"
540
 
541
  @gr.mcp.tool()
542
+ def update_user_file_uuid(user_uuid: str, file_path: str, content: str) -> str:
543
  """
544
  Aktualisiert eine vorhandene Datei.
545
 
 
552
  Erfolgsmeldung mit Versions-Info
553
  """
554
  content_bytes = content.encode('utf-8')
555
+ result = hf_uuid_sse_storage.update_file(user_uuid, file_path, content_bytes)
556
 
557
  if result['success']:
558
  return f"""✅ Datei erfolgreich aktualisiert!
559
  🆔 Datei-UUID: {result['file_uuid']}
560
  🔢 Neue Version: {result['version']}
561
  📁 Pfad: {result['file_path']}
562
+ 👤 Benutzer: {result['user_uuid']}"""
563
  else:
564
  return f"❌ Fehler: {result.get('error', 'Unbekannter Fehler')}"
565
 
566
  @gr.mcp.tool()
567
+ def delete_user_file_uuid(user_uuid: str, file_path: str) -> str:
568
  """
569
  Löscht eine Datei eines Benutzers.
570
 
 
575
  Returns:
576
  Erfolgsmeldung
577
  """
578
+ result = hf_uuid_sse_storage.delete_file(user_uuid, file_path)
579
 
580
  if result['success']:
581
  return f"""🗑️ Datei erfolgreich gelöscht!
582
  📁 Pfad: {result['file_path']}
583
+ 👤 Benutzer: {result['user_uuid']}
584
+ 🌐 Ihr SSE-Endpunkt bleibt: /gradio_api/mcp/user/{user_uuid}/sse"""
585
  else:
586
  return f"❌ Fehler: {result.get('error', 'Unbekannter Fehler')}"
587
 
588
  @gr.mcp.tool()
589
+ def list_user_files_uuid(user_uuid: str, folder_path: str = "") -> str:
590
  """
591
  Listet alle Dateien eines Benutzers auf.
592
 
 
597
  Returns:
598
  Liste aller Dateien des Benutzers
599
  """
600
+ files = hf_uuid_sse_storage.list_files(user_uuid, folder_path)
601
 
602
  if not files:
603
  return "📁 Keine Dateien gefunden!"
604
 
605
  output = f"📊 Gefunden: {len(files)} Dateien für Benutzer {user_uuid}\n"
606
+ output += f"🌐 Ihr SSE-Endpunkt: /gradio_api/mcp/user/{user_uuid}/sse\n\n"
607
 
608
  for file in files:
609
  output += f"""📄 {file['filename']} (Version {file['version']})
 
617
 
618
  return output
619
 
620
+ # ===== GRADIO INTERFACE MIT UUID-SSE INTEGRATION =====
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
621
 
622
+ # Benutzerdefinierte MCP-Routen erstellen
623
+ def create_user_mcp_server(user_uuid: str):
624
+ """Erstellt einen benutzerspezifischen MCP-Server"""
625
+
626
+ @gr.mcp.tool()
627
+ def user_create_file(file_path: str, content: str, metadata: Optional[str] = None) -> str:
628
+ """Erstelle Datei für aktuellen Benutzer"""
629
+ return create_user_file_uuid(user_uuid, file_path, content, metadata)
630
+
631
+ @gr.mcp.tool()
632
+ def user_read_file(file_path: str) -> str:
633
+ """Lese Datei für aktuellen Benutzer"""
634
+ return read_user_file_uuid(user_uuid, file_path)
635
+
636
+ @gr.mcp.tool()
637
+ def user_update_file(file_path: str, content: str) -> str:
638
+ """Aktualisiere Datei für aktuellen Benutzer"""
639
+ return update_user_file_uuid(user_uuid, file_path, content)
640
+
641
+ @gr.mcp.tool()
642
+ def user_delete_file(file_path: str) -> str:
643
+ """Lösche Datei für aktuellen Benutzer"""
644
+ return delete_user_file_uuid(user_uuid, file_path)
645
+
646
+ @gr.mcp.tool()
647
+ def user_list_files(folder_path: str = "") -> str:
648
+ """Liste Dateien für aktuellen Benutzer auf"""
649
+ return list_user_files_uuid(user_uuid, folder_path)
650
+
651
+ @gr.mcp.tool()
652
+ def user_get_stats() -> str:
653
+ """Hole Statistiken für aktuellen Benutzer"""
654
+ return get_user_stats_uuid(user_uuid)
655
 
656
+ # Haupt-Gradio-Interface
657
+ with gr.Blocks(title="🤗 HF UUID-SSE Filestorage MCP", theme=gr.themes.Soft()) as demo:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
658
  gr.Markdown("""
659
  # 🗃️ Hugging Face Dataset Filestorage MCP Server
660
+ ### 🔗 UUID-basierte persönliche SSE-Endpunkte
661
 
662
+ Jeder Benutzer bekommt einen eigenen SSE-Endpunkt mit seiner UUID - z.B. `/gradio_api/mcp/user/123e4567-e89b-12d3-a456-426614174000/sse`
 
663
  """)
664
 
665
  with gr.Tab("📝 Registrieren"):
666
  with gr.Row():
667
  with gr.Column():
668
+ gr.Markdown("### 🎯 Neuen Benutzer mit persönlichem SSE-Endpunkt erstellen")
669
  register_btn = gr.Button("🎯 Neuen Benutzer erstellen", variant="primary", size="lg")
670
  with gr.Column():
671
+ register_output = gr.Textbox(label="Registrierungsergebnis", lines=8, max_lines=10)
672
 
673
+ register_btn.click(register_user_uuid_sse, outputs=register_output)
674
 
675
  with gr.Tab("📤 Datei erstellen"):
676
  with gr.Row():
 
686
  create_output = gr.Textbox(label="Ergebnis", lines=8, max_lines=10)
687
 
688
  create_btn.click(
689
+ create_user_file_uuid,
690
  inputs=[create_user_uuid, create_file_path, create_content, create_metadata],
691
  outputs=create_output
692
  )
693
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
694
  with gr.Tab("📋 Meine Dateien"):
695
  with gr.Row():
696
  with gr.Column():
 
702
  with gr.Column():
703
  list_output = gr.Textbox(label="Datei-Liste", lines=15, max_lines=20)
704
 
705
+ list_btn.click(list_user_files_uuid, inputs=[list_user_uuid, list_folder], outputs=list_output)
706
 
707
+ with gr.Tab("ℹ️ Mein SSE-Endpunkt"):
708
  with gr.Row():
709
  with gr.Column():
710
+ gr.Markdown("### 🔗 Meinen persönlichen SSE-Endpunkt finden")
711
+ sse_user_uuid = gr.Textbox(label="🆔 Ihre UUID", placeholder="Ihre UUID eingeben")
712
+ sse_btn = gr.Button("🔗 SSE-Endpunkt anzeigen", variant="secondary")
713
 
714
  with gr.Column():
715
+ sse_output = gr.Textbox(label="SSE-Endpunkt-Details", lines=8)
716
 
717
+ def get_sse_endpoint(user_uuid):
718
+ if not user_uuid:
719
+ return " Bitte geben Sie Ihre UUID ein!"
720
+
721
+ endpoint = hf_uuid_sse_storage.get_user_sse_endpoint(user_uuid)
722
+ if endpoint:
723
+ full_url = f"https://dein-space-name.hf.space{endpoint}"
724
+ return f"""🔗 Ihr persönlicher SSE-Endpunkt:
725
 
726
+ 🌐 Komplett: {full_url}
727
+ 📍 Kurz: {endpoint}
 
 
 
728
 
729
+ 📝 MCP-Konfiguration:
730
+ ```json
731
+ {{
732
+ "mcpServers": {{
733
+ "mein-filestorage-{user_uuid[:8]}": {{
734
+ "url": "{full_url}"
735
+ }}
736
+ }}
737
+ }}
738
+ ```"""
739
+ else:
740
+ return "❌ Kein SSE-Endpunkt für diese UUID gefunden. Bitte registrieren Sie sich zuerst."
741
 
742
+ sse_btn.click(get_sse_endpoint, inputs=[sse_user_uuid], outputs=sse_output)
743
 
744
  gr.Markdown("""
745
  ---
746
+ ### 🚀 UUID-basierte MCP Server Integration
747
 
748
+ **Persönliche SSE-Endpunkte:** `/gradio_api/mcp/user/{Ihre-UUID}/sse`
749
 
750
  **Verfügbare MCP Tools:**
751
+ - 📝 `register_user_uuid_sse` - Neuen Benutzer mit SSE-Endpunkt registrieren
752
+ - 📤 `create_user_file_uuid` - Datei für Benutzer erstellen
753
+ - 📖 `read_user_file_uuid` - Datei lesen und Download-Link erhalten
754
+ - ✏️ `update_user_file_uuid` - Datei-Inhalt aktualisieren
755
+ - 🗑️ `delete_user_file_uuid` - Datei löschen
756
+ - 📋 `list_user_files_uuid` - Alle Dateien eines Benutzers auflisten
757
+
758
+ **Beispiel MCP Konfiguration für Benutzer:**
 
759
  ```json
760
  {
761
  "mcpServers": {
762
+ "mein-filestorage-123e4567": {
763
+ "url": "https://dein-space-name.hf.space/gradio_api/mcp/user/123e4567-e89b-12d3-a456-426614174000/sse"
764
  }
765
  }
766
  }
767
  ```
768
 
769
+ 🔐 **Jeder Benutzer hat seinen eigenen MCP-Server-Endpunkt!**
 
 
 
 
 
 
 
770
  """)
771
 
772
+ # Benutzerdefinierte SSE-Routen handler
773
+ def user_sse_handler(user_uuid: str):
774
+ """Handler für benutzerspezifische SSE-Endpunkte"""
775
+ user = hf_uuid_sse_storage.get_user_by_uuid(user_uuid)
776
+ if not user:
777
+ return {"error": "Ungültige Benutzer-UUID"}, 404
778
+
779
+ # Erstelle benutzerspezifischen MCP-Server
780
+ create_user_mcp_server(user_uuid)
781
+
782
+ # Hier würde die SSE-Verbindung etabliert
783
+ return {"status": "connected", "user_uuid": user_uuid, "endpoint": f"/user/{user_uuid}/sse"}
784
+
785
+ # MCP Server mit benutzerdefinierten Routen
786
  if __name__ == "__main__":
787
+ # Standard-MCP-Server für allgemeine Funktionen
788
  demo.launch(
789
  mcp_server=True,
790
  server_name="0.0.0.0",
791
  server_port=7860,
792
+ share=False,
793
+ # Benutzerdefinierte Routen werden über Gradio's MCP-Integration gehandhabt
794
+ routes=[
795
+ {
796
+ "path": "/gradio_api/mcp/user/{user_uuid}/sse",
797
+ "method": "GET",
798
+ "handler": user_sse_handler
799
+ }
800
+ ]
801
  )