Skip to content

Add-on: getGitMCP a quick fetcher interface #74

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
8 changes: 8 additions & 0 deletions web-tools/get-git-mcp-ui/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
88 changes: 88 additions & 0 deletions web-tools/get-git-mcp-ui/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>getGitMCP - Paste GitHub URLs, get MCP Endpoints via GitMCP</title>
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap">
<link rel="icon" type="image/svg+xml" href="favicon.svg">
</head>
<body>
<div class="container">
<header>
<div class="logo">
<h1>getGitMCP</h1>
</div>
<nav>
<a href="https://gitmcp.io/docs" target="_blank">Docs</a>
<a href="https://github.com/idosal/git-mcp" target="_blank">GitHub</a>
</nav>
</header>

<main>
<div class="hero">
<h2>Paste GitHub URLs, get MCP Endpoints</h2>
<h4> powered by GitMCP</h4>
<br>
<p>GitMCP converts any GitHub project into a remote Model Context Protocol endpoint for your AI assistants to use.</p>
</div>

<div class="converter-box">
<div class="input-container">
<input
type="text"
id="github-url"
placeholder="Enter GitHub URL (repo, pages, or user/org)"
autocomplete="off"
spellcheck="false"
>
<button id="convert-btn">Convert</button>
</div>
<div class="examples">
<span>Examples:</span>
<code>github.com/username/repo</code>
<code>username.github.io/repo</code>
<code>github.com/username</code>
</div>
<div id="loading" class="loading hidden">
<div class="spinner"></div>
<span>Fetching repositories...</span>
</div>
<div id="error-message" class="error-message hidden"></div>
<div id="result" class="result hidden">
<h3>Converted URL:</h3>
<a id="result-link" href="#" target="_blank"></a>
</div>
<div id="repos-list" class="repos-list hidden">
<h3>Repositories:</h3>
<ul id="repos-container"></ul>
</div>
</div>
<div class="conversion-examples">
<div class="example-card">
<h3>GitHub Repository</h3>
<div class="example-conversion">
<code>github.com/username/repo</code>
<span class="arrow">→</span>
<code>gitmcp.io/username/repo</code>
</div>
</div>
<div class="example-card">
<h3>GitHub Pages</h3>
<div class="example-conversion">
<code>username.github.io/repo</code>
<span class="arrow">→</span>
<code>username.gitmcp.io/repo</code>
</div>
</div>
</div>
</main>
<footer>
<p>getGitMCP is a quick tool to retrive GitMCP configuration from Github links</p>.GitMCP is a free, open-source service. <a href="https://github.com/idosal/git-mcp" target="_blank">Contribute on GitHub</a></p>
</footer>
</div>

