| import gradio as gr |
| import sqlite3 |
| import requests |
| import json |
| import os |
| from datetime import datetime |
| import hashlib |
| from typing import Optional, List, Dict |
| from pathlib import Path |
| import tempfile |
| import shutil |
|
|
| class FileStorageMCP: |
| def __init__(self, db_path: str = "filestorage.db"): |
| self.db_path = db_path |
| self.anonymfile_api = "https://api.anonymfile.com/upload" |
| self.init_database() |
| |
| def init_database(self): |
| """Initialisiert die SQLite-Datenbank""" |
| conn = sqlite3.connect(self.db_path) |
| cursor = conn.cursor() |
| |
| cursor.execute(''' |
| CREATE TABLE IF NOT EXISTS files ( |
| id INTEGER PRIMARY KEY AUTOINCREMENT, |
| filename TEXT NOT NULL, |
| file_hash TEXT UNIQUE, |
| anonymfile_url TEXT, |
| anonymfile_id TEXT, |
| upload_timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, |
| file_size INTEGER, |
| mime_type TEXT, |
| download_count INTEGER DEFAULT 0, |
| metadata TEXT, |
| space_compatible BOOLEAN DEFAULT 1 |
| ) |
| ''') |
| |
| cursor.execute(''' |
| CREATE TABLE IF NOT EXISTS file_tags ( |
| id INTEGER PRIMARY KEY AUTOINCREMENT, |
| file_id INTEGER, |
| tag TEXT, |
| FOREIGN KEY (file_id) REFERENCES files(id) |
| ) |
| ''') |
| |
| conn.commit() |
| conn.close() |
| |
| def calculate_file_hash(self, file_content: bytes) -> str: |
| """Berechnet SHA256 Hash von Datei-Inhalt""" |
| return hashlib.sha256(file_content).hexdigest() |
| |
| def upload_to_anonymfile(self, file_content: bytes, filename: str) -> Dict: |
| """Lädt Datei zu anonymfile.com hoch""" |
| try: |
| files = {'file': (filename, file_content)} |
| response = requests.post(self.anonymfile_api, files=files) |
| |
| if response.status_code == 200: |
| data = response.json() |
| if data.get('status'): |
| return { |
| 'success': True, |
| 'url': data['data']['file']['url']['full'], |
| 'id': data['data']['file']['metadata']['id'], |
| 'size': data['data']['file']['metadata']['size']['readable'] |
| } |
| return {'success': False, 'error': 'Upload fehlgeschlagen'} |
| except Exception as e: |
| return {'success': False, 'error': str(e)} |
| |
| def add_file_to_database(self, filename: str, file_hash: str, |
| anonymfile_url: str, anonymfile_id: str, |
| file_size: int, mime_type: str) -> int: |
| """Fügt Datei zur Datenbank hinzu""" |
| conn = sqlite3.connect(self.db_path) |
| cursor = conn.cursor() |
| |
| try: |
| cursor.execute(''' |
| INSERT INTO files (filename, file_hash, anonymfile_url, |
| anonymfile_id, file_size, mime_type) |
| VALUES (?, ?, ?, ?, ?, ?) |
| ''', (filename, file_hash, anonymfile_url, anonymfile_id, file_size, mime_type)) |
| |
| file_id = cursor.lastrowid |
| conn.commit() |
| return file_id |
| except sqlite3.IntegrityError: |
| |
| cursor.execute('SELECT id FROM files WHERE file_hash = ?', (file_hash,)) |
| return cursor.fetchone()[0] |
| finally: |
| conn.close() |
| |
| def get_file_info(self, file_id: int) -> Optional[Dict]: |
| """Holt Datei-Informationen aus der Datenbank""" |
| conn = sqlite3.connect(self.db_path) |
| cursor = conn.cursor() |
| |
| cursor.execute(''' |
| SELECT f.*, GROUP_CONCAT(t.tag) as tags |
| FROM files f |
| LEFT JOIN file_tags t ON f.id = t.file_id |
| WHERE f.id = ? |
| GROUP BY f.id |
| ''', (file_id,)) |
| |
| result = cursor.fetchone() |
| conn.close() |
| |
| if result: |
| columns = [desc[0] for desc in cursor.description] |
| return dict(zip(columns, result)) |
| return None |
| |
| def search_files(self, query: str, tag: Optional[str] = None) -> List[Dict]: |
| """Sucht nach Dateien""" |
| conn = sqlite3.connect(self.db_path) |
| cursor = conn.cursor() |
| |
| sql = ''' |
| SELECT f.*, GROUP_CONCAT(t.tag) as tags |
| FROM files f |
| LEFT JOIN file_tags t ON f.id = t.file_id |
| WHERE 1=1 |
| ''' |
| params = [] |
| |
| if query: |
| sql += ' AND (f.filename LIKE ? OR f.metadata LIKE ?)' |
| params.extend([f'%{query}%', f'%{query}%']) |
| |
| if tag: |
| sql += ' AND f.id IN (SELECT file_id FROM file_tags WHERE tag = ?)' |
| params.append(tag) |
| |
| sql += ' GROUP BY f.id ORDER BY f.upload_timestamp DESC' |
| |
| cursor.execute(sql, params) |
| results = cursor.fetchall() |
| conn.close() |
| |
| files = [] |
| columns = [desc[0] for desc in cursor.description] |
| for row in results: |
| files.append(dict(zip(columns, row))) |
| |
| return files |
| |
| def add_tag(self, file_id: int, tag: str): |
| """Fügt Datei ein Tag hinzu""" |
| conn = sqlite3.connect(self.db_path) |
| cursor = conn.cursor() |
| |
| cursor.execute('INSERT INTO file_tags (file_id, tag) VALUES (?, ?)', |
| (file_id, tag)) |
| |
| conn.commit() |
| conn.close() |
| |
| def increment_download_count(self, file_id: int): |
| """Erhöht Download-Zähler""" |
| conn = sqlite3.connect(self.db_path) |
| cursor = conn.cursor() |
| |
| cursor.execute('UPDATE files SET download_count = download_count + 1 WHERE id = ?', |
| (file_id,)) |
| |
| conn.commit() |
| conn.close() |
|
|
| |
| file_storage = FileStorageMCP() |
|
|
| |
| def upload_file_space(file_obj, tags: str = "", description: str = "") -> str: |
| """ |
| Lädt eine Datei zu anonymfile.com hoch und speichert sie in der lokalen Datenbank. |
| Speziell für Hugging Face Space optimiert. |
| |
| Args: |
| file_obj: Gradio File Objekt |
| tags: Komma-getrennte Tags für die Datei |
| description: Beschreibung der Datei |
| |
| Returns: |
| Erfolgsmeldung mit Datei-Details |
| """ |
| if file_obj is None: |
| return "❌ Keine Datei ausgewählt!" |
| |
| try: |
| |
| if hasattr(file_obj, 'name'): |
| with open(file_obj.name, 'rb') as f: |
| file_content = f.read() |
| filename = os.path.basename(file_obj.name) |
| else: |
| return "❌ Ungültige Datei!" |
| |
| |
| file_hash = file_storage.calculate_file_hash(file_content) |
| file_size = len(file_content) |
| |
| |
| mime_type = getattr(file_obj, 'type', 'application/octet-stream') |
| |
| |
| existing_files = file_storage.search_files("", "") |
| for existing in existing_files: |
| if existing['file_hash'] == file_hash: |
| return f"ℹ️ Datei bereits hochgeladen! ID: {existing['id']}, URL: {existing['anonymfile_url']}" |
| |
| |
| result = file_storage.upload_to_anonymfile(file_content, filename) |
| |
| if not result['success']: |
| return f"❌ Upload-Fehler: {result.get('error', 'Unbekannter Fehler')}" |
| |
| |
| file_id = file_storage.add_file_to_database( |
| filename=filename, |
| file_hash=file_hash, |
| anonymfile_url=result['url'], |
| anonymfile_id=result['id'], |
| file_size=file_size, |
| mime_type=mime_type |
| ) |
| |
| |
| if tags: |
| for tag in tags.split(","): |
| tag = tag.strip() |
| if tag: |
| file_storage.add_tag(file_id, tag) |
| |
| return f"""✅ Datei erfolgreich hochgeladen! |
| 📁 ID: {file_id} |
| 🔗 URL: {result['url']} |
| 📊 Größe: {result['size']} |
| 🏷️ Tags: {tags if tags else 'Keine'} |
| 🚀 Hugging Face Space Ready""" |
| |
| except Exception as e: |
| return f"❌ Fehler beim Upload: {str(e)}" |
|
|
| def get_file_details_space(file_id: int) -> str: |
| """ |
| Ruft Details zu einer hochgeladenen Datei ab. |
| |
| Args: |
| file_id: Die ID der Datei in der Datenbank |
| |
| Returns: |
| Detaillierte Informationen zur Datei |
| """ |
| file_info = file_storage.get_file_info(file_id) |
| |
| if not file_info: |
| return f"❌ Datei mit ID {file_id} nicht gefunden!" |
| |
| return f"""📄 Datei-Details für ID {file_id}: |
| 📁 Name: {file_info['filename']} |
| 🔗 URL: {file_info['anonymfile_url']} |
| 📊 Größe: {file_info['file_size']} Bytes |
| 🎯 Typ: {file_info['mime_type']} |
| 📅 Hochgeladen: {file_info['upload_timestamp']} |
| ⬇️ Downloads: {file_info['download_count']} |
| 🏷️ Tags: {file_info.get('tags', 'Keine')} |
| 🤗 Hugging Face Space""" |
|
|
| def search_storage_space(query: str = "", tag: str = "") -> str: |
| """ |
| Durchsucht den Filestorage nach Dateien. |
| |
| Args: |
| query: Suchbegriff (durchsucht Dateinamen und Metadaten) |
| tag: Tag nach dem gefiltert werden soll |
| |
| Returns: |
| Liste der gefundenen Dateien |
| """ |
| results = file_storage.search_files(query, tag if tag else None) |
| |
| if not results: |
| return "🔍 Keine Dateien gefunden!" |
| |
| output = f"📊 Gefunden: {len(results)} Dateien in Hugging Face Space\n\n" |
| |
| for file in results: |
| output += f"""📁 {file['filename']} (ID: {file['id']}) |
| 🔗 {file['anonymfile_url']} |
| 📅 {file['upload_timestamp']} |
| ⬇️ Downloads: {file['download_count']} |
| 🏷️ Tags: {file.get('tags', 'Keine')} |
| {'-' * 50} |
| """ |
| |
| return output |
|
|
| def list_all_files_space() -> str: |
| """ |
| Listet alle hochgeladenen Dateien auf. |
| |
| Returns: |
| Liste aller Dateien |
| """ |
| return search_storage_space("", "") |
|
|
| def get_space_info() -> str: |
| """ |
| Gibt Informationen über den Hugging Face Space zurück. |
| |
| Returns: |
| Space-Informationen |
| """ |
| total_files = len(file_storage.search_files("", "")) |
| return f"""🤗 Hugging Face Filestorage MCP Server |
| 📊 Gesamtdateien: {total_files} |
| 🔗 MCP Endpoint: /gradio_api/mcp/sse |
| 🚀 Space-Version: 1.0.0 |
| 💾 Datenbank: SQLite |
| ☁️ Storage: anonymfile.com (keine Ablaufzeit)""" |
|
|
| |
| with gr.Blocks(title="🤗 Filestorage MCP Server", theme=gr.themes.Soft()) as demo: |
| gr.Markdown(""" |
| # 🗃️ Filestorage MCP Server |
| ### 🤗 Hugging Face Space Edition |
| |
| Ein vollständiger Filestorage-Server mit anonymfile.com Integration und MCP-Unterstützung. |
| **Keine Ablaufzeit** - Dateien bleiben für immer gespeichert! |
| """) |
| |
| with gr.Tab("📤 Upload"): |
| with gr.Row(): |
| with gr.Column(): |
| file_input = gr.File( |
| label="Datei auswählen", |
| file_types=["*"], |
| file_count="single" |
| ) |
| tags_input = gr.Textbox( |
| label="Tags (optional)", |
| placeholder="dokument, wichtig, 2024", |
| info="Komma-getrennte Tags" |
| ) |
| description_input = gr.Textbox( |
| label="Beschreibung (optional)", |
| placeholder="Kurze Beschreibung...", |
| lines=2 |
| ) |
| upload_btn = gr.Button("📤 Hochladen", variant="primary") |
| |
| with gr.Column(): |
| upload_output = gr.Textbox( |
| label="Upload-Ergebnis", |
| lines=10, |
| max_lines=15 |
| ) |
| |
| upload_btn.click( |
| upload_file_space, |
| inputs=[file_input, tags_input, description_input], |
| outputs=upload_output |
| ) |
| |
| with gr.Tab("🔍 Suche"): |
| with gr.Row(): |
| with gr.Column(): |
| search_query = gr.Textbox( |
| label="Suchbegriff", |
| placeholder="Dateiname oder Schlüsselwort" |
| ) |
| search_tag = gr.Textbox( |
| label="Tag-Filter", |
| placeholder="z.B. 'dokument'" |
| ) |
| search_btn = gr.Button("🔍 Suchen", variant="secondary") |
| |
| with gr.Column(): |
| search_output = gr.Textbox( |
| label="Suchergebnisse", |
| lines=15, |
| max_lines=20 |
| ) |
| |
| search_btn.click( |
| search_storage_space, |
| inputs=[search_query, search_tag], |
| outputs=search_output |
| ) |
| |
| with gr.Tab("📄 Details"): |
| with gr.Row(): |
| with gr.Column(): |
| file_id_input = gr.Number( |
| label="Datei-ID", |
| value=1, |
| precision=0 |
| ) |
| details_btn = gr.Button("📋 Details anzeigen", variant="secondary") |
| |
| with gr.Column(): |
| details_output = gr.Textbox( |
| label="Datei-Details", |
| lines=10, |
| max_lines=15 |
| ) |
| |
| details_btn.click( |
| get_file_details_space, |
| inputs=[file_id_input], |
| outputs=details_output |
| ) |
| |
| with gr.Tab("📋 Alle Dateien"): |
| all_files_btn = gr.Button("🗂️ Alle Dateien anzeigen", variant="secondary") |
| all_files_output = gr.Textbox( |
| label="Alle Dateien", |
| lines=20, |
| max_lines=25 |
| ) |
| |
| all_files_btn.click( |
| list_all_files_space, |
| outputs=all_files_output |
| ) |
| |
| with gr.Tab("ℹ️ Info"): |
| info_btn = gr.Button("🤗 Space-Info anzeigen", variant="secondary") |
| info_output = gr.Textbox( |
| label="Space-Informationen", |
| lines=8 |
| ) |
| |
| info_btn.click( |
| get_space_info, |
| outputs=info_output |
| ) |
| |
| gr.Markdown(""" |
| --- |
| ### 🚀 MCP Server Integration |
| |
| **MCP Endpoint:** `/gradio_api/mcp/sse` |
| |
| Verfügbare MCP Tools: |
| - 📤 `upload_file_space` - Dateien hochladen |
| - 📄 `get_file_details_space` - Datei-Details abrufen |
| - 🔍 `search_storage_space` - Im Filestorage suchen |
| - 📋 `list_all_files_space` - Alle Dateien auflisten |
| |
| ### 🔧 Konfiguration für MCP Clients: |
| ```json |
| { |
| "mcpServers": { |
| "filestorage-space": { |
| "url": "https://dein-space-name.hf.space/gradio_api/mcp/sse" |
| } |
| } |
| } |
| ``` |
| """) |
|
|
| |
| if __name__ == "__main__": |
| demo.launch( |
| mcp_server=True, |
| server_name="0.0.0.0", |
| server_port=7860, |
| share=False |
| ) |