Batch Conversion¶
Convert multiple GitHub repositories to MCP servers at once.
Web UI Batch Mode¶
Visit github-to-mcp.modelcontextprotocol.name/batch to convert multiple repos:
- Enter one URL per line
- Configure shared options
- Click "Generate All"
- Download as a single ZIP
CLI Batch Processing¶
Using a File List¶
Create a file with URLs (one per line):
repos.txt
https://github.com/stripe/stripe-node
https://github.com/openai/openai-node
https://github.com/vercel/next.js
https://github.com/prisma/prisma
Then process:
while read url; do
npx @nirholas/github-to-mcp "$url" -o "./mcp-servers/$(basename $url)"
done < repos.txt
Parallel Processing¶
For faster conversion, use parallel processing:
# Using GNU parallel
cat repos.txt | parallel -j4 \
'npx @nirholas/github-to-mcp {} -o ./mcp-servers/$(basename {})'
With xargs¶
Programmatic Batch¶
Basic Batch¶
import { generateFromGithub } from '@nirholas/github-to-mcp';
const repos = [
'https://github.com/stripe/stripe-node',
'https://github.com/openai/openai-node',
'https://github.com/vercel/next.js',
];
async function batchGenerate() {
for (const repo of repos) {
const name = repo.split('/').pop();
console.log(`Generating: ${name}`);
const result = await generateFromGithub(repo);
await result.save(`./mcp-servers/${name}`);
console.log(`✓ Generated ${result.tools.length} tools for ${name}`);
}
}
batchGenerate();
Parallel Batch¶
import { generateFromGithub } from '@nirholas/github-to-mcp';
const repos = [
'https://github.com/stripe/stripe-node',
'https://github.com/openai/openai-node',
'https://github.com/vercel/next.js',
];
async function parallelBatch() {
const results = await Promise.all(
repos.map(async (repo) => {
const name = repo.split('/').pop();
try {
const result = await generateFromGithub(repo);
await result.save(`./mcp-servers/${name}`);
return { repo, success: true, tools: result.tools.length };
} catch (error) {
return { repo, success: false, error: error.message };
}
})
);
console.log('Results:', results);
}
parallelBatch();
With Concurrency Control¶
import { generateFromGithub } from '@nirholas/github-to-mcp';
import pLimit from 'p-limit';
const repos = [/* ... */];
const limit = pLimit(3); // Max 3 concurrent
async function controlledBatch() {
const tasks = repos.map(repo =>
limit(async () => {
const name = repo.split('/').pop();
const result = await generateFromGithub(repo);
await result.save(`./mcp-servers/${name}`);
return { name, tools: result.tools.length };
})
);
const results = await Promise.all(tasks);
console.log('Completed:', results);
}
controlledBatch();
Batch Configuration¶
Shared Options¶
Apply the same options to all repos:
const sharedOptions = {
language: 'typescript',
includeUniversalTools: true,
extractFromOpenAPI: true,
token: process.env.GITHUB_TOKEN,
};
for (const repo of repos) {
await generateFromGithub(repo, sharedOptions);
}
Per-Repo Options¶
Customize options per repository:
const repoConfigs = [
{
url: 'https://github.com/stripe/stripe-node',
options: { extractFromOpenAPI: true }
},
{
url: 'https://github.com/facebook/react',
options: { maxDepth: 2 } // Large repo, limit depth
},
{
url: 'https://github.com/my-org/private-repo',
options: { token: process.env.PRIVATE_TOKEN }
},
];
for (const { url, options } of repoConfigs) {
await generateFromGithub(url, options);
}
Output Organization¶
By Category¶
mcp-servers/
├── apis/
│ ├── stripe/
│ └── openai/
├── frameworks/
│ ├── nextjs/
│ └── react/
└── tools/
├── prisma/
└── typescript/
By Organization¶
Script for Organization¶
import { generateFromGithub } from '@nirholas/github-to-mcp';
import { mkdir } from 'fs/promises';
import { join } from 'path';
async function organizedBatch(repos: string[], baseDir: string) {
for (const repo of repos) {
const [, owner, name] = repo.match(/github\.com\/([^/]+)\/([^/]+)/) || [];
const outputDir = join(baseDir, owner, name);
await mkdir(outputDir, { recursive: true });
const result = await generateFromGithub(repo);
await result.save(outputDir);
console.log(`✓ ${owner}/${name} → ${outputDir}`);
}
}
Error Handling¶
Retry Failed Repos¶
async function batchWithRetry(repos: string[], maxRetries = 3) {
const results = { success: [], failed: [] };
for (const repo of repos) {
let attempt = 0;
let success = false;
while (attempt < maxRetries && !success) {
try {
const result = await generateFromGithub(repo);
await result.save(`./mcp-servers/${repo.split('/').pop()}`);
results.success.push(repo);
success = true;
} catch (error) {
attempt++;
console.log(`Retry ${attempt}/${maxRetries} for ${repo}`);
if (attempt === maxRetries) {
results.failed.push({ repo, error: error.message });
} else {
await new Promise(r => setTimeout(r, 1000 * attempt)); // Backoff
}
}
}
}
return results;
}
Generate Report¶
async function batchWithReport(repos: string[]) {
const report = {
timestamp: new Date().toISOString(),
total: repos.length,
results: []
};
for (const repo of repos) {
const start = Date.now();
try {
const result = await generateFromGithub(repo);
await result.save(`./mcp-servers/${repo.split('/').pop()}`);
report.results.push({
repo,
status: 'success',
tools: result.tools.length,
duration: Date.now() - start
});
} catch (error) {
report.results.push({
repo,
status: 'failed',
error: error.message,
duration: Date.now() - start
});
}
}
// Save report
await writeFile(
'./batch-report.json',
JSON.stringify(report, null, 2)
);
return report;
}
Automation¶
GitHub Action¶
.github/workflows/generate-mcp.yml
name: Generate MCP Servers
on:
schedule:
- cron: '0 0 * * 0' # Weekly on Sunday
workflow_dispatch:
jobs:
generate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Generate MCP Servers
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
npm install -g @nirholas/github-to-mcp
while read url; do
github-to-mcp "$url" -o "./mcp-servers/$(basename $url)"
done < repos.txt
- name: Upload Artifacts
uses: actions/upload-artifact@v4
with:
name: mcp-servers
path: mcp-servers/
Cron Job¶
update-mcp-servers.sh
#!/bin/bash
set -e
cd ~/mcp-servers
# Update each server
for repo in stripe/stripe-node openai/openai-node; do
name=$(basename $repo)
npx @nirholas/github-to-mcp "https://github.com/$repo" -o "./$name" --force
echo "Updated $name"
done
# Restart Claude Desktop to pick up changes
osascript -e 'quit app "Claude"'
sleep 2
open -a "Claude"
Next Steps¶
- Configuration - All generation options
- CLI Reference - Command-line details
- API Reference - Programmatic API