Skip to main content
Glama
web_server.py5.91 kB
#!/usr/bin/env python3 """ Flask-based Wiki Frontend for MCP Notes A simple web interface to view and navigate your notes. """ from flask import Flask, render_template, request, redirect, url_for from datetime import datetime from pathlib import Path import json import re from collections import defaultdict # Reuse the storage path from server.py NOTES_FILE = Path(__file__).parent / "notes.json" app = Flask(__name__) def load_notes(): """Load notes from JSON file.""" if not NOTES_FILE.exists(): return {"tag_schema": {}, "notes": []} with open(NOTES_FILE, 'r', encoding='utf-8') as f: return json.load(f) def find_wikilinks(content): """Extract [[wiki links]] from content.""" pattern = r'\[\[([^\]]+)\]\]' return re.findall(pattern, content) def render_wikilinks(content, all_notes): """Convert [[Note Title]] to clickable links.""" # Create a map of titles to IDs title_to_id = {note['title']: note['id'] for note in all_notes} def replace_link(match): title = match.group(1) if title in title_to_id: note_id = title_to_id[title] return f'<a href="/note/{note_id}" class="wikilink">{title}</a>' else: return f'<span class="wikilink-missing">{title}</span>' pattern = r'\[\[([^\]]+)\]\]' return re.sub(pattern, replace_link, content) def get_backlinks(note_id, all_notes): """Find all notes that link to this note.""" target_note = next((n for n in all_notes if n['id'] == note_id), None) if not target_note: return [] target_title = target_note['title'] backlinks = [] for note in all_notes: if note['id'] != note_id: links = find_wikilinks(note['content']) if target_title in links: backlinks.append(note) return backlinks def get_tag_counts(notes): """Count usage of each tag.""" counts = { 'category': defaultdict(int), 'type': defaultdict(int), 'priority': defaultdict(int), 'topics': defaultdict(int) } for note in notes: tags = note.get('tags', {}) if 'category' in tags: counts['category'][tags['category']] += 1 if 'type' in tags: counts['type'][tags['type']] += 1 if 'priority' in tags: counts['priority'][tags['priority']] += 1 for topic in tags.get('topics', []): counts['topics'][topic] += 1 return counts @app.route('/') def index(): """Home page with recent notes and tag cloud.""" data = load_notes() notes = data['notes'] # Sort by most recently updated notes_sorted = sorted(notes, key=lambda n: n['updated'], reverse=True) tag_counts = get_tag_counts(notes) return render_template('index.html', notes=notes_sorted[:20], # Show 20 most recent tag_counts=tag_counts, total_notes=len(notes)) @app.route('/note/<note_id>') def view_note(note_id): """View a single note with backlinks.""" data = load_notes() notes = data['notes'] note = next((n for n in notes if n['id'] == note_id), None) if not note: return "Note not found", 404 # Convert markdown to HTML (basic implementation) import markdown content_html = markdown.markdown(note['content'], extensions=['fenced_code', 'tables']) # Add wikilinks content_html = render_wikilinks(content_html, notes) # Get backlinks backlinks = get_backlinks(note_id, notes) # Get outgoing links outgoing = find_wikilinks(note['content']) return render_template('note.html', note=note, content_html=content_html, backlinks=backlinks, outgoing_links=outgoing) @app.route('/tag/<dimension>/<value>') def view_tag(dimension, value): """View all notes with a specific tag.""" data = load_notes() notes = data['notes'] # Filter notes by tag filtered = [] for note in notes: tags = note.get('tags', {}) if dimension == 'topics': if value in tags.get('topics', []): filtered.append(note) elif tags.get(dimension) == value: filtered.append(note) # Sort by most recently updated filtered_sorted = sorted(filtered, key=lambda n: n['updated'], reverse=True) return render_template('tag.html', dimension=dimension, value=value, notes=filtered_sorted) @app.route('/search') def search(): """Search notes by title or content.""" query = request.args.get('q', '').lower() if not query: return redirect(url_for('index')) data = load_notes() notes = data['notes'] # Simple text search results = [] for note in notes: if (query in note['title'].lower() or query in note['content'].lower()): results.append(note) results_sorted = sorted(results, key=lambda n: n['updated'], reverse=True) return render_template('search.html', query=query, results=results_sorted) @app.route('/all') def all_notes(): """View all notes alphabetically.""" data = load_notes() notes = sorted(data['notes'], key=lambda n: n['title'].lower()) return render_template('all.html', notes=notes) @app.template_filter('datetime') def format_datetime(value): """Format ISO datetime for display.""" try: dt = datetime.fromisoformat(value.replace('Z', '+00:00')) return dt.strftime('%Y-%m-%d %H:%M') except: return value if __name__ == '__main__': print("Starting MCP Notes Wiki Frontend...") print("Visit http://localhost:5000") app.run(debug=True, port=5000)

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/StarrStack/mcp-notes'

If you have feedback or need assistance with the MCP directory API, please join our Discord server