Skip to main content
Glama
cbcoutinho

Nextcloud MCP Server

by cbcoutinho
user_info.html33.2 kB
{% extends "base.html" %} {% block title %}Nextcloud MCP Server{% endblock %} {% block extra_head %} <!-- htmx for dynamic loading --> <script src="https://unpkg.com/htmx.org@1.9.10"></script> <!-- Alpine.js for state management --> <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script> <!-- Plotly.js for vector visualization --> <script src="https://cdn.plot.ly/plotly-3.3.0.min.js"></script> <!-- Vector Viz static assets --> <link rel="stylesheet" href="/app/static/vector-viz.css"> {% endblock %} {% block extra_styles %} /* Smooth htmx transitions */ .htmx-swapping { opacity: 0; transition: opacity 200ms ease-out; } .htmx-settling { opacity: 1; transition: opacity 200ms ease-in; } /* Logout button styling */ .logout-section { margin-top: 20px; padding-top: 20px; border-top: 1px solid var(--color-border); } /* Welcome tab specific styles */ .hero-section { background: linear-gradient(135deg, var(--color-primary-element) 0%, #0082c9 100%); color: white; padding: 60px 24px; margin: -24px -24px 40px -24px; border-radius: 0 0 var(--border-radius-large) var(--border-radius-large); text-align: center; } .hero-section h1 { color: white; font-size: 36px; margin: 0 0 16px 0; font-weight: 600; } .hero-section p { font-size: 18px; opacity: 0.95; max-width: 700px; margin: 0 auto; line-height: 1.6; } .feature-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 24px; margin: 32px 0; } .feature-card { background: var(--color-main-background); border: 2px solid var(--color-border); border-radius: var(--border-radius-large); padding: 24px; transition: all 0.2s; cursor: pointer; text-decoration: none; color: inherit; display: block; } .feature-card:hover { border-color: var(--color-primary-element); box-shadow: 0 4px 12px rgba(0, 103, 158, 0.15); transform: translateY(-2px); } .feature-card h3 { color: var(--color-primary-element); font-size: 20px; margin: 12px 0 8px 0; font-weight: 600; display: flex; align-items: center; gap: 12px; } .feature-card p { color: var(--color-text-maxcontrast); font-size: 14px; line-height: 1.6; margin: 8px 0 0 0; } .feature-icon { width: 48px; height: 48px; background: var(--color-primary-element-light); border-radius: var(--border-radius); display: flex; align-items: center; justify-content: center; margin-bottom: 8px; } .feature-icon svg { width: 28px; height: 28px; fill: var(--color-primary-element); } .info-section { background: var(--color-background-hover); border-radius: var(--border-radius-large); padding: 32px; margin: 32px 0; } .info-section h2 { color: var(--color-main-text); font-size: 24px; margin: 0 0 16px 0; border: none; padding: 0; } .info-section p { color: var(--color-text-maxcontrast); line-height: 1.7; margin: 12px 0; } .info-section ul { margin: 12px 0; padding-left: 24px; } .info-section li { color: var(--color-text-maxcontrast); line-height: 1.7; margin: 8px 0; } .info-section code { background: var(--color-main-background); padding: 2px 8px; border-radius: var(--border-radius); font-size: 13px; } .auth-status { background: var(--color-primary-element-light); border-left: 4px solid var(--color-primary-element); padding: 16px 20px; margin: 24px 0; border-radius: var(--border-radius); display: flex; align-items: center; gap: 12px; } .auth-status svg { width: 24px; height: 24px; fill: var(--color-primary-element); flex-shrink: 0; } .auth-status-text { flex: 1; } .auth-status-text strong { display: block; color: var(--color-main-text); font-size: 14px; margin-bottom: 4px; } .auth-status-text span { color: var(--color-text-maxcontrast); font-size: 13px; } {% endblock %} {% block content %} <div class="app-content-wrapper" x-data="{ activeSection: 'welcome', navOpen: true }"> <!-- Side Navigation --> <nav id="app-navigation" :class="{ 'app-navigation--closed': !navOpen }"> <div class="app-navigation__content"> <!-- Navigation List --> <ul class="app-navigation-list"> <li class="app-navigation-entry" :class="{ 'active': activeSection === 'welcome' }"> <div class="app-navigation-entry__wrapper"> <a href="#" @click.prevent="activeSection = 'welcome'" class="app-navigation-entry-link"> <span class="app-navigation-entry-icon"> <svg class="nav-icon" viewBox="0 0 24 24"> <path d="M10,20V14H14V20H19V12H22L12,3L2,12H5V20H10Z" /> </svg> </span> <span class="app-navigation-entry__name">Welcome</span> </a> </div> </li> <li class="app-navigation-entry" :class="{ 'active': activeSection === 'user-info' }"> <div class="app-navigation-entry__wrapper"> <a href="#" @click.prevent="activeSection = 'user-info'" class="app-navigation-entry-link"> <span class="app-navigation-entry-icon"> <svg class="nav-icon" viewBox="0 0 24 24"> <path d="M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z" /> </svg> </span> <span class="app-navigation-entry__name">User Info</span> </a> </div> </li> {% if show_vector_sync_tab %} <li class="app-navigation-entry" :class="{ 'active': activeSection === 'vector-sync' }"> <div class="app-navigation-entry__wrapper"> <a href="#" @click.prevent="activeSection = 'vector-sync'" class="app-navigation-entry-link"> <span class="app-navigation-entry-icon"> <svg class="nav-icon" viewBox="0 0 24 24"> <path d="M12,18A6,6 0 0,1 6,12C6,11 6.25,10.03 6.7,9.2L5.24,7.74C4.46,8.97 4,10.43 4,12A8,8 0 0,0 12,20V23L16,19L12,15M12,4V1L8,5L12,9V6A6,6 0 0,1 18,12C18,13 17.75,13.97 17.3,14.8L18.76,16.26C19.54,15.03 20,13.57 20,12A8,8 0 0,0 12,4Z" /> </svg> </span> <span class="app-navigation-entry__name">Vector Sync</span> </a> </div> </li> <li class="app-navigation-entry" :class="{ 'active': activeSection === 'vector-viz' }"> <div class="app-navigation-entry__wrapper"> <a href="#" @click.prevent="activeSection = 'vector-viz'" class="app-navigation-entry-link"> <span class="app-navigation-entry-icon"> <svg class="nav-icon" viewBox="0 0 24 24"> <path d="M22,21H2V3H4V19H6V10H10V19H12V6H16V19H18V14H22V21Z" /> </svg> </span> <span class="app-navigation-entry__name">Vector Viz</span> </a> </div> </li> {% endif %} {% if show_webhooks_tab %} <li class="app-navigation-entry" :class="{ 'active': activeSection === 'webhooks' }"> <div class="app-navigation-entry__wrapper"> <a href="#" @click.prevent="activeSection = 'webhooks'" class="app-navigation-entry-link"> <span class="app-navigation-entry-icon"> <svg class="nav-icon" viewBox="0 0 24 24"> <path d="M10.59,13.41C11,13.8 11,14.44 10.59,14.83C10.2,15.22 9.56,15.22 9.17,14.83C7.22,12.88 7.22,9.71 9.17,7.76V7.76L12.71,4.22C14.66,2.27 17.83,2.27 19.78,4.22C21.73,6.17 21.73,9.34 19.78,11.29L18.29,12.78C18.3,11.96 18.17,11.14 17.89,10.36L18.36,9.88C19.54,8.71 19.54,6.81 18.36,5.64C17.19,4.46 15.29,4.46 14.12,5.64L10.59,9.17C9.41,10.34 9.41,12.24 10.59,13.41M13.41,9.17C13.8,8.78 14.44,8.78 14.83,9.17C16.78,11.12 16.78,14.29 14.83,16.24V16.24L11.29,19.78C9.34,21.73 6.17,21.73 4.22,19.78C2.27,17.83 2.27,14.66 4.22,12.71L5.71,11.22C5.7,12.04 5.83,12.86 6.11,13.65L5.64,14.12C4.46,15.29 4.46,17.19 5.64,18.36C6.81,19.54 8.71,19.54 9.88,18.36L13.41,14.83C14.59,13.66 14.59,11.76 13.41,10.59C13,10.2 13,9.56 13.41,9.17Z" /> </svg> </span> <span class="app-navigation-entry__name">Webhooks</span> </a> </div> </li> {% endif %} </ul> <!-- Settings/Logout at bottom --> {% if logout_url %} <ul class="app-navigation__settings"> <li class="app-navigation-entry"> <div class="app-navigation-entry__wrapper"> <a href="{{ logout_url }}" class="app-navigation-entry-link"> <span class="app-navigation-entry-icon"> <svg class="nav-icon" viewBox="0 0 24 24"> <path d="M16,17V14H9V10H16V7L21,12L16,17M14,2A2,2 0 0,1 16,4V6H14V4H5V20H14V18H16V20A2,2 0 0,1 14,22H5A2,2 0 0,1 3,20V4A2,2 0 0,1 5,2H14Z" /> </svg> </span> <span class="app-navigation-entry__name">Logout</span> </a> </div> </li> </ul> {% endif %} </div> <!-- Toggle Button (mobile) --> <button @click="navOpen = !navOpen" class="app-navigation-toggle" :aria-expanded="navOpen.toString()"> ☰ </button> </nav> <!-- Main Content Area --> <main id="app-content"> <div class="page-content"> <!-- Welcome Section --> <div x-show="activeSection === 'welcome'"> <!-- Hero Section --> <div class="hero-section"> <h1>Welcome to Nextcloud MCP Server</h1> <p> Interactive user interface for semantic search and document retrieval. Test queries, visualize results, and explore your Nextcloud content using RAG workflows. </p> </div> <!-- Authentication Status --> <div class="auth-status"> <svg viewBox="0 0 24 24"> <path d="M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z" /> </svg> <div class="auth-status-text"> <strong>Authenticated as: {{ username }}</strong> <span>Authentication mode: <code>{{ auth_mode }}</code></span> </div> </div> {% if vector_sync_enabled %} <!-- Vector Sync Enabled Content --> <div class="info-section"> <h2>About Semantic Search</h2> <p> This interface provides access to <strong>semantic search</strong> capabilities powered by vector embeddings. Unlike traditional keyword search, semantic search understands the <em>meaning</em> of your queries and finds conceptually similar content across your Nextcloud apps. </p> <p> <strong>How it works:</strong> </p> <ul> <li>Documents from Notes, Calendar, Files, Contacts, and Deck are indexed into a vector database</li> <li>Each document chunk is converted to a 768-dimensional vector embedding that captures semantic meaning</li> <li>Queries are also converted to embeddings and matched against document vectors using similarity search</li> <li>Results can be retrieved using pure semantic search or hybrid BM25 search combining keywords and semantics</li> </ul> </div> <div class="info-section"> <h2>RAG Workflow Integration</h2> <p> This UI allows you to <strong>test the same queries that Large Language Models (LLMs) would use</strong> in a Retrieval-Augmented Generation (RAG) workflow. When an AI assistant needs to answer questions about your data: </p> <ul> <li><strong>Step 1:</strong> The assistant converts your question into a search query</li> <li><strong>Step 2:</strong> The MCP server retrieves relevant document chunks using semantic search</li> <li><strong>Step 3:</strong> Retrieved context is passed to the LLM to generate an informed answer</li> </ul> <!-- RAG Workflow Diagram --> <div style="background: var(--color-main-background); border: 2px solid var(--color-primary-element); border-radius: var(--border-radius-large); padding: 24px; margin: 24px 0; overflow-x: auto;"> <div style="text-align: center; font-weight: 600; margin-bottom: 20px; color: var(--color-primary-element); font-size: 16px;"> MCP Sampling RAG Workflow </div> <!-- Four-component bidirectional flow --> <div style="max-width: 1000px; margin: 0 auto;"> <div style="display: grid; grid-template-columns: 0.7fr auto 1fr auto 1fr auto 0.9fr; gap: 10px; align-items: center;"> <!-- User --> <div style="background: var(--color-background-hover); border: 2px solid var(--color-border); border-radius: var(--border-radius-large); padding: 14px; text-align: center;"> <div style="font-size: 26px; margin-bottom: 5px;">👤</div> <div style="font-weight: 600; color: var(--color-main-text); font-size: 12px;">User</div> <div style="font-size: 9px; color: var(--color-text-maxcontrast); font-style: italic; margin-top: 5px; line-height: 1.2;"> "What are health<br>benefits of coffee?" </div> </div> <!-- Arrow User <-> Client --> <div style="text-align: center;"> <div style="font-size: 20px; color: var(--color-text-maxcontrast);">↔</div> </div> <!-- MCP Client + LLM (combined) --> <div style="background: var(--color-primary-element-light); border: 2px solid var(--color-primary-element); border-radius: var(--border-radius-large); padding: 12px; text-align: center;"> <div style="font-weight: 600; color: var(--color-primary-element); font-size: 13px; margin-bottom: 8px;">MCP Client + LLM</div> <div style="background: var(--color-main-background); border-radius: var(--border-radius); padding: 8px; margin-bottom: 6px;"> <div style="font-size: 9px; color: var(--color-text-maxcontrast);">(Claude Code)</div> </div> <div style="background: var(--color-main-background); border-radius: var(--border-radius); padding: 8px; border: 2px solid var(--color-primary-element);"> <div style="font-size: 16px; margin-bottom: 2px;">🧠</div> <div style="font-weight: 600; color: var(--color-main-text); font-size: 10px;">Client's LLM</div> <div style="font-size: 8px; color: var(--color-text-maxcontrast);">(Claude)</div> </div> <div style="margin-top: 8px; font-size: 8px; color: var(--color-text-maxcontrast); line-height: 1.2;"> <strong>Enables RAG:</strong><br> Receives context,<br> generates answer </div> </div> <!-- Arrow Client <-> Server --> <div style="text-align: center;"> <div style="font-size: 20px; color: var(--color-primary-element);">↔</div> <div style="font-size: 7px; color: var(--color-text-maxcontrast); margin-top: 2px; font-weight: 600; line-height: 1.1;"> Query +<br> Sampling </div> </div> <!-- MCP Server --> <div style="background: var(--color-primary-element-light); border: 2px solid var(--color-primary-element); border-radius: var(--border-radius-large); padding: 12px; text-align: center;"> <div style="font-weight: 600; color: var(--color-primary-element); font-size: 13px; margin-bottom: 8px;">MCP Server</div> <div style="background: var(--color-main-background); border-radius: var(--border-radius); padding: 7px; margin-bottom: 5px;"> <div style="font-weight: 600; color: var(--color-main-text); font-size: 9px; margin-bottom: 2px;">1. Semantic Search</div> <div style="font-size: 7px; color: var(--color-text-maxcontrast); line-height: 1.2;"> Vector embeddings<br> BM25 Hybrid + RRF </div> </div> <div style="background: var(--color-main-background); border-radius: var(--border-radius); padding: 7px; margin-bottom: 5px;"> <div style="font-weight: 600; color: var(--color-main-text); font-size: 9px; margin-bottom: 2px;">2. Retrieve Context</div> <div style="font-size: 7px; color: var(--color-text-maxcontrast); line-height: 1.2;"> Top relevant docs<br> with scores </div> </div> <div style="background: var(--color-main-background); border-radius: var(--border-radius); padding: 7px; margin-bottom: 5px;"> <div style="font-weight: 600; color: var(--color-main-text); font-size: 9px; margin-bottom: 2px;">3. Format Response</div> <div style="font-size: 7px; color: var(--color-text-maxcontrast); line-height: 1.2;"> Document chunks<br> with citations </div> </div> <div style="background: var(--color-main-background); border-radius: var(--border-radius); padding: 7px;"> <div style="font-weight: 600; color: var(--color-main-text); font-size: 9px; margin-bottom: 2px;">4. Send to LLM</div> <div style="font-size: 7px; color: var(--color-text-maxcontrast); line-height: 1.2;"> Via MCP sampling<br> for answer generation </div> </div> </div> <!-- Arrow Server <-> Nextcloud --> <div style="text-align: center;"> <div style="font-size: 20px; color: var(--color-primary-element);">↔</div> <div style="font-size: 7px; color: var(--color-text-maxcontrast); margin-top: 2px; font-weight: 600; line-height: 1.1;"> Retrieve </div> </div> <!-- Nextcloud --> <div style="background: var(--color-background-hover); border: 2px solid var(--color-border); border-radius: var(--border-radius-large); padding: 12px; text-align: center; position: relative;"> <img src="/app/static/nextcloud-logo.png" alt="Nextcloud" style="width: 40px; height: 40px; margin-bottom: 6px;" /> <div style="font-weight: 600; color: var(--color-main-text); font-size: 12px; margin-bottom: 4px;">Nextcloud</div> <div style="font-size: 8px; color: var(--color-text-maxcontrast); line-height: 1.2;"> Notes, Calendar,<br> Files, Contacts,<br> Deck </div> </div> </div> <!-- Explanation below diagram --> <div style="margin-top: 24px; padding: 16px; background: var(--color-background-hover); border-radius: var(--border-radius); border-left: 4px solid var(--color-primary-element);"> <div style="font-size: 12px; color: var(--color-main-text); line-height: 1.6;"> <strong>How RAG works via MCP Sampling:</strong> </div> <ol style="margin: 8px 0 0 0; padding-left: 20px; font-size: 11px; color: var(--color-text-maxcontrast); line-height: 1.6;"> <li>User asks question through MCP Client</li> <li>Client sends query to MCP Server</li> <li>Server retrieves relevant document context from Nextcloud</li> <li><strong>Server sends context back to Client's LLM</strong> (MCP Sampling)</li> <li>Client's LLM generates answer with citations using retrieved context</li> <li>Answer returned to user</li> </ol> <div style="margin-top: 8px; font-size: 10px; color: var(--color-text-maxcontrast); font-style: italic;"> The server has no LLM - it only retrieves context. The client's existing LLM is reused for answer generation. </div> </div> </div> </div> <p style="margin-top: 16px;"> <strong>Key Point:</strong> The MCP server retrieves context but doesn't generate answers itself. Through <strong>MCP sampling</strong>, it requests the client's LLM to generate responses, giving users full control over which model is used and ensuring all processing happens client-side. </p> <p> By using this interface, you can preview search results, understand relevance scores, and verify that the system retrieves the right information before it reaches the LLM. </p> </div> <!-- Feature Cards --> <h2>Available Features</h2> <div class="feature-grid"> <a href="#" @click.prevent="activeSection = 'user-info'" class="feature-card"> <div class="feature-icon"> <svg viewBox="0 0 24 24"> <path d="M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z" /> </svg> </div> <h3>User Information</h3> <p> View your authentication details, session information, and IdP profile. Manage background access permissions. </p> </a> <a href="#" @click.prevent="activeSection = 'vector-sync'" class="feature-card"> <div class="feature-icon"> <svg viewBox="0 0 24 24"> <path d="M12,18A6,6 0 0,1 6,12C6,11 6.25,10.03 6.7,9.2L5.24,7.74C4.46,8.97 4,10.43 4,12A8,8 0 0,0 12,20V23L16,19L12,15M12,4V1L8,5L12,9V6A6,6 0 0,1 18,12C18,13 17.75,13.97 17.3,14.8L18.76,16.26C19.54,15.03 20,13.57 20,12A8,8 0 0,0 12,4Z" /> </svg> </div> <h3>Vector Sync Status</h3> <p> Monitor real-time indexing progress with metrics for indexed documents, pending queue, and synchronization status. </p> </a> <a href="#" @click.prevent="activeSection = 'vector-viz'" class="feature-card"> <div class="feature-icon"> <svg viewBox="0 0 24 24"> <path d="M22,21H2V3H4V19H6V10H10V19H12V6H16V19H18V14H22V21Z" /> </svg> </div> <h3>Vector Visualization</h3> <p> Interactive search interface with 2D PCA visualization. Compare algorithms, view relevance scores, and explore matched document chunks. </p> </a> </div> {% else %} <!-- Vector Sync Disabled Content --> <div class="warning"> <h3 style="margin-top: 0;">Vector Sync is Disabled</h3> <p> Semantic search and vector visualization features are currently disabled. To enable these features, set <code>VECTOR_SYNC_ENABLED=true</code> in your environment configuration. </p> <p style="margin-bottom: 0;"> <strong>Learn more:</strong> <a href="https://github.com/cbcoutinho/nextcloud-mcp-server/blob/master/docs/configuration.md" target="_blank" style="color: inherit; text-decoration: underline;"> Configuration Guide </a> </p> </div> <!-- Limited Feature Card --> <h2>Available Features</h2> <div class="feature-grid"> <a href="#" @click.prevent="activeSection = 'user-info'" class="feature-card"> <div class="feature-icon"> <svg viewBox="0 0 24 24"> <path d="M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z" /> </svg> </div> <h3>User Information</h3> <p> View your authentication details, session information, and IdP profile. Manage background access permissions. </p> </a> </div> {% endif %} <!-- Documentation Section --> <div class="info-section" style="margin-top: 40px;"> <h2>Documentation</h2> <p> For detailed information about configuration, authentication modes, and advanced features, please refer to the project documentation: </p> <ul> <li><a href="https://github.com/cbcoutinho/nextcloud-mcp-server/blob/master/docs/installation.md" target="_blank">Installation Guide</a></li> <li><a href="https://github.com/cbcoutinho/nextcloud-mcp-server/blob/master/docs/configuration.md" target="_blank">Configuration Options</a></li> <li><a href="https://github.com/cbcoutinho/nextcloud-mcp-server/blob/master/docs/authentication.md" target="_blank">Authentication Modes</a></li> {% if vector_sync_enabled %} <li><a href="https://github.com/cbcoutinho/nextcloud-mcp-server/blob/master/docs/user-guide/vector-sync-ui.md" target="_blank">Vector Sync UI Guide</a></li> {% endif %} </ul> </div> </div> <!-- User Info Section --> <div x-show="activeSection === 'user-info'"> <div class="content-section"> <h1>User Information</h1> {{ user_info_tab_html|safe }} </div> </div> {% if show_vector_sync_tab %} <!-- Vector Sync Section --> <div x-show="activeSection === 'vector-sync'"> <div class="content-section"> <h1>Vector Sync Status</h1> {{ vector_sync_tab_html|safe }} </div> </div> <!-- Vector Viz Section --> <div x-show="activeSection === 'vector-viz'"> <div class="content-section"> <h1>Vector Visualization</h1> <div hx-get="/app/vector-viz" hx-trigger="load" hx-swap="outerHTML"> <p style="color: #999;">Loading vector visualization...</p> </div> </div> </div> {% endif %} {% if show_webhooks_tab %} <!-- Webhooks Section --> <div x-show="activeSection === 'webhooks'"> <div class="content-section"> <h1>Webhook Management</h1> {{ webhooks_tab_html|safe }} </div> </div> {% endif %} </div> </main> </div> <script> // Set global Nextcloud base URL for use in external JS window.NEXTCLOUD_BASE_URL = '{{ nextcloud_host_for_links }}'; </script> <script src="/app/static/vector-viz.js"></script> {% endblock %}

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/cbcoutinho/nextcloud-mcp-server'

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