Skip to main content
Glama

PostGIS MCP Server

by receptopalak
server.js16.2 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js"); const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js"); const types_js_1 = require("@modelcontextprotocol/sdk/types.js"); const zod_1 = require("zod"); const pg_1 = require("pg"); const dotenv_1 = require("dotenv"); // Environment variables yükle (0, dotenv_1.config)(); // Zod schemas for input validation const CreatePointSchema = zod_1.z.object({ longitude: zod_1.z.number(), latitude: zod_1.z.number(), srid: zod_1.z.number().optional().default(4326), }); const CalculateDistanceSchema = zod_1.z.object({ point1_lon: zod_1.z.number(), point1_lat: zod_1.z.number(), point2_lon: zod_1.z.number(), point2_lat: zod_1.z.number(), use_geography: zod_1.z.boolean().optional().default(true), }); const FindNearbySchema = zod_1.z.object({ latitude: zod_1.z.number(), longitude: zod_1.z.number(), distance_km: zod_1.z.number(), table_name: zod_1.z.string(), limit: zod_1.z.number().optional().default(10), }); const CreateBufferSchema = zod_1.z.object({ geometry_wkt: zod_1.z.string(), distance_meters: zod_1.z.number(), }); const TransformCoordinatesSchema = zod_1.z.object({ geometry_wkt: zod_1.z.string(), source_srid: zod_1.z.number(), target_srid: zod_1.z.number(), }); // PostgreSQL client const createDbClient = () => { return new pg_1.Client({ host: process.env.DB_HOST || "localhost", port: parseInt(process.env.DB_PORT || "5432"), database: process.env.DB_NAME, user: process.env.DB_USER, password: process.env.DB_PASSWORD, }); }; // MCP Server oluştur const server = new index_js_1.Server({ name: "postgis-mcp-server", version: "1.0.0", }, { capabilities: { tools: {}, resources: {}, }, }); // Available tools listesi server.setRequestHandler(types_js_1.ListToolsRequestSchema, () => __awaiter(void 0, void 0, void 0, function* () { return { tools: [ { name: "test-connection", description: "PostGIS veritabanı bağlantısını test et", inputSchema: { type: "object", properties: {}, }, }, { name: "create-point", description: "Koordinatlardan nokta geometrisi oluştur", inputSchema: { type: "object", properties: { longitude: { type: "number", description: "Boylam koordinatı", }, latitude: { type: "number", description: "Enlem koordinatı", }, srid: { type: "number", description: "Spatial Reference System ID (varsayılan: 4326)", }, }, required: ["longitude", "latitude"], }, }, { name: "calculate-distance", description: "İki nokta arasındaki mesafeyi hesapla", inputSchema: { type: "object", properties: { point1_lon: { type: "number", description: "Birinci nokta boylam" }, point1_lat: { type: "number", description: "Birinci nokta enlem" }, point2_lon: { type: "number", description: "İkinci nokta boylam" }, point2_lat: { type: "number", description: "İkinci nokta enlem" }, use_geography: { type: "boolean", description: "Coğrafi hesaplama kullan (varsayılan: true)" }, }, required: ["point1_lon", "point1_lat", "point2_lon", "point2_lat"], }, }, { name: "find-nearby", description: "Belirli bir noktanın çevresindeki özellikleri bul", inputSchema: { type: "object", properties: { latitude: { type: "number", description: "Merkez nokta enlem" }, longitude: { type: "number", description: "Merkez nokta boylam" }, distance_km: { type: "number", description: "Arama mesafesi (km)" }, table_name: { type: "string", description: "Aranacak tablo adı" }, limit: { type: "number", description: "Maksimum sonuç sayısı (varsayılan: 10)" }, }, required: ["latitude", "longitude", "distance_km", "table_name"], }, }, { name: "create-buffer", description: "Geometri etrafında buffer oluştur", inputSchema: { type: "object", properties: { geometry_wkt: { type: "string", description: "WKT formatında geometri" }, distance_meters: { type: "number", description: "Buffer mesafesi (metre)" }, }, required: ["geometry_wkt", "distance_meters"], }, }, { name: "transform-coordinates", description: "Koordinat sistemini dönüştür", inputSchema: { type: "object", properties: { geometry_wkt: { type: "string", description: "WKT formatında geometri" }, source_srid: { type: "number", description: "Kaynak koordinat sistemi SRID" }, target_srid: { type: "number", description: "Hedef koordinat sistemi SRID" }, }, required: ["geometry_wkt", "source_srid", "target_srid"], }, }, ], }; })); // Available resources listesi server.setRequestHandler(types_js_1.ListResourcesRequestSchema, () => __awaiter(void 0, void 0, void 0, function* () { return { resources: [ { uri: "spatial://tables", name: "Spatial Tables", description: "Veritabanındaki spatial tabloların listesi", mimeType: "application/json", }, { uri: "spatial://srs", name: "Spatial Reference Systems", description: "Mevcut koordinat sistemleri", mimeType: "application/json", }, ], }; })); // Resources okuma server.setRequestHandler(types_js_1.ReadResourceRequestSchema, (request) => __awaiter(void 0, void 0, void 0, function* () { const { uri } = request.params; const client = createDbClient(); try { yield client.connect(); if (uri === "spatial://tables") { const result = yield client.query(` SELECT f_table_name as table_name, f_geometry_column as geometry_column, type as geometry_type, srid FROM geometry_columns ORDER BY f_table_name; `); return { contents: [ { uri, mimeType: "application/json", text: JSON.stringify(result.rows, null, 2), }, ], }; } if (uri === "spatial://srs") { const result = yield client.query(` SELECT srid, auth_name, auth_srid, srtext FROM spatial_ref_sys WHERE srid IN (4326, 3857, 2154, 32633, 32634, 32635) ORDER BY srid; `); return { contents: [ { uri, mimeType: "application/json", text: JSON.stringify(result.rows, null, 2), }, ], }; } throw new Error(`Unknown resource: ${uri}`); } catch (error) { throw new Error(`Resource read error: ${error instanceof Error ? error.message : 'Unknown error'}`); } finally { yield client.end(); } })); // Tool implementations server.setRequestHandler(types_js_1.CallToolRequestSchema, (request) => __awaiter(void 0, void 0, void 0, function* () { const { name, arguments: args } = request.params; const client = createDbClient(); try { yield client.connect(); switch (name) { case "test-connection": { const result = yield client.query("SELECT PostGIS_Version() as version"); return { content: [ { type: "text", text: `PostGIS bağlantısı başarılı! Versiyon: ${result.rows[0].version}`, }, ], }; } case "create-point": { const { longitude, latitude, srid } = CreatePointSchema.parse(args); const result = yield client.query(` SELECT ST_AsText(ST_SetSRID(ST_MakePoint($1, $2), $3)) as wkt, ST_AsGeoJSON(ST_SetSRID(ST_MakePoint($1, $2), $3)) as geojson `, [longitude, latitude, srid]); const response = { wkt: result.rows[0].wkt, geojson: JSON.parse(result.rows[0].geojson), coordinates: [longitude, latitude], srid: srid, }; return { content: [ { type: "text", text: JSON.stringify(response, null, 2), }, ], }; } case "calculate-distance": { const { point1_lon, point1_lat, point2_lon, point2_lat, use_geography } = CalculateDistanceSchema.parse(args); const castType = use_geography ? "::geography" : ""; const result = yield client.query(` SELECT ST_Distance( ST_SetSRID(ST_MakePoint($1, $2), 4326)${castType}, ST_SetSRID(ST_MakePoint($3, $4), 4326)${castType} ) as distance `, [point1_lon, point1_lat, point2_lon, point2_lat]); const distance = parseFloat(result.rows[0].distance); const response = { distance_meters: distance, distance_km: distance / 1000, point1: [point1_lon, point1_lat], point2: [point2_lon, point2_lat], calculation_type: use_geography ? "geography" : "geometry", }; return { content: [ { type: "text", text: JSON.stringify(response, null, 2), }, ], }; } case "find-nearby": { const { latitude, longitude, distance_km, table_name, limit } = FindNearbySchema.parse(args); // Tablo adını sanitize et (basit güvenlik) const sanitizedTableName = table_name.replace(/[^a-zA-Z0-9_]/g, ''); const result = yield client.query(` SELECT *, ST_AsGeoJSON(geom) as geometry, ST_Distance( geom, ST_SetSRID(ST_MakePoint($1, $2), 4326)::geography ) / 1000 as distance_km FROM ${sanitizedTableName} WHERE ST_DWithin( geom, ST_SetSRID(ST_MakePoint($1, $2), 4326)::geography, $3 ) ORDER BY distance_km LIMIT $4; `, [longitude, latitude, distance_km * 1000, limit]); return { content: [ { type: "text", text: JSON.stringify(result.rows, null, 2), }, ], }; } case "create-buffer": { const { geometry_wkt, distance_meters } = CreateBufferSchema.parse(args); const result = yield client.query(` SELECT ST_AsText(ST_Buffer(ST_GeomFromText($1), $2)) as buffer_wkt, ST_AsGeoJSON(ST_Buffer(ST_GeomFromText($1), $2)) as buffer_geojson, ST_Area(ST_Buffer(ST_GeomFromText($1), $2)) as buffer_area `, [geometry_wkt, distance_meters]); const response = { original_geometry: geometry_wkt, buffer_distance_meters: distance_meters, buffer_wkt: result.rows[0].buffer_wkt, buffer_geojson: JSON.parse(result.rows[0].buffer_geojson), buffer_area: parseFloat(result.rows[0].buffer_area), }; return { content: [ { type: "text", text: JSON.stringify(response, null, 2), }, ], }; } case "transform-coordinates": { const { geometry_wkt, source_srid, target_srid } = TransformCoordinatesSchema.parse(args); const result = yield client.query(` SELECT ST_AsText(ST_Transform(ST_GeomFromText($1, $2), $3)) as transformed_wkt, ST_AsGeoJSON(ST_Transform(ST_GeomFromText($1, $2), $3)) as transformed_geojson, $2 as source_srid, $3 as target_srid `, [geometry_wkt, source_srid, target_srid]); const response = { original_geometry: geometry_wkt, source_srid: source_srid, target_srid: target_srid, transformed_wkt: result.rows[0].transformed_wkt, transformed_geojson: JSON.parse(result.rows[0].transformed_geojson), }; return { content: [ { type: "text", text: JSON.stringify(response, null, 2), }, ], }; } default: { return { content: [ { type: "text", text: `Bilinmeyen tool: ${name}`, }, ], }; } } } catch (error) { const err = error; return { content: [ { type: "text", text: `Hata: ${err.message}`, }, ], }; } finally { yield client.end(); } })); // Server'ı başlat const transport = new stdio_js_1.StdioServerTransport(); await server.connect(transport); console.log("PostGIS MCP Server başlatıldı!");

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/receptopalak/postgis-mcp'

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