Skip to content
Open
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
122 changes: 121 additions & 1 deletion mcp/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,121 @@
// mcp code goes here
#!/usr/bin/env node

import path from "path";
import fs from "fs";
import { fileURLToPath } from "url";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

// ESM __dirname workaround
const __dirname = path.dirname(fileURLToPath(import.meta.url));

// TypeScript Interfaces
interface Powerstats {
intelligence: number;
strength: number;
speed: number;
durability: number;
power: number;
combat: number;
}

interface Superhero {
id: string | number;
name: string;
image: string;
powerstats: Powerstats;
}

// Data Loading Function
async function loadSuperheroes(): Promise<Superhero[]> {
try {
const data = await fs.promises.readFile(
path.join(__dirname, "../data/superheroes.json"),
"utf-8"
);
return JSON.parse(data);
} catch (err) {
throw new Error(`Failed to load superheroes data: ${err instanceof Error ? err.message : String(err)}`);
}
}

// Markdown Formatting Function
function formatSuperheroMarkdown(hero: Superhero): string {
return `Here is the data for ${hero.name} retrieved using the superheroes MCP:

• Name: ${hero.name}
• Image: <img src="${hero.image}" alt="${hero.name}"/>
• Powerstats:
• Intelligence: ${hero.powerstats.intelligence}
• Strength: ${hero.powerstats.strength}
• Speed: ${hero.powerstats.speed}
• Durability: ${hero.powerstats.durability}
• Power: ${hero.powerstats.power}
• Combat: ${hero.powerstats.combat}`;
}

// MCP Server Configuration
const server = new McpServer({
name: "superheroes-mcp",
version: "1.0.0"
}, {
capabilities: {
resources: {},
tools: {}
}
});

// Tool Definition
server.registerTool(
"get_superhero",
{
description: "Get superhero details by name or id",
Copy link

Copilot AI Nov 4, 2025

Choose a reason for hiding this comment

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

The description indicates 'name or id' but the implementation uses OR logic that matches on either field. Consider clarifying that at least one parameter is required, or add validation to enforce this requirement (as noted in the search logic issue).

Copilot uses AI. Check for mistakes.
inputSchema: {
name: z.string().optional().describe("Name of the superhero (optional)"),
id: z.string().optional().describe("ID of the superhero (optional)")
}
},
async ({ name, id }: { name?: string; id?: string }) => {
// Load superheroes data
const superheroes = await loadSuperheroes();

// Convert name to lowercase for case-insensitive search
const nameLc = name?.toLowerCase() ?? "";

// Convert id to string for comparison
const idStr = id ?? "";

// Find superhero where either name matches or ID matches
const hero = superheroes.find(h => {
const heroNameLc = h.name?.toLowerCase() ?? "";
const heroIdStr = h.id?.toString() ?? "";
return heroNameLc === nameLc || heroIdStr === idStr;
});
Comment on lines +90 to +94
Copy link

Copilot AI Nov 4, 2025

Choose a reason for hiding this comment

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

The search logic allows matching with empty strings. When both name and id are undefined, nameLc and idStr will be empty strings, and the first hero with an empty/undefined name or id will be returned instead of throwing 'Superhero not found'. Add validation to ensure at least one search parameter is provided before searching.

Copilot uses AI. Check for mistakes.

if (!hero) {
throw new Error("Superhero not found");
}

// Return formatted markdown
return {
content: [{
type: "text" as const,
text: formatSuperheroMarkdown(hero)
}]
};
}
);

// Main Function
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Superhero MCP Server running on stdio");
}

// Error Handling
main().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});