server.js•16.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ı!");