diff --git a/CHANGELOG.md b/CHANGELOG.md index bd3a37c..a910ffe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 0.0.52 + +- feat: add `--host` CLI option to bind server to a specific hostname (#76) + ## 0.0.51 - fix: improve transit error messages for unsupported regions (#75) diff --git a/README.md b/README.md index d8f99ab..0487908 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,9 @@ For multi-session deployments, per-request API key isolation, or remote access: ```bash npx @cablate/mcp-google-map --port 3000 --apikey "YOUR_API_KEY" + +# Bind to all interfaces for remote access (e.g. Docker, LAN) +npx @cablate/mcp-google-map --host 0.0.0.0 --port 3000 --apikey "YOUR_API_KEY" ``` Then configure your MCP client: @@ -198,6 +201,7 @@ API keys can be provided in three ways (priority order): ```env GOOGLE_MAPS_API_KEY=your_api_key_here MCP_SERVER_PORT=3000 + MCP_SERVER_HOST=0.0.0.0 ``` ## Development diff --git a/README.zh-TW.md b/README.zh-TW.md index 1de08d1..e0c3525 100644 --- a/README.zh-TW.md +++ b/README.zh-TW.md @@ -126,6 +126,9 @@ npx @cablate/mcp-google-map --port 3000 --apikey "YOUR_API_KEY" ```bash npx @cablate/mcp-google-map --port 3000 --apikey "YOUR_API_KEY" + +# 綁定所有網路介面以支援遠端存取(例如 Docker、區域網路) +npx @cablate/mcp-google-map --host 0.0.0.0 --port 3000 --apikey "YOUR_API_KEY" ``` 然後設定你的 MCP 客戶端: @@ -196,6 +199,7 @@ API key 可透過三種方式提供(優先順序): ```env GOOGLE_MAPS_API_KEY=your_api_key_here MCP_SERVER_PORT=3000 + MCP_SERVER_HOST=0.0.0.0 ``` ## 開發 diff --git a/src/cli.ts b/src/cli.ts index 3524279..2b964cf 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -22,7 +22,7 @@ dotenvConfig({ path: resolve(process.cwd(), ".env") }); // Also try to load from the package installation directory dotenvConfig({ path: resolve(__dirname, "../.env") }); -export async function startServer(port?: number, apiKey?: string): Promise { +export async function startServer(port?: number, apiKey?: string, host?: string): Promise { // Override environment variables with CLI arguments if provided if (port) { process.env.MCP_SERVER_PORT = port.toString(); @@ -30,6 +30,9 @@ export async function startServer(port?: number, apiKey?: string): Promise if (apiKey) { process.env.GOOGLE_MAPS_API_KEY = apiKey; } + if (host) { + process.env.MCP_SERVER_HOST = host; + } Logger.log("🚀 Starting Google Maps MCP Server..."); Logger.log("📍 18 tools registered (set GOOGLE_MAPS_ENABLED_TOOLS to limit)"); @@ -55,10 +58,12 @@ export async function startServer(port?: number, apiKey?: string): Promise try { const server = new BaseMcpServer(config.name, filterTools(config.tools)); - Logger.log(`🔧 [${config.name}] Initializing MCP Server in HTTP mode on port ${serverPort}...`); - await server.startHttpServer(serverPort); + const serverHost = process.env.MCP_SERVER_HOST || "0.0.0.0"; + Logger.log(`🔧 [${config.name}] Initializing MCP Server in HTTP mode on ${serverHost}:${serverPort}...`); + await server.startHttpServer(serverPort, serverHost); + const displayHost = serverHost === "0.0.0.0" ? "localhost" : serverHost; Logger.log(`✅ [${config.name}] MCP Server started successfully!`); - Logger.log(` 🌐 Endpoint: http://localhost:${serverPort}/mcp`); + Logger.log(` 🌐 Endpoint: http://${displayHost}:${serverPort}/mcp`); Logger.log(` 📚 Tools: ${config.tools.length} available`); } catch (error) { Logger.error(`❌ [${config.name}] Failed to start MCP Server on port ${serverPort}:`, error); @@ -423,6 +428,11 @@ if (isRunDirectly || isMainModule) { description: "Port to run the MCP server on", default: process.env.MCP_SERVER_PORT ? parseInt(process.env.MCP_SERVER_PORT) : 3000, }) + .option("host", { + type: "string", + description: "Hostname to bind the server to (e.g. 0.0.0.0 for all interfaces)", + default: process.env.MCP_SERVER_HOST || "0.0.0.0", + }) .option("apikey", { alias: "k", type: "string", @@ -437,6 +447,7 @@ if (isRunDirectly || isMainModule) { .example([ ["$0", "Start HTTP server with default settings"], ['$0 --port 3000 --apikey "your_api_key"', "Start HTTP with custom port and API key"], + ["$0 --host 0.0.0.0 --port 3000", "Start HTTP accessible from all interfaces"], ["$0 --stdio", "Start in stdio mode (for Claude Desktop, Cursor, etc.)"], ]); }, @@ -463,7 +474,7 @@ if (isRunDirectly || isMainModule) { Logger.log(""); } - startServer(argv.port as number, argv.apikey as string).catch((error) => { + startServer(argv.port as number, argv.apikey as string, argv.host as string).catch((error) => { Logger.error("❌ Failed to start server:", error); process.exit(1); }); diff --git a/src/core/BaseMcpServer.ts b/src/core/BaseMcpServer.ts index d7e3f4b..76a5f7b 100644 --- a/src/core/BaseMcpServer.ts +++ b/src/core/BaseMcpServer.ts @@ -74,7 +74,7 @@ export class BaseMcpServer { Logger.log(`${this.serverName} connected and ready to process requests`); } - async startHttpServer(port: number): Promise { + async startHttpServer(port: number, host: string = "0.0.0.0"): Promise { const app = express(); app.use(express.json()); @@ -174,9 +174,10 @@ export class BaseMcpServer { // Handle DELETE requests for session termination app.delete("/mcp", handleSessionRequest); - this.httpServer = app.listen(port, () => { - Logger.log(`[${this.serverName}] HTTP server listening on port ${port}`); - Logger.log(`[${this.serverName}] MCP endpoint available at http://localhost:${port}/mcp`); + const displayHost = host === "0.0.0.0" ? "localhost" : host; + this.httpServer = app.listen(port, host, () => { + Logger.log(`[${this.serverName}] HTTP server listening on ${host}:${port}`); + Logger.log(`[${this.serverName}] MCP endpoint available at http://${displayHost}:${port}/mcp`); }); }