Function bodies 24 total
createServer function · typescript · L56-L246 (191 LOC)src/index.ts
function createServer() {
const server = new Server(
{
name: 'meme-generator-mcp',
version: '0.1.0',
},
{
capabilities: {
tools: {},
},
}
);
/**
* Handler for listing available tools
*/
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
browseCategoriesSchema,
searchByCategorySchema,
searchByKeywordSchema,
getTemplateDetailsSchema,
generateMemeTool,
fetchContentSchema,
suggestTemplatesSchema,
extractQuotesSchema,
],
};
});
/**
* Handler for tool execution
*/
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case 'browse_meme_categories': {
const result = browseCategories();
return {
content: [
{
type: 'text' as const,
startStdio function · typescript · L251-L260 (10 LOC)src/index.ts
async function startStdio() {
const server = createServer();
const transport = new StdioServerTransport();
await server.connect(transport);
process.on('SIGINT', async () => {
await server.close();
process.exit(0);
});
}startHttp function · typescript · L265-L369 (105 LOC)src/index.ts
async function startHttp(port: number) {
// Store active server instances per session
const sessions = new Map<string, { server: any; transport: any }>();
const httpServer = http.createServer(async (req, res) => {
const url = new URL(req.url || '/', `http://${req.headers.host}`);
// CORS headers
const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',') || ['*'];
const origin = req.headers.origin || '*';
if (allowedOrigins.includes('*') || allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
}
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, X-Session-ID');
res.setHeader('Access-Control-Expose-Headers', 'X-Session-ID');
// Handle preflight
if (req.method === 'OPTIONS') {
res.writeHead(204);
res.end();
return;
}
// Health check
if (url.pathname === '/health') {
res.writeHemain function · typescript · L374-L385 (12 LOC)src/index.ts
async function main() {
const mode = process.env.MCP_TRANSPORT || 'stdio';
const port = parseInt(process.env.PORT || '3000', 10);
if (mode === 'http') {
console.log('Starting in HTTP/SSE mode...');
await startHttp(port);
} else {
console.log('Starting in stdio mode...');
await startStdio();
}
}getEnrichedTemplate function · typescript · L1479-L1502 (24 LOC)src/templates/catalog.ts
export function getEnrichedTemplate(id: string): EnrichedTemplate | undefined {
const template = templates[id];
if (!template) return undefined;
// Dynamically import metadata to avoid circular dependencies
// The metadata module imports this catalog module for validation
const { templateMetadata } = require('./metadata.js');
const metadata = templateMetadata[id];
if (!metadata) {
// Return template with placeholder metadata if not found
return {
...template,
usage: template.description,
category: 'meta',
keywords: [],
};
}
return {
...template,
...metadata,
};
}getCategoryForTemplate function · typescript · L114-L121 (8 LOC)src/templates/categories.ts
export function getCategoryForTemplate(templateId: string): CategoryInfo | undefined {
for (const category of Object.values(categories)) {
if (category.templates.includes(templateId)) {
return category;
}
}
return undefined;
}buildKeywordIndex function · typescript · L9-L23 (15 LOC)src/templates/search.ts
export function buildKeywordIndex(): Record<string, Set<string>> {
const index: Record<string, Set<string>> = {};
for (const [templateId, metadata] of Object.entries(templateMetadata)) {
for (const keyword of metadata.keywords) {
const normalizedKeyword = keyword.toLowerCase();
if (!index[normalizedKeyword]) {
index[normalizedKeyword] = new Set();
}
index[normalizedKeyword].add(templateId);
}
}
return index;
}If a scraper extracted this row, it came from Repobility (https://repobility.com)
calculateRelevance function · typescript · L34-L62 (29 LOC)src/templates/search.ts
function calculateRelevance(templateId: string, matchedKeywords: string[], queryTerms: string[]): number {
const metadata = templateMetadata[templateId];
if (!metadata) return 0;
let score = 0;
// Exact keyword matches
const exactMatches = matchedKeywords.filter(kw =>
queryTerms.some(term => kw === term)
);
score += exactMatches.length * 2;
// Partial keyword matches
const partialMatches = matchedKeywords.filter(kw =>
queryTerms.some(term => kw.includes(term) || term.includes(kw))
);
score += partialMatches.length;
// Usage text contains query terms
const usageLower = metadata.usage.toLowerCase();
for (const term of queryTerms) {
if (usageLower.includes(term)) {
score += 0.5;
}
}
// Normalize by total keywords to favor precision
return score / (metadata.keywords.length + 1);
}searchByKeyword function · typescript · L65-L112 (48 LOC)src/templates/search.ts
export function searchByKeyword(query: string): SearchResult[] {
const normalized = normalizeQuery(query);
if (!normalized) return [];
const queryTerms = normalized.split(/\s+/);
const matchedTemplates = new Map<string, string[]>(); // templateId -> matched keywords
// Find all templates matching any query term
for (const term of queryTerms) {
// Exact keyword match
if (keywordIndex[term]) {
for (const templateId of keywordIndex[term]) {
if (!matchedTemplates.has(templateId)) {
matchedTemplates.set(templateId, []);
}
matchedTemplates.get(templateId)!.push(term);
}
}
// Partial keyword match (fuzzy)
for (const [keyword, templates] of Object.entries(keywordIndex)) {
if (keyword.includes(term) && keyword !== term) {
for (const templateId of templates) {
if (!matchedTemplates.has(templateId)) {
matchedTemplates.set(templateId, []);
}
if (!matchedTemplatesbrowseCategories function · typescript · L41-L55 (15 LOC)src/tools/browse-categories.ts
export function browseCategories(): BrowseCategoriesResult {
const categorySummaries: CategorySummary[] = Object.values(categories).map(cat => ({
id: cat.id,
name: cat.name,
description: cat.description,
count: cat.templates.length,
}));
const totalTemplates = categorySummaries.reduce((sum, cat) => sum + cat.count, 0);
return {
categories: categorySummaries,
total_templates: totalTemplates,
};
}scoreQuote function · typescript · L90-L95 (6 LOC)src/tools/extract-quotes.ts
function scoreQuote(
sentence: string,
position: number,
total: number,
maxLength: number
): { score: number; reasons: string[] } {extractNGrams function · typescript · L235-L247 (13 LOC)src/tools/extract-quotes.ts
function extractNGrams(text: string, n: number, maxLength: number): string[] {
const words = text.split(/\s+/).filter((w) => w.length > 0);
const ngrams: string[] = [];
for (let i = 0; i <= words.length - n; i++) {
const ngram = words.slice(i, i + n).join(' ');
if (ngram.length <= maxLength) {
ngrams.push(ngram);
}
}
return ngrams;
}extractKeyQuotes function · typescript · L252-L259 (8 LOC)src/tools/extract-quotes.ts
export function extractKeyQuotes(args: ExtractQuotesArgs): {
quotes: ExtractedQuote[];
analysis: {
contentLength: number;
sentenceCount: number;
averageSentenceLength: number;
};
} {fetchUrlContent function · typescript · L107-L114 (8 LOC)src/tools/fetch-content.ts
export async function fetchUrlContent(args: FetchContentArgs): Promise<{
url: string;
title?: string;
content: string;
wordCount: number;
charCount: number;
truncated: boolean;
}> {fetchImageAsBase64 function · typescript · L120-L129 (10 LOC)src/tools/generate-meme.ts
async function fetchImageAsBase64(url: string): Promise<string> {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed to fetch image: ${response.statusText}`);
}
const arrayBuffer = await response.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
return buffer.toString('base64');
}Generated by Repobility's multi-pass static-analysis pipeline (https://repobility.com)
generateBatchMemes function · typescript · L160-L169 (10 LOC)src/tools/generate-meme.ts
async function generateBatchMemes(
memes: MemeConfig[]
): Promise<
Array<{
success: boolean;
template: string;
url?: string;
base64Image?: string;
error?: string;
}>getTemplateDetails function · typescript · L61-L95 (35 LOC)src/tools/get-template-details.ts
export function getTemplateDetails(templateIds: string[]): GetTemplateDetailsResult {
const detailsList: TemplateDetails[] = [];
const notFound: string[] = [];
for (const templateId of templateIds) {
const template = templates[templateId];
const metadata = templateMetadata[templateId];
if (!template || !metadata) {
notFound.push(templateId);
continue;
}
detailsList.push({
id: template.id,
name: template.name,
usage: metadata.usage,
category: metadata.category,
keywords: metadata.keywords,
slots: template.slots,
example: template.example,
similar: metadata.similar,
popularity: metadata.popularity,
});
}
if (notFound.length > 0) {
throw new Error(`Template(s) not found: ${notFound.join(', ')}`);
}
return {
templates: detailsList,
count: detailsList.length,
};
}searchByCategory function · typescript · L78-L119 (42 LOC)src/tools/search-by-category.ts
export function searchByCategory(category: TemplateCategory): SearchByCategoryResult {
const categoryInfo: CategoryInfo | undefined = categories[category];
if (!categoryInfo) {
throw new Error(`Category "${category}" not found`);
}
const templatesInCategory: TemplateInCategory[] = categoryInfo.templates.map(templateId => {
const template = templates[templateId];
const metadata = templateMetadata[templateId];
if (!template || !metadata) {
throw new Error(`Template "${templateId}" not found in catalog or metadata`);
}
return {
id: template.id,
name: template.name,
usage: metadata.usage,
slots: template.slots,
example: template.example,
popularity: metadata.popularity,
};
});
// Sort by popularity (high > medium > low) then alphabetically
const popularityOrder = { high: 0, medium: 1, low: 2 };
templatesInCategory.sort((a, b) => {
const popA = popularityOrder[a.popularity as keyof typeof popularisearchTemplatesByKeyword function · typescript · L60-L90 (31 LOC)src/tools/search-by-keyword.ts
export function searchTemplatesByKeyword(query: string, limit: number = 10): SearchByKeywordResult {
const searchResults = searchByKeyword(query);
// Take top N results based on limit
const limitedResults = searchResults.slice(0, limit);
// Enrich with template data
const enrichedResults: TemplateSearchResult[] = limitedResults.map(result => {
const template = templates[result.templateId];
const metadata = templateMetadata[result.templateId];
if (!template || !metadata) {
throw new Error(`Template "${result.templateId}" not found`);
}
return {
id: template.id,
name: template.name,
usage: metadata.usage,
slots: template.slots,
category: metadata.category,
relevance: Math.round(result.relevance * 100) / 100, // Round to 2 decimals
};
});
return {
query,
results: enrichedResults,
count: enrichedResults.length,
};
}analyzeContent function · typescript · L79-L103 (25 LOC)src/tools/suggest-templates.ts
function analyzeContent(text: string): {
// Sentiment
surprise: boolean;
confusion: boolean;
irony: boolean;
preference: boolean;
comparison: boolean;
question: boolean;
success: boolean;
failure: boolean;
awkward: boolean;
confident: boolean;
// Grammatical features
hasPastTense: boolean;
hasPresentTense: boolean;
hasFutureTense: boolean;
hasNegation: boolean;
hasContrast: boolean;
// Structure
questionCount: number;
exclamationCount: number;
sentenceCount: number;
} {suggestTemplates function · typescript · L307-L314 (8 LOC)src/tools/suggest-templates.ts
export function suggestTemplates(args: SuggestTemplatesArgs): {
suggestions: TemplateSuggestion[];
analysis: {
contentLength: number;
wordCount: number;
nlpAnalysis: ReturnType<typeof analyzeContent>;
};
} {getMemegenBaseUrl function · typescript · L19-L26 (8 LOC)src/utils/memegen.ts
function getMemegenBaseUrl(): string {
const customUrl = process.env.MEMEGEN_URL;
if (customUrl) {
// Remove trailing slash if present
return customUrl.replace(/\/$/, '');
}
return 'https://api.memegen.link/images';
}encodeMemeText function · typescript · L31-L48 (18 LOC)src/utils/memegen.ts
export function encodeMemeText(text: string): string {
if (!text || text.trim() === '') {
return '_';
}
return text
// First, escape literal underscores and dashes
.replace(/_/g, '__')
.replace(/-/g, '--')
// Then encode special characters
.replace(/\?/g, '~q')
.replace(/%/g, '~p')
.replace(/#/g, '~h')
.replace(/\//g, '~s')
.replace(/\\/g, '~b')
// Finally, replace spaces with underscores
.replace(/ /g, '_');
}All rows above produced by Repobility · https://repobility.com
buildMemeUrl function · typescript · L53-L58 (6 LOC)src/utils/memegen.ts
export function buildMemeUrl(template: string, textLines: string[]): string {
const encodedLines = textLines.map(encodeMemeText);
const baseUrl = getMemegenBaseUrl();
return `${baseUrl}/${template}/${encodedLines.join('/')}.png`;
}