<script src="script.js"></script>
</body>
</html>
8 changes: 8 additions & 0 deletions web-tools/get-git-mcp-ui/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions web-tools/get-git-mcp-ui/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "my-v0-project",
"version": "0.1.0",
"private": true,
"scripts": {
"build": "echo 'no build script'"
}
}
207 changes: 207 additions & 0 deletions web-tools/get-git-mcp-ui/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
document.addEventListener("DOMContentLoaded", () => {
const githubUrlInput = document.getElementById("github-url")
const convertBtn = document.getElementById("convert-btn")
const loadingElement = document.getElementById("loading")
const errorMessageElement = document.getElementById("error-message")
const resultElement = document.getElementById("result")
const resultLinkElement = document.getElementById("result-link")
const reposListElement = document.getElementById("repos-list")
const reposContainerElement = document.getElementById("repos-container")

// Regular expressions for URL validation and parsing
const githubRepoRegex = /^(?:https?:\/\/)?(?:www\.)?github\.com\/([^/]+)\/([^/]+)(?:\/.*)?$/i
const githubPagesRegex = /^(?:https?:\/\/)?([^/]+)\.github\.io\/([^/]+)(?:\/.*)?$/i
const githubUserRegex = /^(?:https?:\/\/)?(?:www\.)?github\.com\/([^/]+)\/?$/i

// Handle input on Enter key
githubUrlInput.addEventListener("keydown", (e) => {
if (e.key === "Enter") {
processUrl()
}
})

// Handle paste event
githubUrlInput.addEventListener("paste", (e) => {
// Use setTimeout to allow the paste to complete before processing
setTimeout(() => {
const pastedUrl = githubUrlInput.value.trim()
if (pastedUrl) {
const sanitizedUrl = sanitizeUrl(pastedUrl)
githubUrlInput.value = sanitizedUrl
processUrl()
}
}, 0)
})

// Handle button click
convertBtn.addEventListener("click", processUrl)

// Sanitize URL by removing tracking parameters, fragments, and normalizing
function sanitizeUrl(url) {
try {
// Create URL object to easily manipulate parts
const urlObj = new URL(url)

// Remove common tracking parameters
const paramsToRemove = ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content", "ref", "source"]
paramsToRemove.forEach((param) => urlObj.searchParams.delete(param))

// For GitHub URLs, clean up paths
if (urlObj.hostname === "github.com" || urlObj.hostname.endsWith(".github.io")) {
const pathParts = urlObj.pathname.split("/")

// If it's a GitHub repo with additional paths (blob, tree, etc.)
if (pathParts.length > 3 && urlObj.hostname === "github.com") {
// Keep only username and repo name
urlObj.pathname = `/${pathParts[1]}/${pathParts[2]}`
} else if (pathParts.length > 2 && urlObj.hostname.endsWith(".github.io")) {
// For GitHub Pages, keep only the first path segment
urlObj.pathname = `/${pathParts[1]}`
}

// Remove hash
urlObj.hash = ""
}

// Return the cleaned URL without trailing slash
return urlObj.toString().replace(/\/$/, "")
} catch (e) {
// If URL parsing fails, return the original URL
return url
}
}

// Main function to process the URL
function processUrl() {
const url = githubUrlInput.value.trim()

// Reset UI state
hideAllElements()

if (!url) {
showError("Please enter a GitHub URL")
return
}

// Check if URL is a GitHub repository
const repoMatch = url.match(githubRepoRegex)
if (repoMatch) {
const [, owner, repo] = repoMatch
const gitMcpUrl = `https://gitmcp.io/${owner}/${repo}`
showResult(gitMcpUrl)
window.open(gitMcpUrl, "_blank")
return
}

// Check if URL is a GitHub Pages site
const pagesMatch = url.match(githubPagesRegex)
if (pagesMatch) {
const [, owner, repo] = pagesMatch
const gitMcpUrl = `https://${owner}.gitmcp.io/${repo}`
showResult(gitMcpUrl)
window.open(gitMcpUrl, "_blank")
return
}

// Check if URL is a GitHub user or organization
const userMatch = url.match(githubUserRegex)
if (userMatch) {
const [, owner] = userMatch
fetchUserRepositories(owner)
return
}

// If none of the patterns match, just keep the sanitized URL in the input
// No error message needed as per requirements
}

// Fetch repositories for a GitHub user or organization
async function fetchUserRepositories(owner) {
showLoading()

try {
const response = await fetch(`https://api.github.com/users/${owner}/repos?sort=updated&per_page=100`)

if (!response.ok) {
if (response.status === 404) {
showError(`User or organization "${owner}" not found.`)
} else {
showError(`Error fetching repositories: ${response.statusText}`)
}
return
}

const repos = await response.json()

if (repos.length === 0) {
showError(`No public repositories found for "${owner}".`)
return
}

displayRepositories(owner, repos)
} catch (error) {
showError(`Error: ${error.message}`)
} finally {
hideLoading()
}
}

// Display the list of repositories
function displayRepositories(owner, repos) {
reposContainerElement.innerHTML = ""

repos.forEach((repo) => {
const li = document.createElement("li")
const a = document.createElement("a")
a.href = `https://gitmcp.io/${owner}/${repo.name}`
a.textContent = repo.name
a.target = "_blank"

// Add click event to open in new tab
a.addEventListener("click", (e) => {
e.preventDefault()
window.open(a.href, "_blank")
})

li.appendChild(a)

if (repo.description) {
const description = document.createElement("div")
description.className = "repo-description"
description.textContent = repo.description
li.appendChild(description)
}

reposContainerElement.appendChild(li)
})

reposListElement.classList.remove("hidden")
}

// UI Helper Functions
function hideAllElements() {
errorMessageElement.classList.add("hidden")
resultElement.classList.add("hidden")
reposListElement.classList.add("hidden")
loadingElement.classList.add("hidden")
}

function showError(message) {
errorMessageElement.textContent = message
errorMessageElement.classList.remove("hidden")
}

function showResult(url) {
resultLinkElement.href = url
resultLinkElement.textContent = url
resultElement.classList.remove("hidden")
}

function showLoading() {
loadingElement.classList.remove("hidden")
}

function hideLoading() {
loadingElement.classList.add("hidden")
}
})
Loading