Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions README.zh-TW.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 客戶端:
Expand Down Expand Up @@ -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
```

## 開發
Expand Down
21 changes: 16 additions & 5 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,17 @@
// Also try to load from the package installation directory
dotenvConfig({ path: resolve(__dirname, "../.env") });

export async function startServer(port?: number, apiKey?: string): Promise<void> {
export async function startServer(port?: number, apiKey?: string, host?: string): Promise<void> {
// Override environment variables with CLI arguments if provided
if (port) {
process.env.MCP_SERVER_PORT = port.toString();
}
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)");
Expand All @@ -55,10 +58,12 @@

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);
Expand Down Expand Up @@ -95,7 +100,7 @@
"local-rank-tracker",
] as const;

async function execTool(toolName: string, params: any, apiKey: string): Promise<any> {

Check warning on line 103 in src/cli.ts

View workflow job for this annotation

GitHub Actions / ci

Unexpected any. Specify a different type

Check warning on line 103 in src/cli.ts

View workflow job for this annotation

GitHub Actions / ci

Unexpected any. Specify a different type
const searcher = new PlacesSearcher(apiKey);

switch (toolName) {
Expand Down Expand Up @@ -191,7 +196,7 @@
try {
const result = await searcher.geocode(address);
return { address, ...result };
} catch (error: any) {

Check warning on line 199 in src/cli.ts

View workflow job for this annotation

GitHub Actions / ci

Unexpected any. Specify a different type
return { address, success: false, error: error.message };
}
})
Expand Down Expand Up @@ -234,7 +239,7 @@
const packageJsonPath = resolve(__dirname, "../package.json");
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
packageVersion = packageJson.version;
} catch (e) {

Check warning on line 242 in src/cli.ts

View workflow job for this annotation

GitHub Actions / ci

'e' is defined but never used
packageVersion = "0.0.0";
}

Expand Down Expand Up @@ -285,7 +290,7 @@
const result = await execTool(argv.tool as string, params, argv.apikey as string);
console.log(JSON.stringify(result, null, 2));
process.exit(0);
} catch (error: any) {

Check warning on line 293 in src/cli.ts

View workflow job for this annotation

GitHub Actions / ci

Unexpected any. Specify a different type
console.error(JSON.stringify({ error: error.message }, null, 2));
process.exit(1);
}
Expand Down Expand Up @@ -359,7 +364,7 @@

const searcher = new PlacesSearcher(argv.apikey as string);
const concurrency = Math.min(Math.max(argv.concurrency as number, 1), 50);
const results: any[] = [];

Check warning on line 367 in src/cli.ts

View workflow job for this annotation

GitHub Actions / ci

Unexpected any. Specify a different type
let completed = 0;

// Process with concurrency limit
Expand All @@ -381,7 +386,7 @@
try {
const result = await searcher.geocode(address);
results[index] = { address, ...result };
} catch (error: any) {

Check warning on line 389 in src/cli.ts

View workflow job for this annotation

GitHub Actions / ci

Unexpected any. Specify a different type
results[index] = { address, success: false, error: error.message };
}
completed++;
Expand Down Expand Up @@ -423,6 +428,11 @@
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",
Expand All @@ -437,6 +447,7 @@
.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.)"],
]);
},
Expand All @@ -463,7 +474,7 @@
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);
});
Expand Down
9 changes: 5 additions & 4 deletions src/core/BaseMcpServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
description: string;
schema: Record<string, z.ZodTypeAny>;
annotations?: ToolAnnotations;
action: (params: any) => Promise<any>;

Check warning on line 22 in src/core/BaseMcpServer.ts

View workflow job for this annotation

GitHub Actions / ci

Unexpected any. Specify a different type

Check warning on line 22 in src/core/BaseMcpServer.ts

View workflow job for this annotation

GitHub Actions / ci

Unexpected any. Specify a different type
}

export interface SessionContext {
Expand Down Expand Up @@ -53,7 +53,7 @@
inputSchema: z.object(tool.schema),
annotations: tool.annotations,
},
async (params: any) => tool.action(params)

Check warning on line 56 in src/core/BaseMcpServer.ts

View workflow job for this annotation

GitHub Actions / ci

Unexpected any. Specify a different type
);
});
return server;
Expand All @@ -74,7 +74,7 @@
Logger.log(`${this.serverName} connected and ready to process requests`);
}

async startHttpServer(port: number): Promise<void> {
async startHttpServer(port: number, host: string = "0.0.0.0"): Promise<void> {
const app = express();
app.use(express.json());

Expand Down Expand Up @@ -174,9 +174,10 @@
// 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`);
});
}

Expand Down
Loading