diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c68732..5a60a0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ ### Fixes +- **CLI metadata flags**: Handle `--help` and `--version` before server startup so users can inspect usage and version information without requiring Mapbox environment configuration. - **Prompt descriptions**: Add missing `driving-traffic` transport mode to `get-directions`, `search-along-route`, and `show-reachable-areas` prompt descriptions - **ground_location_tool**: Use `mapbox/` prefix for isochrone profiles and add `driving-traffic` support - **ground_location_tool**: Reverse geocode now returns neighborhood/locality/place name instead of street address by default diff --git a/src/cli.ts b/src/cli.ts new file mode 100644 index 0000000..9e98c3c --- /dev/null +++ b/src/cli.ts @@ -0,0 +1,88 @@ +// Copyright (c) Mapbox, Inc. +// Licensed under the MIT License. + +interface CliVersionInfo { + name: string; + version: string; +} + +interface CliMetadataResult { + handled: boolean; + exitCode: number; + output?: string; + error?: string; +} + +const OPTIONS = [ + '--help, -h Show this help message', + '--version, -v Show the server version', + '--enable-tools Enable only the comma-separated tools', + '--disable-tools Disable the comma-separated tools', + '--disable-mcp-ui Disable MCP-UI resources' +]; + +const OPTIONS_WITH_VALUES = new Set(['--enable-tools', '--disable-tools']); +const KNOWN_FLAGS = new Set([ + '--help', + '-h', + '--version', + '-v', + '--enable-tools', + '--disable-tools', + '--disable-mcp-ui' +]); + +function formatHelp(versionInfo: CliVersionInfo): string { + return [ + `${versionInfo.name} ${versionInfo.version}`, + '', + 'Usage: mcp-server [options]', + '', + 'Options:', + ...OPTIONS.map((option) => ` ${option}`), + '' + ].join('\n'); +} + +export function handleCliMetadataArgs( + args: string[], + versionInfo: CliVersionInfo +): CliMetadataResult { + if (args.includes('--help') || args.includes('-h')) { + return { + handled: true, + exitCode: 0, + output: formatHelp(versionInfo) + }; + } + + if (args.includes('--version') || args.includes('-v')) { + return { + handled: true, + exitCode: 0, + output: `${versionInfo.version}\n` + }; + } + + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + + if (OPTIONS_WITH_VALUES.has(arg)) { + i++; + continue; + } + + if (arg.startsWith('-') && !KNOWN_FLAGS.has(arg)) { + return { + handled: true, + exitCode: 1, + error: `Unknown option: ${arg}\n` + }; + } + } + + return { + handled: false, + exitCode: 0 + }; +} diff --git a/src/index.ts b/src/index.ts index d0f3dc6..c64ca97 100644 --- a/src/index.ts +++ b/src/index.ts @@ -30,6 +30,7 @@ import { getAllResources } from './resources/resourceRegistry.js'; import { getAllPrompts, getPromptByName } from './prompts/promptRegistry.js'; import { completePromptArgument } from './completions/index.js'; import { getVersionInfo } from './utils/versionUtils.js'; +import { handleCliMetadataArgs } from './cli.js'; import { initializeTracing, shutdownTracing, @@ -61,6 +62,20 @@ if (existsSync(envPath)) { } const versionInfo = getVersionInfo(); +const cliMetadataResult = handleCliMetadataArgs( + process.argv.slice(2), + versionInfo +); + +if (cliMetadataResult.handled) { + if (cliMetadataResult.output) { + process.stdout.write(cliMetadataResult.output); + } + if (cliMetadataResult.error) { + process.stderr.write(cliMetadataResult.error); + } + process.exit(cliMetadataResult.exitCode); +} // Parse configuration from command-line arguments const config = parseToolConfigFromArgs(); diff --git a/test/cli.test.ts b/test/cli.test.ts new file mode 100644 index 0000000..20a2eae --- /dev/null +++ b/test/cli.test.ts @@ -0,0 +1,74 @@ +// Copyright (c) Mapbox, Inc. +// Licensed under the MIT License. + +import { describe, expect, it } from 'vitest'; +import { handleCliMetadataArgs } from '../src/cli.js'; + +const versionInfo = { + name: 'Mapbox MCP server', + version: '1.2.3' +}; + +describe('CLI metadata arguments', () => { + it('handles --help before server startup', () => { + const result = handleCliMetadataArgs(['--help'], versionInfo); + + expect(result).toEqual({ + handled: true, + exitCode: 0, + output: expect.stringContaining('Usage: mcp-server [options]') + }); + }); + + it('handles -h before server startup', () => { + const result = handleCliMetadataArgs(['-h'], versionInfo); + + expect(result).toEqual({ + handled: true, + exitCode: 0, + output: expect.stringContaining('Usage: mcp-server [options]') + }); + }); + + it('handles --version before server startup', () => { + const result = handleCliMetadataArgs(['--version'], versionInfo); + + expect(result).toEqual({ + handled: true, + exitCode: 0, + output: '1.2.3\n' + }); + }); + + it('handles -v before server startup', () => { + const result = handleCliMetadataArgs(['-v'], versionInfo); + + expect(result).toEqual({ + handled: true, + exitCode: 0, + output: '1.2.3\n' + }); + }); + + it('rejects unknown flags before server startup', () => { + const result = handleCliMetadataArgs(['--bogus'], versionInfo); + + expect(result).toEqual({ + handled: true, + exitCode: 1, + error: 'Unknown option: --bogus\n' + }); + }); + + it('does not handle server configuration flags', () => { + const result = handleCliMetadataArgs( + ['--enable-tools', 'version_tool'], + versionInfo + ); + + expect(result).toEqual({ + handled: false, + exitCode: 0 + }); + }); +});