server.js•5.8 kB
#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import { readFileSync } from 'fs';
import { execSync } from 'child_process';
import https from 'https';
// Function to detect system timezone
function detectTimezone() {
// Try environment variable first
if (process.env.TZ) {
return process.env.TZ;
}
// Try reading /etc/timezone (Linux)
try {
const tz = readFileSync('/etc/timezone', 'utf8').trim();
if (tz) return tz;
} catch (e) {}
// Try reading /etc/localtime symlink (Linux/Mac)
try {
const result = execSync('readlink /etc/localtime', { encoding: 'utf8' }).trim();
const match = result.match(/zoneinfo\/(.+)$/);
if (match) return match[1];
} catch (e) {}
// Default fallback
return 'UTC';
}
// Function to get location via IP geolocation
async function detectLocation() {
return new Promise((resolve) => {
https.get('https://ipapi.co/json/', (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => {
try {
const locationData = JSON.parse(data);
resolve({
city: locationData.city || 'Unknown',
province: locationData.region || 'Unknown',
country: locationData.country_name || 'Unknown',
country_code: locationData.country || 'Unknown',
coordinates: {
latitude: locationData.latitude || 0,
longitude: locationData.longitude || 0
},
timezone: locationData.timezone || detectTimezone(),
ip: locationData.ip || 'Unknown'
});
} catch (e) {
// Fallback to environment variables or defaults
resolve({
city: process.env.CITY || 'Unknown',
province: process.env.PROVINCE || 'Unknown',
country: process.env.COUNTRY || 'Unknown',
coordinates: {
latitude: parseFloat(process.env.LATITUDE) || 0,
longitude: parseFloat(process.env.LONGITUDE) || 0
},
timezone: detectTimezone()
});
}
});
}).on('error', () => {
// Fallback to environment variables or defaults
resolve({
city: process.env.CITY || 'Unknown',
province: process.env.PROVINCE || 'Unknown',
country: process.env.COUNTRY || 'Unknown',
coordinates: {
latitude: parseFloat(process.env.LATITUDE) || 0,
longitude: parseFloat(process.env.LONGITUDE) || 0
},
timezone: detectTimezone()
});
});
});
}
// Initialize timezone and location
let TIMEZONE = detectTimezone();
let LOCATION = null;
// Async initialization
(async () => {
LOCATION = await detectLocation();
if (LOCATION.timezone && LOCATION.timezone !== 'Unknown') {
TIMEZONE = LOCATION.timezone;
}
console.error(`Detected timezone: ${TIMEZONE}`);
console.error(`Detected location: ${LOCATION.city}, ${LOCATION.province}, ${LOCATION.country}`);
})();
// Create server instance
const server = new Server(
{
name: 'time-location-server',
version: '1.0.0',
},
{
capabilities: {
tools: {},
},
}
);
// List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'get_current_time',
description: 'Get the current date and time in your local timezone',
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'get_location',
description: 'Get your current location based on IP geolocation or system settings',
inputSchema: {
type: 'object',
properties: {},
},
},
],
};
});
// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name } = request.params.tool;
switch (name) {
case 'get_current_time': {
const now = new Date();
let localTime;
try {
localTime = new Intl.DateTimeFormat('en-US', {
timeZone: TIMEZONE,
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false,
weekday: 'long',
timeZoneName: 'long'
}).format(now);
} catch (e) {
// Fallback if timezone is invalid
localTime = now.toString();
}
const isoString = now.toISOString();
return {
content: [
{
type: 'text',
text: JSON.stringify({
current_time: localTime,
timezone: TIMEZONE,
iso_time: isoString,
unix_timestamp: Math.floor(now.getTime() / 1000),
utc_offset: new Date().getTimezoneOffset()
}, null, 2),
},
],
};
}
case 'get_location': {
// Wait for location if not yet loaded
if (!LOCATION) {
LOCATION = await detectLocation();
}
return {
content: [
{
type: 'text',
text: JSON.stringify(LOCATION, null, 2),
},
],
};
}
default:
throw new Error(`Unknown tool: ${name}`);
}
});
// Start server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('Time & Location MCP Server running on stdio');
}
main().catch((error) => {
console.error('Server error:', error);
process.exit(1);
});