Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions pages/_includes/lucien-servers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const lucienSpecifiedServers = [
'materials-project',
'web-fetch'
];

109 changes: 109 additions & 0 deletions pages/registry/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@
flex-direction: column;
border: 1px solid var(--border);
cursor: pointer;
position: relative;
}

.server-card:hover {
Expand Down Expand Up @@ -392,6 +393,35 @@
margin-top: auto;
margin-bottom: 0;
}

.install-button {
background-color: var(--button-bg);
color: white;
border: none;
border-radius: 4px;
padding: 6px 12px;
font-size: 0.8rem;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
transition: background-color 0.2s;
text-decoration: none;
width: fit-content;
position: absolute;
bottom: 10px;
right: 1.25rem;
z-index: 10;
}

.install-button:hover {
background-color: var(--button-hover);
}

.install-button svg {
width: 14px;
height: 14px;
}

.server-card .meta .author {
color: var(--accent-highlight);
Expand Down Expand Up @@ -1473,13 +1503,48 @@ <h3>Raw Manifest:</h3>
</div>
</div>

<!-- Load Lucien servers configuration -->

<script>
{% include lucien-servers.js %}
// Server data - to be loaded from the combined JSON file (as a map with server names as keys)
let serversMap = {};
let serversArray = []; // Also keep an array version for rendering

// Fetch all servers data
const serverDataUrl = '/api/servers.json';
const searchParams = new URLSearchParams(window.location.search);
const sortRule = {
'isPinned': 0,
'isOfficial': 1,
'others': 2,
}
const isLucien = searchParams.get('source') === 'lucien'
const sourceProtocol = searchParams.get('protocol') || 'lucien'

// Use the Lucien specified servers from the external JS file
const defaultSpecifiedServers = []
if (isLucien) {
// Get servers based on whether we're in staging or production
defaultSpecifiedServers.push(...lucienSpecifiedServers);
}

function serverSortRule(serverA, serverB) {

function determineServerType(server) {
if (defaultSpecifiedServers.includes(server.name)) {
return 'isPinned';
}
if (server.is_official) {
return 'isOfficial';
}
return 'others';
}

const serverAType = determineServerType(serverA);
const serverBType = determineServerType(serverB);
return sortRule[serverAType] - sortRule[serverBType];
}

// First load GitHub star counts, then fetch server data
loadGitHubStarCounts()
Expand All @@ -1493,6 +1558,8 @@ <h3>Raw Manifest:</h3>
.then(data => {
serversMap = data;
serversArray = Object.values(serversMap);
// sort servers by sortRule
serversArray.sort(serverSortRule);

// Generate tag filters dynamically from server data
generateTagFilters(serversArray);
Expand Down Expand Up @@ -2006,6 +2073,48 @@ <h3>Raw Manifest:</h3>
// Use the star count element created in the header

serverContent.appendChild(meta);

// Add install button with click function for deeplink
if (isLucien) {
const installButton = document.createElement('button');
installButton.className = 'install-button';
installButton.type = 'button';

// Add download icon
installButton.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="7 10 12 15 17 10"></polyline>
<line x1="12" y1="15" x2="12" y2="3"></line>
</svg>
Install
`;
}

// Handle click event to build search params and trigger deeplink
installButton.addEventListener('click', (event) => {
// Prevent the modal from opening
event.stopPropagation();

// Build search params with server install info
const params = new URLSearchParams();
params.append('name', server.name);
params.append('action', 'install');

const installInfo = Object.values(server.installations)[0];
// No remote for now, only stdio
params.append('command', installInfo.command);
params.append('args', encodeURIComponent(installInfo.args.join(' ')));
if (installInfo.env) {
params.append('env', JSON.stringify(installInfo.env));
}

// Create and trigger the deeplink
const deeplink = `${sourceProtocol}://mcpm?${params.toString()}`;
Copy link

Copilot AI Jun 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use encodeURIComponent on parameter values (e.g. params.append('args', encodeURIComponent(installInfo.args.join(' ')))) to ensure special characters don’t break the deep link URL.

Copilot uses AI. Check for mistakes.
window.location.href = deeplink;
});

serverContent.appendChild(installButton);

const linksContainer = document.createElement('div');
linksContainer.className = 'server-links';
Expand Down