-
Notifications
You must be signed in to change notification settings - Fork 2
Implement MCP server with superhero tool #14
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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", | ||
| 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
|
||
|
|
||
| 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); | ||
| }); | ||
There was a problem hiding this comment.
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).