We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/rsp2k/mcp-vultr'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
---
import Layout from '../layouts/Layout.astro';
---
<Layout title="Login - Service Collections" requireAuth={false}>
<div class="min-h-full flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
<div class="max-w-md w-full space-y-8">
<div>
<h2 class="mt-6 text-center text-3xl font-extrabold text-gray-900">
Sign in to your account
</h2>
<p class="mt-2 text-center text-sm text-gray-600">
Access your Service Collection dashboard
</p>
</div>
<!-- GitHub OAuth Section - FIRST -->
<div class="mt-8"
x-data="{
email: '',
password: '',
loading: false,
error: null,
async handleSubmit(e) {
e.preventDefault();
this.loading = true;
this.error = null;
const result = await $store.auth.login(this.email, this.password);
if (result.success) {
// Redirect to dashboard
window.location.href = '/dashboard';
} else {
this.error = result.error;
this.loading = false;
}
}
}">
<button type="button"
@click="async () => {
loading = true;
error = null;
try {
const response = await fetch('/api/auth/github/authorize', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({})
});
if (response.ok) {
const data = await response.json();
// Redirect to GitHub authorization URL
window.location.href = data.authorization_url;
} else {
const errorData = await response.json();
error = errorData.detail || 'Failed to start OAuth flow';
loading = false;
}
} catch (err) {
error = 'Network error occurred';
loading = false;
}
}"
style="background-color: #24292e; color: #ffffff; border: 2px solid #24292e; width: 100%; display: flex; justify-content: center; align-items: center; padding: 12px 16px; border-radius: 6px; box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); font-size: 14px; font-weight: 500; transition: background-color 0.2s;"
onmouseenter="this.style.backgroundColor='#1b1f23'"
onmouseleave="this.style.backgroundColor='#24292e'"
:disabled="loading">
<svg class="w-5 h-5 mr-2" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z"/>
</svg>
<span x-show="!loading">Continue with GitHub</span>
<span x-show="loading" class="flex items-center">
<svg class="animate-spin -ml-1 mr-2 h-4 w-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
Redirecting...
</span>
</button>
<!-- Separator -->
<div class="relative mt-6 mb-6">
<div class="absolute inset-0 flex items-center">
<div class="w-full border-t border-gray-300"></div>
</div>
<div class="relative flex justify-center text-sm">
<span class="px-2 bg-white text-gray-500">or</span>
</div>
</div>
<!-- Error message -->
<div x-show="error"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 transform scale-95"
x-transition:enter-end="opacity-100 transform scale-100"
class="rounded-md bg-red-50 p-4 mb-6">
<div class="flex">
<div class="flex-shrink-0">
<svg class="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" />
</svg>
</div>
<div class="ml-3">
<h3 class="text-sm font-medium text-red-800">
Authentication failed
</h3>
<div class="mt-2 text-sm text-red-700">
<p x-text="error"></p>
</div>
</div>
</div>
</div>
<form class="space-y-6" @submit="handleSubmit">
<div class="rounded-md shadow-sm -space-y-px">
<div>
<label for="email-address" class="sr-only">Email address</label>
<input id="email-address"
name="email"
type="email"
autocomplete="email"
required
class="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-primary-500 focus:border-primary-500 focus:z-10 sm:text-sm"
placeholder="Email address"
x-model="email">
</div>
<div>
<label for="password" class="sr-only">Password</label>
<input id="password"
name="password"
type="password"
autocomplete="current-password"
required
class="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-none focus:ring-primary-500 focus:border-primary-500 focus:z-10 sm:text-sm"
placeholder="Password"
x-model="password">
</div>
</div>
<div class="flex items-center justify-between">
<div class="flex items-center">
<input id="remember-me"
name="remember-me"
type="checkbox"
class="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded">
<label for="remember-me" class="ml-2 block text-sm text-gray-900">
Remember me
</label>
</div>
<div class="text-sm">
<a href="/forgot-password" class="font-medium text-primary-600 hover:text-primary-500">
Forgot your password?
</a>
</div>
</div>
<div>
<button type="submit"
class="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 disabled:opacity-50 disabled:cursor-not-allowed"
:disabled="loading">
<span class="absolute left-0 inset-y-0 flex items-center pl-3">
<!-- Simple lock icon -->
<svg class="h-5 w-5 text-primary-500 group-hover:text-primary-400"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
</svg>
</span>
<span x-show="!loading">Sign in</span>
<span x-show="loading" class="flex items-center">
<svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
Signing in...
</span>
</button>
</div>
<div class="text-center">
<span class="text-sm text-gray-500">
Don't have an account?
<a href="/register" class="font-medium text-primary-600 hover:text-primary-500">
Contact your administrator
</a>
</span>
</div>
</form>
</div>
</div>
</Layout>
<script>
// Handle OAuth token callback and check authentication immediately
(function() {
console.log('π Login page loaded, checking for OAuth token...');
// Prevent infinite redirect loops
const redirectCount = parseInt(sessionStorage.getItem('redirect_count') || '0');
if (redirectCount > 3) {
console.error('π« Too many redirects, stopping to prevent infinite loop');
sessionStorage.removeItem('redirect_count');
return;
}
// Extract token from URL parameters (OAuth callback)
const urlParams = new URLSearchParams(window.location.search);
const returnUrl = urlParams.get('return');
console.log('π Return URL:', returnUrl);
if (returnUrl) {
// Parse token from return URL if present
const returnParams = new URLSearchParams(returnUrl.split('?')[1] || '');
const token = returnParams.get('token');
console.log('π Token found:', token ? token.substring(0, 20) + '...' : 'None');
if (token) {
// Store the token and redirect to dashboard with token parameter
localStorage.setItem('auth_token', token);
console.log('β
OAuth token received and stored in localStorage');
sessionStorage.setItem('redirect_count', (redirectCount + 1).toString());
console.log('π Redirecting to dashboard with token...');
window.location.href = `/dashboard?token=${encodeURIComponent(token)}`;
return;
}
}
// Check if user is already authenticated
const existingToken = localStorage.getItem('auth_token');
if (existingToken) {
console.log('β
Found existing token in localStorage');
sessionStorage.setItem('redirect_count', (redirectCount + 1).toString());
console.log('π Redirecting to dashboard...');
window.location.href = '/dashboard';
} else {
console.log('βΉοΈ No token found, staying on login page');
}
})();
</script>