#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
Tool,
} from '@modelcontextprotocol/sdk/types.js';
import { z } from 'zod';
import dotenv from 'dotenv';
import {
UberAPI,
UberConfig,
UberAPIError,
RideRequest,
Location
} from './uber-api.js';
// Load environment variables
dotenv.config();
// Validation schemas for tool parameters
const LocationParamsSchema = z.object({
latitude: z.number(),
longitude: z.number(),
address: z.string().optional(),
});
const RideRequestParamsSchema = z.object({
product_id: z.string(),
start_latitude: z.number(),
start_longitude: z.number(),
end_latitude: z.number().optional(),
end_longitude: z.number().optional(),
seat_count: z.number().min(1).max(6).optional(),
});
const EstimateParamsSchema = z.object({
start_latitude: z.number(),
start_longitude: z.number(),
end_latitude: z.number().optional(),
end_longitude: z.number().optional(),
product_id: z.string().optional(),
});
const RideIdParamsSchema = z.object({
request_id: z.string(),
});
const RatingParamsSchema = z.object({
request_id: z.string(),
rating: z.number().min(1).max(5),
feedback: z.string().optional(),
});
const HistoryParamsSchema = z.object({
offset: z.number().min(0).optional(),
limit: z.number().min(1).max(50).optional(),
});
// Turkish language support
const tr = {
tools: {
get_products: {
name: 'uber_arac_turlerini_getir',
description: 'Belirli bir konumda mevcut olan Uber araç türlerini (uberX, uberXL, Comfort, Black) getirir',
},
get_price_estimate: {
name: 'uber_fiyat_tahmini_al',
description: 'Başlangıç ve bitiş noktaları arasında yolculuk için fiyat tahmini alır',
},
get_time_estimate: {
name: 'uber_sure_tahmini_al',
description: 'Sürücünün gelme süresini tahmin eder',
},
request_ride: {
name: 'uber_arac_cagir',
description: 'Yeni bir Uber yolculuğu talep eder',
},
get_current_ride: {
name: 'uber_aktif_yolculuk',
description: 'Şu anda aktif olan yolculuğu getirir',
},
get_ride_details: {
name: 'uber_yolculuk_detaylari',
description: 'Belirli bir yolculuğun detaylarını getirir',
},
cancel_ride: {
name: 'uber_yolculuk_iptal',
description: 'Aktif yolculuğu iptal eder',
},
get_ride_history: {
name: 'uber_yolculuk_gecmisi',
description: 'Geçmiş yolculukların listesini getirir',
},
rate_driver: {
name: 'uber_sofor_puanla',
description: 'Tamamlanan yolculuk için şoförü puanlar',
},
get_user_profile: {
name: 'uber_profil_getir',
description: 'Kullanıcı profil bilgilerini getirir',
},
},
messages: {
auth_required: 'Bu işlem için Uber hesabınıza giriş yapmanız gerekir',
success: 'İşlem başarıyla tamamlandı',
no_current_ride: 'Şu anda aktif yolculuğunuz bulunmuyor',
ride_cancelled: 'Yolculuk başarıyla iptal edildi',
driver_rated: 'Şoför başarıyla puanlandı',
validation_error: 'Geçersiz parametreler',
},
ride_types: {
POOL: 'UberPOOL',
X: 'uberX',
XL: 'uberXL',
SELECT: 'Uber Select',
BLACK: 'Uber Black',
SUV: 'Uber SUV',
TAXI: 'uberTAXI',
WAV: 'uberWAV',
}
};
class UberMCPServer {
private server: Server;
private uberAPI: UberAPI;
constructor() {
// Initialize Uber API
const config: UberConfig = {
clientId: process.env.UBER_CLIENT_ID || '',
clientSecret: process.env.UBER_CLIENT_SECRET || '',
serverToken: process.env.UBER_SERVER_TOKEN,
baseUrl: process.env.UBER_SANDBOX === 'true'
? (process.env.UBER_SANDBOX_API_BASE_URL || 'https://sandbox-api.uber.com')
: (process.env.UBER_API_BASE_URL || 'https://api.uber.com'),
sandbox: process.env.UBER_SANDBOX === 'true',
};
this.uberAPI = new UberAPI(config);
// Initialize MCP server
this.server = new Server(
{
name: process.env.MCP_SERVER_NAME || 'uber-mcp-server',
version: process.env.MCP_SERVER_VERSION || '1.0.0',
},
{
capabilities: {
tools: {},
},
}
);
this.setupTools();
}
private setupTools(): void {
// List available tools
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'uber_arac_turlerini_getir',
description: 'Belirli bir konumda mevcut olan Uber araç türlerini (uberX, uberXL, Comfort, Black) getirir',
inputSchema: {
type: 'object',
properties: {
latitude: { type: 'number', description: 'Enlem koordinatı' },
longitude: { type: 'number', description: 'Boylam koordinatı' },
},
required: ['latitude', 'longitude'],
},
},
{
name: 'uber_fiyat_tahmini_al',
description: 'Başlangıç ve bitiş noktaları arasında yolculuk için fiyat tahmini alır',
inputSchema: {
type: 'object',
properties: {
start_latitude: { type: 'number', description: 'Başlangıç enlem koordinatı' },
start_longitude: { type: 'number', description: 'Başlangıç boylam koordinatı' },
end_latitude: { type: 'number', description: 'Bitiş enlem koordinatı' },
end_longitude: { type: 'number', description: 'Bitiş boylam koordinatı' },
},
required: ['start_latitude', 'start_longitude', 'end_latitude', 'end_longitude'],
},
},
{
name: 'uber_sure_tahmini_al',
description: 'Sürücünün gelme süresini tahmin eder',
inputSchema: {
type: 'object',
properties: {
start_latitude: { type: 'number', description: 'Başlangıç enlem koordinatı' },
start_longitude: { type: 'number', description: 'Başlangıç boylam koordinatı' },
product_id: { type: 'string', description: 'Ürün ID (isteğe bağlı)' },
},
required: ['start_latitude', 'start_longitude'],
},
},
{
name: 'uber_arac_cagir',
description: 'Yeni bir Uber yolculuğu talep eder',
inputSchema: {
type: 'object',
properties: {
product_id: { type: 'string', description: 'Araç türü ID' },
start_latitude: { type: 'number', description: 'Başlangıç enlem koordinatı' },
start_longitude: { type: 'number', description: 'Başlangıç boylam koordinatı' },
end_latitude: { type: 'number', description: 'Bitiş enlem koordinatı (isteğe bağlı)' },
end_longitude: { type: 'number', description: 'Bitiş boylam koordinatı (isteğe bağlı)' },
seat_count: { type: 'number', description: 'Yolcu sayısı (1-6 arası)' },
},
required: ['product_id', 'start_latitude', 'start_longitude'],
},
},
{
name: 'uber_aktif_yolculuk',
description: 'Şu anda aktif olan yolculuğu getirir',
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'uber_yolculuk_detaylari',
description: 'Belirli bir yolculuğun detaylarını getirir',
inputSchema: {
type: 'object',
properties: {
request_id: { type: 'string', description: 'Yolculuk talep ID' },
},
required: ['request_id'],
},
},
{
name: 'uber_yolculuk_iptal',
description: 'Aktif yolculuğu iptal eder',
inputSchema: {
type: 'object',
properties: {
request_id: { type: 'string', description: 'İptal edilecek yolculuk ID' },
},
required: ['request_id'],
},
},
{
name: 'uber_yolculuk_gecmisi',
description: 'Geçmiş yolculukların listesini getirir',
inputSchema: {
type: 'object',
properties: {
offset: { type: 'number', description: 'Başlangıç offset (varsayılan: 0)' },
limit: { type: 'number', description: 'Maksimum sonuç sayısı (varsayılan: 10)' },
},
},
},
{
name: 'uber_sofor_puanla',
description: 'Tamamlanan yolculuk için şoförü puanlar',
inputSchema: {
type: 'object',
properties: {
request_id: { type: 'string', description: 'Yolculuk talep ID' },
rating: { type: 'number', description: 'Puan (1-5 arası)' },
feedback: { type: 'string', description: 'Geri bildirim (isteğe bağlı)' },
},
required: ['request_id', 'rating'],
},
},
{
name: 'uber_profil_getir',
description: 'Kullanıcı profil bilgilerini getirir',
inputSchema: {
type: 'object',
properties: {},
},
},
],
};
});
// Handle tool calls
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
try {
switch (request.params.name) {
case 'uber_arac_turlerini_getir':
return await this.handleGetProducts(request.params.arguments);
case 'uber_fiyat_tahmini_al':
return await this.handleGetPriceEstimate(request.params.arguments);
case 'uber_sure_tahmini_al':
return await this.handleGetTimeEstimate(request.params.arguments);
case 'uber_arac_cagir':
return await this.handleRequestRide(request.params.arguments);
case 'uber_aktif_yolculuk':
return await this.handleGetCurrentRide();
case 'uber_yolculuk_detaylari':
return await this.handleGetRideDetails(request.params.arguments);
case 'uber_yolculuk_iptal':
return await this.handleCancelRide(request.params.arguments);
case 'uber_yolculuk_gecmisi':
return await this.handleGetRideHistory(request.params.arguments);
case 'uber_sofor_puanla':
return await this.handleRateDriver(request.params.arguments);
case 'uber_profil_getir':
return await this.handleGetUserProfile();
default:
throw new Error(`Bilinmeyen araç: ${request.params.name}`);
}
} catch (error) {
console.error('Tool execution error:', error);
if (error instanceof UberAPIError) {
return {
content: [
{
type: 'text',
text: `Uber API Hatası: ${error.message} (Kod: ${error.code})`,
},
],
};
}
return {
content: [
{
type: 'text',
text: `Hata: ${error instanceof Error ? error.message : 'Bilinmeyen hata'}`,
},
],
};
}
});
}
private async handleGetProducts(args: any) {
const params = LocationParamsSchema.parse(args);
const products = await this.uberAPI.getProducts(params.latitude, params.longitude);
const formattedProducts = products.map(product => ({
id: product.product_id,
isim: product.display_name,
aciklama: product.description,
kapasite: product.capacity,
gorsel: product.image,
}));
return {
content: [
{
type: 'text',
text: `Mevcut araç türleri (${params.latitude}, ${params.longitude}):\n\n${
formattedProducts.map(p =>
`• ${p.isim} (${p.kapasite} kişi)\n ${p.aciklama}\n ID: ${p.id}`
).join('\n\n')
}`,
},
],
};
}
private async handleGetPriceEstimate(args: any) {
const params = EstimateParamsSchema.parse(args);
const estimates = await this.uberAPI.getPriceEstimate(
params.start_latitude,
params.start_longitude,
params.end_latitude!,
params.end_longitude!
);
const formattedEstimates = estimates.map(estimate => ({
araç_türü: estimate.display_name,
tahmini_fiyat: estimate.estimate,
düşük_tahmini: estimate.low_estimate,
yüksek_tahmini: estimate.high_estimate,
surge_çarpanı: estimate.surge_multiplier,
süre_dakika: estimate.duration ? Math.round(estimate.duration / 60) : undefined,
mesafe_km: estimate.distance ? (estimate.distance * 1.60934).toFixed(1) : undefined,
}));
return {
content: [
{
type: 'text',
text: `Fiyat tahminleri:\n\n${
formattedEstimates.map(e =>
`• ${e.araç_türü}: ${e.tahmini_fiyat}\n` +
` Süre: ${e.süre_dakika || 'N/A'} dakika\n` +
` Mesafe: ${e.mesafe_km || 'N/A'} km\n` +
(e.surge_çarpanı && e.surge_çarpanı > 1 ? ` ⚠️ Surge: ${e.surge_çarpanı}x` : '')
).join('\n\n')
}`,
},
],
};
}
private async handleGetTimeEstimate(args: any) {
const params = EstimateParamsSchema.parse(args);
const estimates = await this.uberAPI.getTimeEstimate(
params.start_latitude,
params.start_longitude,
params.product_id
);
return {
content: [
{
type: 'text',
text: `Süre tahminleri:\n\n${
estimates.map((e: any) =>
`• ${e.display_name}: ${Math.round(e.estimate / 60)} dakika`
).join('\n')
}`,
},
],
};
}
private async handleRequestRide(args: any) {
const params = RideRequestParamsSchema.parse(args);
const ride = await this.uberAPI.requestRide(params as RideRequest);
return {
content: [
{
type: 'text',
text: `Araç çağırma talebi oluşturuldu!\n\n` +
`• Talep ID: ${ride.request_id}\n` +
`• Durum: ${this.uberAPI.translateStatus(ride.status)}\n` +
`${ride.eta ? `• Tahmini varış: ${ride.eta} dakika` : ''}`,
},
],
};
}
private async handleGetCurrentRide() {
const ride = await this.uberAPI.getCurrentRide();
if (!ride) {
return {
content: [
{
type: 'text',
text: tr.messages.no_current_ride,
},
],
};
}
let rideInfo = `Aktif yolculuk:\n\n`;
rideInfo += `• Talep ID: ${ride.request_id}\n`;
rideInfo += `• Durum: ${this.uberAPI.translateStatus(ride.status)}\n`;
if (ride.driver) {
rideInfo += `\nŞoför:\n`;
rideInfo += `• İsim: ${ride.driver.name}\n`;
rideInfo += `• Telefon: ${ride.driver.phone_number}\n`;
if (ride.driver.rating) {
rideInfo += `• Puan: ${ride.driver.rating}/5\n`;
}
}
if (ride.vehicle) {
rideInfo += `\nAraç:\n`;
rideInfo += `• ${ride.vehicle.make} ${ride.vehicle.model}\n`;
rideInfo += `• Plaka: ${ride.vehicle.license_plate}\n`;
}
if (ride.eta) {
rideInfo += `\n• Tahmini varış: ${ride.eta} dakika`;
}
return {
content: [
{
type: 'text',
text: rideInfo,
},
],
};
}
private async handleGetRideDetails(args: any) {
const params = RideIdParamsSchema.parse(args);
const ride = await this.uberAPI.getRideDetails(params.request_id);
let rideInfo = `Yolculuk detayları:\n\n`;
rideInfo += `• Talep ID: ${ride.request_id}\n`;
rideInfo += `• Durum: ${this.uberAPI.translateStatus(ride.status)}\n`;
if (ride.driver) {
rideInfo += `\nŞoför:\n`;
rideInfo += `• İsim: ${ride.driver.name}\n`;
rideInfo += `• Telefon: ${ride.driver.phone_number}\n`;
if (ride.driver.rating) {
rideInfo += `• Puan: ${ride.driver.rating}/5\n`;
}
}
if (ride.vehicle) {
rideInfo += `\nAraç:\n`;
rideInfo += `• ${ride.vehicle.make} ${ride.vehicle.model}\n`;
rideInfo += `• Plaka: ${ride.vehicle.license_plate}\n`;
}
return {
content: [
{
type: 'text',
text: rideInfo,
},
],
};
}
private async handleCancelRide(args: any) {
const params = RideIdParamsSchema.parse(args);
await this.uberAPI.cancelRide(params.request_id);
return {
content: [
{
type: 'text',
text: tr.messages.ride_cancelled,
},
],
};
}
private async handleGetRideHistory(args: any = {}) {
const params = HistoryParamsSchema.parse(args);
const history = await this.uberAPI.getRideHistory(params.offset, params.limit);
const rides = history.history || [];
if (rides.length === 0) {
return {
content: [
{
type: 'text',
text: 'Geçmiş yolculuk bulunamadı.',
},
],
};
}
const formattedHistory = rides.map((ride: any, index: number) => {
const startTime = new Date(ride.start_time).toLocaleString('tr-TR');
const endTime = ride.end_time ? new Date(ride.end_time).toLocaleString('tr-TR') : 'Devam ediyor';
return `${index + 1}. Yolculuk\n` +
` • Tarih: ${startTime}\n` +
` • Bitiş: ${endTime}\n` +
` • Durum: ${this.uberAPI.translateStatus(ride.status)}\n` +
` • Mesafe: ${ride.distance ? `${(ride.distance * 1.60934).toFixed(1)} km` : 'N/A'}\n` +
` • Ücret: ${ride.fare ? `${ride.fare.total_fare} ${ride.fare.currency_code}` : 'N/A'}`;
}).join('\n\n');
return {
content: [
{
type: 'text',
text: `Yolculuk Geçmişi:\n\n${formattedHistory}`,
},
],
};
}
private async handleRateDriver(args: any) {
const params = RatingParamsSchema.parse(args);
await this.uberAPI.rateDriver(params.request_id, params.rating, params.feedback);
return {
content: [
{
type: 'text',
text: tr.messages.driver_rated,
},
],
};
}
private async handleGetUserProfile() {
const profile = await this.uberAPI.getUserProfile();
return {
content: [
{
type: 'text',
text: `Kullanıcı Profili:\n\n` +
`• İsim: ${profile.first_name} ${profile.last_name}\n` +
`• E-posta: ${profile.email}\n` +
`• Telefon: ${profile.mobile || 'Belirtilmemiş'}\n` +
`• Şehir: ${profile.home_city || 'Belirtilmemiş'}\n` +
`• Üyelik tarihi: ${new Date(profile.signup_date).toLocaleDateString('tr-TR')}`,
},
],
};
}
async run(): Promise<void> {
console.error('Uber MCP Server başlatılıyor...');
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error('Uber MCP Server başarıyla başlatıldı ve bağlantılar dinleniyor.');
}
}
// Start the server
if (require.main === module) {
const server = new UberMCPServer();
server.run().catch((error) => {
console.error('Server başlatma hatası:', error);
process.exit(1);
});
}
export { UberMCPServer };