diff --git a/.env.example b/.env.example index 88daead..4828950 100644 --- a/.env.example +++ b/.env.example @@ -9,14 +9,6 @@ # Get your token at: https://account.mapbox.com/access-tokens MAPBOX_ACCESS_TOKEN=your-mapbox-token-here -# ============================================================================= -# Optional MCP Configuration -# ============================================================================= - -# Disable console logging (recommended when using stdio transport) -# Uncomment to enable: -# MCP_DISABLE_LOGGING=true - # ============================================================================= # OpenTelemetry Tracing (Optional) # ============================================================================= @@ -35,9 +27,12 @@ OTEL_SERVICE_NAME=mapbox-mcp-devkit-server # Resource attributes (customize as needed) OTEL_RESOURCE_ATTRIBUTES=service.name=mapbox-mcp-devkit-server,service.version=0.4.5 -# Optional: Console tracing for development (SSE transport only) -# ⚠️ Do NOT enable with stdio transport - it breaks JSON-RPC communication -# OTEL_EXPORTER_CONSOLE_ENABLED=true +# Optional: OTEL diagnostic log level (default: NONE) +# This server uses stdio transport exclusively. OTEL diagnostic logs are disabled +# by default to prevent corrupting JSON-RPC communication. +# Set to DEBUG, INFO, WARN, or ERROR only for troubleshooting OTEL configuration issues. +# Valid values: NONE (default), ERROR, WARN, INFO, DEBUG, VERBOSE +# OTEL_LOG_LEVEL=ERROR # Optional: OTLP authentication headers (for production backends) # OTEL_EXPORTER_OTLP_HEADERS={"Authorization": "Bearer your-token"} diff --git a/CLAUDE.md b/CLAUDE.md index da032fc..ec691ae 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -56,10 +56,10 @@ This server includes OpenTelemetry (OTEL) instrumentation for distributed tracin ### Environment Variables - `OTEL_EXPORTER_OTLP_ENDPOINT` — OTLP endpoint URL (e.g., `http://localhost:4318`). If not set, tracing is disabled. -- `OTEL_EXPORTER_CONSOLE_ENABLED` — Set to `true` to log traces to console (avoid with stdio transport) - `OTEL_TRACING_ENABLED` — Set to `false` to explicitly disable tracing even if endpoint is configured - `OTEL_SERVICE_NAME` — Override service name (default: `mapbox-mcp-devkit-server`) - `OTEL_EXPORTER_OTLP_HEADERS` — JSON string of additional headers for OTLP exporter +- `OTEL_LOG_LEVEL` — OTEL diagnostic log level: `NONE` (default), `ERROR`, `WARN`, `INFO`, `DEBUG`, `VERBOSE`. Defaults to `NONE` to prevent diagnostic logs from corrupting stdio transport. ### What Gets Traced diff --git a/README.md b/README.md index 6b2d824..4db8fd3 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,12 @@ https://github.com/user-attachments/assets/8b1b8ef2-9fba-4951-bc9a-beaed4f6aff6 - [Coordinate Conversion tool](#coordinate-conversion-tool) - [Bounding Box tool](#bounding-box-tool) - [Resources](#resources) - - [Observability & Tracing](#observability--tracing) + - [Observability \& Tracing](#observability--tracing) + - [Features](#features) + - [Quick Start with Jaeger](#quick-start-with-jaeger) + - [Supported Backends](#supported-backends) + - [Documentation](#documentation) + - [Environment Variables](#environment-variables) - [Development](#development) - [Testing](#testing) - [Tool Snapshot Tests](#tool-snapshot-tests) @@ -35,7 +40,7 @@ https://github.com/user-attachments/assets/8b1b8ef2-9fba-4951-bc9a-beaed4f6aff6 - [Using Node.js](#using-nodejs) - [Using Docker](#using-docker) - [Creating New Tools](#creating-new-tools) - - [Environment Variables](#environment-variables) + - [Environment Variables](#environment-variables-1) - [VERBOSE_ERRORS](#verbose_errors) - [Troubleshooting](#troubleshooting) - [Contributing](#contributing) @@ -48,6 +53,8 @@ Get started by integrating with your preferred AI development environment: - [Claude Code Integration](./docs/claude-code-integration.md) - Command-line development with Claude - [Claude Desktop Integration](./docs/claude-desktop-integration.md) - Desktop application integration +- [Cursor Integration](./docs/cursor-integration.md) - Cursor IDE integration +- [VS Code Integration](./docs/vscode-integration.md) - Visual Studio Code with GitHub Copilot ### DXT Package Distribution @@ -592,11 +599,8 @@ npm test -- --updateSnapshot #### Using Node.js ```sh -# Build -npm run build - -# Inspect -npx @modelcontextprotocol/inspector node dist/esm/index.js +# Run the built image +npm run inspect:build ``` #### Using Docker diff --git a/docs/claude-code-integration.md b/docs/claude-code-integration.md index a3d48fb..c77114e 100644 --- a/docs/claude-code-integration.md +++ b/docs/claude-code-integration.md @@ -95,8 +95,8 @@ If you want to use a local version (need to clone and build from this repo): { "mcpServers": { "mapbox-devkit": { - "command": "/path/to/your/node", - "args": ["/path/to/mapbox-mcp-devkit/dist/esm/index.js"], + "command": "node", + "args": ["/absolute/path/to/mapbox-mcp-devkit/dist/esm/index.js"], "env": { "MAPBOX_ACCESS_TOKEN": "your_token_here" } diff --git a/docs/claude-desktop-integration.md b/docs/claude-desktop-integration.md index 7987263..41381ba 100644 --- a/docs/claude-desktop-integration.md +++ b/docs/claude-desktop-integration.md @@ -106,8 +106,8 @@ If you want to use a local version (need to clone and build from this repo): { "mcpServers": { "mapbox-devkit": { - "command": "/path/to/your/node", - "args": ["/path/to/mapbox-mcp-devkit/dist/esm/index.js"], + "command": "node", + "args": ["/absolute/path/to/mapbox-mcp-devkit/dist/esm/index.js"], "env": { "MAPBOX_ACCESS_TOKEN": "your_token_here" } diff --git a/docs/cursor-integration.md b/docs/cursor-integration.md new file mode 100644 index 0000000..fb07a17 --- /dev/null +++ b/docs/cursor-integration.md @@ -0,0 +1,129 @@ +# Cursor Integration + +This guide explains how to configure Cursor IDE for use with the Mapbox MCP DevKit Server. + +## Requirements + +- [Cursor](https://www.cursor.com/) installed +- Mapbox MCP DevKit Server [built locally](../README.md#development-setup) + +```sh +# from repository root: +# using node +npm run build + +# or alternatively, using docker +docker build -t mapbox-mcp-devkit . +``` + +## Setup Instructions + +### Configure Cursor to use Mapbox MCP DevKit Server + +1. Go to Cursor Settings/ MCP Tools and click on "Add Custom MCP". +2. Add either of the following MCP config: + - **NPM version** (recommended for simplicity) + + ```json + { + "mcpServers": { + "MapboxDevKit": { + "type": "stdio", + "command": "npx", + "args": ["-y", "@mapbox/mcp-devkit-server"], + "env": { + "MAPBOX_ACCESS_TOKEN": "" + } + } + } + } + ``` + + - **Docker version** (recommended for isolation) + + ```json + { + "mcpServers": { + "MapboxDevKit": { + "type": "stdio", + "command": "docker", + "args": [ + "run", + "-i", + "--rm", + "--env", + "MAPBOX_ACCESS_TOKEN=", + "mapbox-mcp-devkit" + ] + } + } + } + ``` + + - **Node version** (for local development) + ```json + { + "mcpServers": { + "MapboxDevKit": { + "type": "stdio", + "command": "node", + "args": ["/absolute/path/to/repo/dist/esm/index.js"], + "env": { + "MAPBOX_ACCESS_TOKEN": "" + } + } + } + } + ``` + +3. Click "Save" to apply the configuration. + +## Important Notes + +### Token Scopes + +Your Mapbox access token must have the appropriate scopes for the tools you want to use. See the [main README](../README.md#tools) for required token scopes per tool. + +### Configuration Options + +Additional environment variables you can set: + +- `OTEL_EXPORTER_OTLP_ENDPOINT` - Enable OpenTelemetry tracing (see [tracing docs](./tracing.md)) +- `OTEL_SERVICE_NAME` - Override service name for tracing +- `MAPBOX_API_ENDPOINT` - Override Mapbox API endpoint (advanced) + +## Troubleshooting + +### Server Not Appearing + +If the Mapbox DevKit Server doesn't appear in Cursor's MCP tools: + +1. Check that your `MAPBOX_ACCESS_TOKEN` is valid +2. For Docker: ensure the image is built with `docker images | grep mapbox-mcp-devkit` +3. Check Cursor's logs for any error messages + +### Connection Errors + +If you see JSON-RPC or parsing errors: + +1. If using Node version, verify the path to `dist/esm/index.js` is correct +2. Run `npm run build` to ensure the latest build is available + +### Tool Execution Failures + +If tools fail to execute: + +1. Verify your Mapbox token has the required scopes (see [README](../README.md)) +2. Check if you're hitting rate limits (429 errors) +3. Enable verbose errors by adding `"VERBOSE_ERRORS": "true"` to the env config + +## Example Usage + +Once configured, you can use natural language to interact with Mapbox tools: + +- "List all my Mapbox styles" +- "Show me the bounding box for San Francisco" +- "Convert these coordinates to lat/lng: -122.4194, 37.7749" +- "Create a new Mapbox style called 'my-custom-map'" + +See the [README](../README.md#example-prompts) for more example prompts. diff --git a/docs/tracing-verification.md b/docs/tracing-verification.md index 8a81c90..f8ce56b 100644 --- a/docs/tracing-verification.md +++ b/docs/tracing-verification.md @@ -73,9 +73,7 @@ npm run tracing:jaeger:stop ### Successful Tracing Setup -✅ **Console output shows**: `OpenTelemetry tracing: enabled` - -✅ **Jaeger UI shows traces** for your service +✅ **Jaeger UI shows traces** for your service after tool execution ✅ **Trace details include**: @@ -193,34 +191,16 @@ OTEL_EXPORTER_OTLP_ENDPOINT=https://api.honeycomb.io/v1/traces OTEL_EXPORTER_OTLP_HEADERS='{"x-honeycomb-team":"YOUR_API_KEY"}' ``` -## Alternative Methods - -### Console Tracing (Development Only) - -**⚠️ Not recommended for stdio transport** - -```bash -# Add to .env (only works well with SSE transport) -OTEL_EXPORTER_CONSOLE_ENABLED=true -``` - -This prints traces to stderr. Only use this for debugging, as it can interfere with MCP's stdio communication. - -### Verifying Different Transports +## Diagnostic Logging -#### stdio Transport (Default) - Silent Operation +By default, OpenTelemetry diagnostic logs are disabled to prevent interference with stdio transport. If you need to troubleshoot OTEL configuration issues, you can enable diagnostic logging: ```bash -# Normal operation -npm run inspect:build +# Add to .env - only for debugging OTEL issues +OTEL_LOG_LEVEL=ERROR ``` -#### SSE Transport - Full Logging - -```bash -# If your inspector supports SSE transport -SERVER_TRANSPORT=sse npm run inspect:build -``` +**⚠️ Warning:** Any console output (including OTEL diagnostic logs) can corrupt stdio communication. Only enable diagnostic logging when actively troubleshooting, and disable it once resolved. ## Production Considerations diff --git a/docs/tracing.md b/docs/tracing.md index e8a70e6..a5d292e 100644 --- a/docs/tracing.md +++ b/docs/tracing.md @@ -2,16 +2,11 @@ This MCP server includes comprehensive distributed tracing using OpenTelemetry (OTEL), providing production-ready observability for tool executions and HTTP requests. -## ⚠️ Important MCP Transport Compatibility +## ⚠️ Important: Stdio Transport Only -**Console tracing should be avoided with stdio transport** as console output interferes with MCP's stdio JSON-RPC communication. +**This server uses stdio transport exclusively.** Only OTLP exporters are supported for tracing. -**Transport-specific recommendations:** - -- **stdio transport (default):** Use OTLP exporters only, avoid console tracing -- **SSE transport:** Console tracing is safe to use for development - -The server automatically detects the transport type and adjusts logging behavior accordingly. +Console output is incompatible with stdio and will corrupt JSON-RPC communication. All diagnostic logging is disabled by default to ensure reliable operation. ## Features @@ -56,14 +51,15 @@ The tracing system supports several configuration options through environment va #### Basic Configuration ```bash -# Enable console tracing (development) -OTEL_EXPORTER_CONSOLE_ENABLED=true - -# OTLP HTTP endpoint (production) +# OTLP HTTP endpoint (required to enable tracing) OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 # Optional OTLP headers for authentication OTEL_EXPORTER_OTLP_HEADERS='{"Authorization": "Bearer your-token"}' + +# Optional: OTEL diagnostic log level (default: NONE) +# Only use for troubleshooting OTEL configuration issues +OTEL_LOG_LEVEL=ERROR ``` #### Service Configuration @@ -82,29 +78,23 @@ OTEL_TRACES_SAMPLER=traceidratio OTEL_TRACES_SAMPLER_ARG=0.1 # Sample 10% of traces ``` -### Transport-Specific Configuration +### Enabling Tracing -**For stdio transport (default):** +Tracing is opt-in and disabled by default. To enable tracing, you must configure an OTLP endpoint: ```bash -# ✅ RECOMMENDED: Use OTLP exporter for stdio transport +# Enable tracing by setting the OTLP endpoint OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 -# ❌ AVOID: Console output interferes with stdio JSON-RPC -# OTEL_EXPORTER_CONSOLE_ENABLED=true -``` - -**For SSE transport:** - -```bash -# ✅ SAFE: Console tracing works with SSE transport -SERVER_TRANSPORT=sse -OTEL_EXPORTER_CONSOLE_ENABLED=true +# Optionally customize the service name +OTEL_SERVICE_NAME=mapbox-mcp-devkit-server -# ✅ ALSO GOOD: OTLP exporter works with any transport -OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 +# For debugging OTEL configuration issues only +# OTEL_LOG_LEVEL=ERROR ``` +**Note:** Console exporters are not supported due to stdio transport limitations. + ### Verification To verify tracing is working correctly, see the [Tracing Verification Guide](./tracing-verification.md) which shows how to: diff --git a/docs/vscode-integration.md b/docs/vscode-integration.md new file mode 100644 index 0000000..27e2914 --- /dev/null +++ b/docs/vscode-integration.md @@ -0,0 +1,186 @@ +# VS Code Integration + +This guide explains how to configure Visual Studio Code for use with the Mapbox MCP DevKit Server. + +## Requirements + +- [Visual Studio Code](https://code.visualstudio.com/) installed +- GitHub Copilot extension installed and configured +- Mapbox MCP DevKit Server built locally + +```sh +# from repository root: +# using node +npm install +npm run build + +# or alternatively, using docker +docker build -t mapbox-mcp-devkit . +``` + +## Setup Instructions + +### Configure VS Code to use Mapbox MCP DevKit Server + +1. Open your VS Code `settings.json` file: + - **Mac/Linux**: `Cmd+Shift+P` → "Preferences: Open User Settings (JSON)" + - **Windows**: `Ctrl+Shift+P` → "Preferences: Open User Settings (JSON)" + +2. At the top level of the JSON file, add the MCP configuration: + - **NPM version** (recommended for simplicity) + + ```json + "mcp": { + "servers": { + "MapboxDevKit": { + "type": "stdio", + "command": "npx", + "args": ["-y", "@mapbox/mcp-devkit-server"], + "env": { + "MAPBOX_ACCESS_TOKEN": "" + } + } + } + } + ``` + + - **Docker version** (recommended for isolation) + + ```json + "mcp": { + "servers": { + "MapboxDevKit": { + "type": "stdio", + "command": "docker", + "args": [ + "run", + "-i", + "--rm", + "--env", + "MAPBOX_ACCESS_TOKEN=", + "mapbox-mcp-devkit" + ] + } + } + } + ``` + + - **Node version** (for local development) + ```json + "mcp": { + "servers": { + "MapboxDevKit": { + "type": "stdio", + "command": "node", + "args": [ + "/absolute/path/to/repo/dist/esm/index.js" + ], + "env": { + "MAPBOX_ACCESS_TOKEN": "" + } + } + } + } + ``` + +3. Save the `settings.json` file. + +4. Restart VS Code to apply the changes. + +### Verify Installation + +After restarting VS Code, you should see "Mapbox DevKit" appear in the GitHub Copilot tools menu or MCP servers list. + +## Important Notes + +### Token Scopes + +Your Mapbox access token must have the appropriate scopes for the tools you want to use. See the [main README](../README.md#tools) for required token scopes per tool. + +### Configuration Options + +Additional environment variables you can set: + +- `OTEL_EXPORTER_OTLP_ENDPOINT` - Enable OpenTelemetry tracing (see [tracing docs](./tracing.md)) +- `OTEL_SERVICE_NAME` - Override service name for tracing +- `MAPBOX_API_ENDPOINT` - Override Mapbox API endpoint (advanced) + +## Complete Example Configuration + +Here's a complete `settings.json` example with the NPM version: + +```json +{ + "editor.fontSize": 14, + "editor.tabSize": 2, + "mcp": { + "servers": { + "MapboxDevKit": { + "type": "stdio", + "command": "npx", + "args": ["-y", "@mapbox/mcp-devkit-server"], + "env": { + "MAPBOX_ACCESS_TOKEN": "pk.eyJ1IjoiZXhhbXBsZSIsImEiOiJjbGV4YW1wbGUifQ.example" + } + } + } + } +} +``` + +## Troubleshooting + +### Server Not Appearing + +If the Mapbox DevKit Server doesn't appear in VS Code's MCP servers: + +1. **Check GitHub Copilot**: Ensure GitHub Copilot is installed and active +2. **Verify token**: Check that your `MAPBOX_ACCESS_TOKEN` is valid +3. **Docker image**: If using Docker, verify the image exists: `docker images | grep mapbox-mcp-devkit` +4. **Check Output**: Open "Output" panel → Select "MCP" from dropdown to see logs + +### Connection Errors / JSON-RPC Parsing Errors + +If you see JSON-RPC or parsing errors in the Output panel: + +1. **Build fresh**: Run `npm run build` to ensure you have the latest build +2. **Path verification**: For Node version, verify the path to `dist/esm/index.js` is correct and absolute +3. **Check logs**: Open "Output" panel → Select "MCP" from dropdown to see detailed logs + +### Tool Execution Failures + +If tools fail to execute: + +1. **Token scopes**: Verify your Mapbox token has the required scopes (see [README](../README.md)) +2. **Rate limits**: Check if you're hitting API rate limits (429 errors) +3. **Verbose errors**: Add `"VERBOSE_ERRORS": "true"` to the env config for detailed error messages +4. **Network**: Ensure you can reach `api.mapbox.com` from your network + +## Example Usage + +Once configured, you can use natural language in GitHub Copilot Chat to interact with Mapbox tools: + +``` +@MapboxDevKit list all my Mapbox styles +``` + +``` +@MapboxDevKit show me the bounding box for Tokyo, Japan +``` + +``` +@MapboxDevKit convert these coordinates to lat/lng: -122.4194, 37.7749 +``` + +``` +@MapboxDevKit create a new Mapbox style called 'dark-mode-map' +``` + +See the [README](../README.md#example-prompts) for more example prompts and available tools. + +## Additional Resources + +- [VS Code MCP Documentation](https://code.visualstudio.com/docs/copilot/mcp) +- [GitHub Copilot Documentation](https://docs.github.com/en/copilot) +- [Mapbox Token Scopes](https://docs.mapbox.com/api/overview/#access-tokens-and-token-scopes) +- [OpenTelemetry Tracing Guide](./tracing.md) diff --git a/src/index.ts b/src/index.ts index 45f8cde..8bebca0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,20 +40,9 @@ if (existsSync(envPath)) { process.env[key] = value; envLoadedCount++; } - - // Log success if logging is enabled - if (!process.env.MCP_DISABLE_LOGGING) { - console.error( - `✓ Loaded ${envLoadedCount} environment variables from ${envPath}` - ); - } } catch (error) { envLoadError = error instanceof Error ? error : new Error(String(error)); - if (!process.env.MCP_DISABLE_LOGGING) { - console.error( - `⚠️ Warning loading .env: ${error instanceof Error ? error.message : String(error)}` - ); - } + // Error will be logged via MCP logging messages and traced if tracing is enabled } } @@ -91,23 +80,42 @@ resources.forEach((resource) => { resource.installTo(server); }); -// MCP-compatible logging functions -// Completely suppress logging when MCP_DISABLE_LOGGING is set (for MCP inspector compatibility) -function logIfEnabled(level: 'log' | 'warn' | 'error', ...args: unknown[]) { - if (!process.env.MCP_DISABLE_LOGGING) { - console[level](...args); +async function main() { + // Send MCP logging messages about .env loading + if (envLoadError) { + server.server.sendLoggingMessage({ + level: 'warning', + data: `Failed to load .env file: ${envLoadError.message}` + }); + } else if (envLoadedCount > 0) { + server.server.sendLoggingMessage({ + level: 'info', + data: `Loaded ${envLoadedCount} environment variables from ${envPath}` + }); + } else { + server.server.sendLoggingMessage({ + level: 'debug', + data: 'No .env file found or file was empty' + }); } -} -async function main() { // Initialize OpenTelemetry tracing if not in test mode if (process.env.NODE_ENV !== 'test' && !process.env.VITEST) { try { await initializeTracing(); - logIfEnabled( - 'log', - `OpenTelemetry tracing: ${isTracingInitialized() ? 'enabled' : 'disabled'}` - ); + + // Send MCP logging message about tracing status + if (isTracingInitialized()) { + server.server.sendLoggingMessage({ + level: 'info', + data: 'OpenTelemetry tracing enabled' + }); + } else { + server.server.sendLoggingMessage({ + level: 'debug', + data: 'OpenTelemetry tracing disabled (no OTLP endpoint configured)' + }); + } // Record .env loading as a span (retrospectively since it happened before tracing init) if (isTracingInitialized()) { @@ -142,24 +150,21 @@ async function main() { span.end(); } } catch (error) { - logIfEnabled('warn', 'Failed to initialize tracing:', error); + // Log tracing initialization failure + server.server.sendLoggingMessage({ + level: 'warning', + data: `Failed to initialize tracing: ${error instanceof Error ? error.message : String(error)}` + }); } } - // Send MCP logging message about environment variables - server.server.sendLoggingMessage({ - level: 'info', - data: 'Environment variables loaded from .env file' - }); - const relevantEnvVars = Object.freeze({ MAPBOX_ACCESS_TOKEN: process.env.MAPBOX_ACCESS_TOKEN ? '***' : undefined, MAPBOX_API_ENDPOINT: process.env.MAPBOX_API_ENDPOINT, OTEL_SERVICE_NAME: process.env.OTEL_SERVICE_NAME, OTEL_EXPORTER_OTLP_ENDPOINT: process.env.OTEL_EXPORTER_OTLP_ENDPOINT, - OTEL_EXPORTER_CONSOLE_ENABLED: process.env.OTEL_EXPORTER_CONSOLE_ENABLED, OTEL_TRACING_ENABLED: process.env.OTEL_TRACING_ENABLED, - MCP_DISABLE_LOGGING: process.env.MCP_DISABLE_LOGGING, + OTEL_LOG_LEVEL: process.env.OTEL_LOG_LEVEL, NODE_ENV: process.env.NODE_ENV }); @@ -178,15 +183,31 @@ async function shutdown() { // Shutdown tracing try { await shutdownTracing(); - } catch (e) { - logIfEnabled('error', 'Error shutting down tracing:', e); + server.server.sendLoggingMessage({ + level: 'info', + data: 'Server shutting down gracefully' + }); + } catch (error) { + server.server.sendLoggingMessage({ + level: 'warning', + data: `Error during shutdown: ${error instanceof Error ? error.message : String(error)}` + }); } process.exit(0); } -function exitWithLog(message: string, error: unknown, code = 1) { - logIfEnabled('error', message, error); +function exitWithError(error: unknown, code = 1) { + // Use MCP logging for fatal errors + try { + server.server.sendLoggingMessage({ + level: 'error', + data: `Fatal error: ${error instanceof Error ? error.message : String(error)}` + }); + } catch { + // If MCP logging fails, we have no choice but to use console + console.error('Fatal error:', error); + } process.exit(code); } @@ -200,11 +221,7 @@ function exitWithLog(message: string, error: unknown, code = 1) { }); }); -process.on('uncaughtException', (err) => - exitWithLog('Uncaught exception:', err) -); -process.on('unhandledRejection', (reason) => - exitWithLog('Unhandled rejection:', reason) -); +process.on('uncaughtException', (err) => exitWithError(err)); +process.on('unhandledRejection', (reason) => exitWithError(reason)); -main().catch((error) => exitWithLog('Fatal error starting MCP server:', error)); +main().catch((error) => exitWithError(error)); diff --git a/src/tools/get-mapbox-doc-source-tool/GetMapboxDocSourceTool.ts b/src/tools/get-mapbox-doc-source-tool/GetMapboxDocSourceTool.ts index ad7cb00..8b758a6 100644 --- a/src/tools/get-mapbox-doc-source-tool/GetMapboxDocSourceTool.ts +++ b/src/tools/get-mapbox-doc-source-tool/GetMapboxDocSourceTool.ts @@ -33,7 +33,6 @@ export class GetMapboxDocSourceTool extends BaseTool< } protected async execute( - // eslint-disable-next-line @typescript-eslint/no-unused-vars _input: GetMapboxDocSourceInput ): Promise { try { diff --git a/src/utils/tracing.ts b/src/utils/tracing.ts index 8e64624..eeb7124 100644 --- a/src/utils/tracing.ts +++ b/src/utils/tracing.ts @@ -9,11 +9,43 @@ import { ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions'; import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; -import { trace, SpanStatusCode, SpanKind, type Span } from '@opentelemetry/api'; +import { + trace, + SpanStatusCode, + SpanKind, + type Span, + diag, + DiagLogLevel +} from '@opentelemetry/api'; import { getVersionInfo } from './versionUtils.js'; import { ATTR_SERVICE_INSTANCE_ID } from '@opentelemetry/semantic-conventions/incubating'; import { type HttpRequest } from './types.js'; +// Suppress OpenTelemetry diagnostic logging IMMEDIATELY to avoid polluting stdio +// This must happen at module load time, before any OTEL operations +// Use OTEL_LOG_LEVEL env var to override if needed for debugging +const configureOtelDiagnostics = () => { + const logLevel = process.env.OTEL_LOG_LEVEL + ? DiagLogLevel[ + process.env.OTEL_LOG_LEVEL.toUpperCase() as keyof typeof DiagLogLevel + ] + : DiagLogLevel.NONE; + + diag.setLogger( + { + verbose: () => {}, + debug: () => {}, + info: () => {}, + warn: () => {}, + error: () => {} + }, + logLevel + ); +}; + +// Configure diagnostics at module load time +configureOtelDiagnostics(); + // Global SDK instance let sdk: NodeSDK | null = null; let isTracingEnabled = false; @@ -117,8 +149,8 @@ export function createToolExecutionContext( * Initialize OpenTelemetry tracing * Should be called once at application startup * - * For MCP servers using stdio transport, console output should be avoided. - * This implementation automatically detects and handles MCP compatibility. + * This server uses stdio transport exclusively. Only OTLP exporters are supported. + * Console output is incompatible with stdio and will corrupt JSON-RPC communication. */ export async function initializeTracing(): Promise { // Skip initialization if already initialized or if running in test environment @@ -145,39 +177,24 @@ export async function initializeTracing(): Promise { 'service.git.tag': versionInfo.tag }); - // Configure exporters - const exporters = []; - - // Console exporter for development (avoid in stdio transport) - if (process.env.OTEL_EXPORTER_CONSOLE_ENABLED === 'true') { - const { ConsoleSpanExporter } = await import( - '@opentelemetry/sdk-trace-base' - ); - exporters.push(new ConsoleSpanExporter()); - } - - // OTLP HTTP exporter for production + // OTLP HTTP exporter - only supported exporter for stdio transport const otlpEndpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT; - if (otlpEndpoint) { - exporters.push( - new OTLPTraceExporter({ - url: `${otlpEndpoint}/v1/traces`, - headers: process.env.OTEL_EXPORTER_OTLP_HEADERS - ? JSON.parse(process.env.OTEL_EXPORTER_OTLP_HEADERS) - : {} - }) - ); - } - - // Skip tracing if no exporters configured - if (exporters.length === 0) { + if (!otlpEndpoint) { + // Skip tracing if no OTLP endpoint configured return; } + const exporter = new OTLPTraceExporter({ + url: `${otlpEndpoint}/v1/traces`, + headers: process.env.OTEL_EXPORTER_OTLP_HEADERS + ? JSON.parse(process.env.OTEL_EXPORTER_OTLP_HEADERS) + : {} + }); + // Create SDK instance sdk = new NodeSDK({ resource, - traceExporter: exporters[0], + traceExporter: exporter, instrumentations: [ getNodeAutoInstrumentations({ // Disable instrumentations that might be too noisy