Skip to main content
Glama

DNDzgz MCP Server

by danilat
index.js10.4 kB
#!/usr/bin/env node import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; import express from "express"; import { z } from "zod"; function getServer() { const server = new McpServer({ name: "DNDzgz", version: "0.0.1", }); server.tool( "zaragoza-tram-estimations", "Get the estimation of when arrives to a tram station in Zaragoza in realtime", { station: z.number() }, async ({ station }) => { const response = await fetch( `https://dndzgz.herokuapp.com/services/tram/${station}` ); if (response.ok) { const data = await response.json(); return { content: [{ type: "text", text: JSON.stringify(data) }], }; } else { return { content: [ { type: "text", text: `Is not possible to get the estimate for ${station}`, }, ], }; } } ); server.tool( "zaragoza-tram-stations", "Get all tram stations in Zaragoza", { latitude: z.number(), longitude: z.number(), }, async ({ latitude, longitude }) => { const response = await fetch("https://dndzgz.herokuapp.com/services/tram"); if (response.ok) { const data = await response.json(); const nearestPositions = getTopNearestPositions( data.locations, latitude, longitude ); return { content: [{ type: "text", text: JSON.stringify(nearestPositions) }], }; } else { return { content: [ { type: "text", text: "It was not possible to get the tram stations", }, ], }; } } ); server.tool( "zaragoza-bus-stops", "Get all bus stops in Zaragoza", { latitude: z.number(), longitude: z.number(), }, async ({ latitude, longitude }) => { const response = await fetch("https://dndzgz.herokuapp.com/services/bus"); if (response.ok) { const data = await response.json(); const nearestPositions = getTopNearestPositions( data.locations, latitude, longitude ); return { content: [{ type: "text", text: JSON.stringify(nearestPositions) }], }; } else { return { content: [ { type: "text", text: "It was not possible to get the bus stops" }, ], }; } } ); server.tool( "zaragoza-bus-estimations", "Get the estimation of when a bus arrives to a stop in Zaragoza in realtime", { stop: z.number() }, async ({ stop }) => { const response = await fetch( `https://dndzgz.herokuapp.com/services/bus/${stop}` ); if (response.ok) { const data = await response.json(); return { content: [{ type: "text", text: JSON.stringify(data) }], }; } else { return { content: [ { type: "text", text: `Is not possible to get the estimate for the stop ${stop}`, }, ], }; } } ); server.tool( "zaragoza-bizi-stations", "Get all Bizi stations in Zaragoza, the bicycle rental public service", { latitude: z.number(), longitude: z.number(), }, async ({ latitude, longitude }) => { const response = await fetch("https://dndzgz.herokuapp.com/services/bizi"); if (response.ok) { const data = await response.json(); const nearestPositions = getTopNearestPositions( data.locations, latitude, longitude ); return { content: [{ type: "text", text: JSON.stringify(nearestPositions) }], }; } else { return { content: [ { type: "text", text: "It was not possible to get the Bizi stations", }, ], }; } } ); server.tool( "zaragoza-bizi-estimations", "Get the estimation of bikes and free slots in a Bizi station in Zaragoza in realtime", { station: z.number() }, async ({ station }) => { const response = await fetch( `https://dndzgz.herokuapp.com/services/bizi/${station}` ); if (response.ok) { const data = await response.json(); return { content: [{ type: "text", text: JSON.stringify(data) }], }; } else { return { content: [ { type: "text", text: `Is not possible to get the estimate for the Bizi station ${station}`, }, ], }; } } ); server.tool( "google-maps-link", "Get a Google Maps link from coordinates to help people to find a station o stop", { latitude: z.number(), longitude: z.number(), }, async ({ latitude, longitude }) => { const mapsUrl = `https://www.google.com/maps?q=${latitude},${longitude}`; return { content: [{ type: "text", text: mapsUrl }], }; } ); server.tool( "geolocation-from-address", "Get the geolocation (latitude and longitude) from an address and the formatted address that was found, only for Zaragoza. Can be used to find a bus stops, tram stations or bizi stations", { address: z.string(), }, async ({ address }) => { const GOOGLE_MAPS_API_KEY = process.env.GOOGLE_MAPS_API_KEY; if (!GOOGLE_MAPS_API_KEY) { return { content: [ { type: "text", text: "You should configure the GOOGLE_MAPS_API_KEY for MCP DNDzgz", }, ], }; } const encodedAddress = encodeURIComponent(`${address}, Zaragoza, Spain`); const url = `https://maps.googleapis.com/maps/api/geocode/json?address=${encodedAddress}&key=${GOOGLE_MAPS_API_KEY}`; const response = await fetch(url); if (response.ok) { const data = await response.json(); if (data.status === "OK" && data.results.length > 0) { const location = data.results[0].geometry.location; location.confindence = data.results[0].geometry.location_type; location.formatted_address = data.results[0].formatted_address; return { content: [{ type: "text", text: JSON.stringify(location) }], }; } else { return { content: [ { type: "text", text: `No geolocation found for the address: ${address}`, }, ], }; } } else { return { content: [ { type: "text", text: `Failed to fetch geolocation for the address: ${address}`, }, ], }; } } ); return server; } function getTopNearestPositions(positions, latitude, longitude, size = 10) { return getOrderedPositionsByDistance(positions, latitude, longitude).slice( 0, size ); } function getOrderedPositionsByDistance(positions, latitude, longitude) { return positions .map((position) => { const distanceInMeters = haversineDistanceInMeters( { lat: latitude, lon: longitude }, position ); return { ...position, distanceInMeters, }; }) .sort((a, b) => a.distanceInMeters - b.distanceInMeters); } function haversineDistanceInMeters(position1, position2) { const toRadians = (degrees) => degrees * (Math.PI / 180); const radiusOfEarth = 6371; const dLat = toRadians(position2.lat - position1.lat); const dLon = toRadians(position2.lon - position1.lon); const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(toRadians(position1.lat)) * Math.cos(toRadians(position2.lat)) * Math.sin(dLon / 2) * Math.sin(dLon / 2); const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return radiusOfEarth * c * 1000; } async function main() { const serverType = process.env.MCP_SERVER_TYPE || "stdio"; if (serverType === "http") { const app = express(); app.use(express.json()); app.post("/mcp", async (req, res) => { console.log("Received POST MCP request"); try { const server = getServer(); const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); res.on("close", () => { console.log("Request closed"); transport.close(); server.close(); }); await server.connect(transport); await transport.handleRequest(req, res, req.body); } catch (error) { console.error("Error handling MCP request:", error); if (!res.headersSent) { res.status(500).json({ jsonrpc: "2.0", error: { code: -32603, message: "Internal server error", }, id: null, }); } } }); app.get("/mcp", async (req, res) => { console.log("Received GET MCP request"); res.writeHead(405).end( JSON.stringify({ jsonrpc: "2.0", error: { code: -32000, message: "Method not allowed.", }, id: null, }) ); }); app.delete("/mcp", async (req, res) => { console.log("Received DELETE MCP request"); res.writeHead(405).end( JSON.stringify({ jsonrpc: "2.0", error: { code: -32000, message: "Method not allowed.", }, id: null, }) ); }); const PORT = 3000; app.listen(PORT, () => { console.log( `MCP Stateless Streamable HTTP Server listening on port ${PORT}` ); }); } else { const server = getServer(); const transport = new StdioServerTransport(); await server.connect(transport); console.error("DNDzgz MCP Server running on stdio"); } } main().catch((error) => { console.error("Fatal error in main():", error); process.exit(1); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/danilat/mcp-dndzgz'

If you have feedback or need assistance with the MCP directory API, please join our Discord server