MCP-Guide
by qpd-v
- src
#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
ListPromptsRequestSchema,
GetPromptRequestSchema,
McpError,
ErrorCode
} from "@modelcontextprotocol/sdk/types.js";
import { readFileSync } from "fs";
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const ROOT_DIR = dirname(__dirname); // Go up one level to the root
// Define tools and prompts before using them in the server configuration
// Define tools with their handlers in the same file
const tools = {
get_system_prompt: {
description: "Return the content of system_prompt.md as a string.",
inputSchema: { type: "object" }, // Define schema directly here
handler: (): string => {
try {
const path = join(ROOT_DIR, "system_prompt.md");
return readFileSync(path, "utf8");
} catch (err) {
const error = err as Error;
throw new McpError(
ErrorCode.InternalError,
`Failed to read system_prompt.md: ${error?.message || 'Unknown error'}`
);
}
}
},
get_birdweather_api: {
description: "Return the BirdWeather OpenAPI JSON as parsed JSON.",
inputSchema: { type: "object" }, // Define schema directly here
handler: (): unknown => {
try {
const path = join(ROOT_DIR, "birdweather_api.json");
return JSON.parse(readFileSync(path, "utf8"));
} catch (err) {
const error = err as Error;
throw new McpError(
ErrorCode.InternalError,
`Failed to read birdweather_api.json: ${error?.message || 'Unknown error'}`
);
}
}
},
get_ebird_api: {
description: "Return the eBird OpenAPI JSON as parsed JSON.",
inputSchema: { type: "object" }, // Define schema directly here
handler: (): unknown => {
try {
const path = join(ROOT_DIR, "ebird_api.json");
return JSON.parse(readFileSync(path, "utf8"));
} catch (err) {
const error = err as Error;
throw new McpError(
ErrorCode.InternalError,
`Failed to read ebird_api.json: ${error?.message || 'Unknown error'}`
);
}
}
}
};
// Core prompt definition that forces proper API usage and data integrity
const PROMPTS = {
"check-bird": {
name: "check-bird",
description: "Analyze bird observations using BirdNET-Pi and eBird data with strict data integrity rules",
arguments: [
{
name: "query",
description: "User's question about birds they saw/heard",
required: true
},
{
name: "token",
description: "BirdWeather station token (pasted in chat)",
required: false
}
]
}
};
// Initialize the server with required capabilities
const server = new Server(
{
name: "mcp-server-birdstats",
version: "0.1.0"
},
{
capabilities: {
tools: tools,
prompts: PROMPTS
}
}
);
// Set up tool handlers
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: Object.entries(tools).map(([name, tool]) => ({
name,
description: tool.description
}))
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const handler = tools[request.params.name as keyof typeof tools]?.handler;
if (!handler) {
throw new McpError(
ErrorCode.MethodNotFound,
`Unknown tool: ${request.params.name}`
);
}
const result = await handler();
return {
content: [{
type: "json",
json: result
}]
};
});
// Set up prompt handlers
server.setRequestHandler(ListPromptsRequestSchema, async () => ({
prompts: Object.values(PROMPTS)
}));
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
if (request.params.name !== "check-bird") {
throw new McpError(
ErrorCode.InvalidRequest,
`Prompt not found: ${request.params.name}`
);
}
return {
messages: [
{
role: "assistant",
content: {
type: "text",
text: `STOP AND READ THESE INSTRUCTIONS:
1. YOU HAVE ACCESS TO:
- BirdWeather API - USE THIS FIRST! No claiming you can't access it.
- eBird API - USE THIS TOO! No claiming you can't access it.
- Web search through @mzxrai/mcp-webresearch as needed
2. DATA RULES - YOU MUST FOLLOW:
- NO example data or estimates
- ONLY use actual API responses
- MUST show percentages for comparisons
- MUST state total observation counts
- If APIs fail, explain exactly which one and why
3. TOKEN HANDLING:
- User will paste token in chat
- DO NOT ask for location - it's in the token
- NEVER ask for environment variables
4. WORKFLOW:
Parse question → Check BirdNET data → Cross-reference eBird → Show percentages → Explain patterns
REQUIRED: Show you understand by checking both APIs and stating observation counts in your response.`
}
}
]
};
});
// Run server using stdio transport
const transport = new StdioServerTransport();
server.connect(transport).catch((error) => {
console.error("Server error:", error);
process.exit(1);
});