The ls-mcp project is designed as a modular, service-oriented architecture for discovering and analyzing Model Context Protocol (MCP) server configurations. The design emphasizes separation of concerns, testability, and future extensibility.
The project is composed of the main ls-mcp application and a new, separate package called agent-files.
ls-mcp: The main application that provides the CLI, orchestrates the discovery process, and handles features like process detection and output rendering.agent-files: A dedicated package that contains the logic for discovering, parsing, and handling MCP configuration file paths.
The ls-mcp application depends on the agent-files package to handle the low-level details of file discovery and parsing.
The DirectoryBubbleService provides intelligent directory traversal capabilities to enhance the discovery of local MCP configuration files. It automatically searches parent directories when local configs are not found in the current directory, improving Developer Experience. This service is part of the agent-files package.
- Intelligent Traversal: Automatically bubbles up directory tree until finding config files or reaching boundaries
- Boundary Safety: Stops at home directory (
~) or root directory (/) to prevent infinite loops - Performance Optimized: Stops at first encounter of matching config file (closest to current directory)
- Error Resilient: Silently handles permission errors, non-existent directories, and other filesystem issues
- Symlink Aware: Follows symlinks during traversal for comprehensive coverage
Searches for a local MCP configuration file by traversing up the directory tree.
async findLocalConfigInParentDirectories(
localPath: string,
startDir: string
): Promise<string | null>Flow:
- Check if config file exists in start directory
- If not found, traverse up to parent directory
- Continue until config file is found or boundaries are reached
- Return absolute path to found config file or null
Checks if a specific configuration file exists in a given directory.
Safely gets the parent directory path, handling edge cases like root directory.
- Optional Feature: Directory bubbling is controlled by constructor options in
MCPConfigService. - Local Paths Only: Only applies to paths marked as
'local'type. - Seamless Integration: Works transparently with existing configuration discovery logic.
const bubbleService = new DirectoryBubbleService()
// Find .vscode/mcp.json starting from nested directory
const configPath = await bubbleService.findLocalConfigInParentDirectories(
'.vscode/mcp.json',
'/projects/my-project/backend/services'
)
// Returns: '/projects/my-project/.vscode/mcp.json'The CLI serves as the entry point for users to interact with the MCP discovery system. It provides a simple command-line interface for discovering and reporting MCP server configurations.
- Single Responsibility: Only handles command-line argument parsing and output formatting
- Minimal Logic: Delegates all business logic to the main application layer
- User-Friendly: Provides clear error messages and helpful output
- Parse Arguments: Handle command-line options and flags
- Initialize Services: Create the main MCPFiles instance
- Execute Discovery: Call the discovery methods
- Format Output: Use RenderService to format results
- Handle Errors: Provide meaningful error messages and exit codes
# Basic discovery
ls-mcp
# Verbose output
ls-mcp --verbose
# JSON output
ls-mcp --json
# Filter by application
ls-mcp --app claudeThe main application file serves as the orchestrator, coordinating between different services and providing a clean API for the CLI and other consumers.
- Service Coordination: Orchestrates interactions between different services.
- Error Handling: Provides unified error handling and reporting.
- Data Transformation: Converts service outputs into the expected format.
- Process Detection: Integrates with MCPServerManagerService for server status.
Discovers and processes all MCP configurations across supported AI applications.
async findFiles(): Promise<MCPFileGroupsResultRecord>Flow:
- Get MCP file groups from
MCPConfigService. - For each group, process configuration files.
- Extract server information from each file.
- Detect running processes for each server.
- Update server status (running/stopped).
- Return structured results.
Provides access to the underlying MCPConfigService for advanced usage.
getMCPConfigService(): MCPConfigService- Facade Pattern: Simplifies complex service interactions.
- Dependency Injection: Services are injected and can be mocked for testing.
- Error Aggregation: Collects errors from multiple services and provides unified reporting.
Central orchestrator for MCP configuration operations, providing a unified interface for configuration discovery and parsing. It consumes the agent-files package for low-level file operations.
- Path Resolution: Coordinates with
MCPPathRegistry(fromagent-files) for OS-specific paths. - File Parsing: Coordinates with
MCPConfigParser(fromagent-files) for configuration parsing. - Data Aggregation: Combines results from multiple sources.
- Error Handling: Provides meaningful error messages for different failure scenarios.
- Directory Bubbling: Uses the
DirectoryBubbleService(fromagent-files) to intelligently traverse parent directories.
This package contains the core logic for file discovery and parsing.
- Purpose: Handles OS-specific path resolution for MCP configuration files across different AI applications.
- Design: Static configuration for paths, platform detection, and support for custom app registration.
- Purpose: Handles parsing and validation of MCP configuration files in various formats (JSON, JSONC).
- Features: Supports multiple configuration key names (
servers,mcpServers, etc.).
- Purpose: Handles intelligent directory traversal to find local MCP config files in parent directories.
Manages MCP server process detection and status reporting.
- Process Detection: Identifies running MCP server processes.
- Status Reporting: Provides running/stopped status for each server.
- Transport Support: Maps MCP config
typefield to internaltransportfield for consistent UI display. - Error Handling: Gracefully handles process detection failures.
Handles output formatting and display of MCP configuration information.
- Human-Readable: Formatted tables and summaries.
- JSON: Structured data output.
- Custom: Extensible formatting options.
CLI → Main.ts → MCPConfigService → agent-files (MCPPathRegistry) → File System
File System → agent-files (MCPConfigParser) → MCPConfigService → Main.ts → CLI
Main.ts → MCPServerManagerService → Process List → Status Update
Status Data → RenderService → Formatted Output → CLI Display
Real MCP server configuration files use the type field to specify transport:
{
"mcpServers": {
"example-server": {
"command": "npx",
"args": ["example-mcp-server"],
"type": "stdio"
},
"http-server": {
"type": "http",
"url": "https://example.com/mcp"
},
"streamable-server": {
"type": "streamable-http",
"url": "https://streamable.example.com/mcp"
}
}
}Supported Transport Types:
stdio: Standard input/output communicationsse: Server-Sent Events communicationhttp: HTTP-based communicationstreamable-http: Synonym for HTTP (treated identically tohttp)
Our application maintains a clean separation between external and internal data:
type: Source of truth from MCP config files (stdio, sse, http, streamable-http)transport: Internal field populated fromtypefor consistent UI display- Mapping:
type→transporthappens at the data layer (MCPConfigService) - Special Handling:
streamable-httpis mapped tohttpsince they are synonyms
MCP Config File (type: "stdio")
↓
MCPConfigParser (extracts type)
↓
MCPConfigService (maps type → transport)
↓
Internal Data Model (transport: "stdio")
↓
UI Components (TRANSPORT column, summary counts)
- Clear Separation: External config format vs. internal app data
- Consistent UI: All transport information comes from one source (
transport) - Maintainable: Changes to config format only affect the mapping layer
- Type Safety: Internal interfaces clearly define expected data structure
When MCP server configurations don't explicitly specify a type field, the system automatically infers transport types using intelligent pattern matching:
Rule 1: URL-based Inference
{
"servers": {
"web-server": {
"url": "https://example.com/mcp"
// No type field → automatically inferred as "http"
// Hostname "example.com" will be displayed in SOURCE column
}
}
}Rule 2-4: Args-based Inference
{
"servers": {
"stdio-server": {
"command": "npx",
"args": ["-y", "stdio-mcp-server", "--stdio"]
// No type field → automatically inferred as "stdio" from --stdio flag
},
"http-server": {
"command": "npx",
"args": ["-y", "http-mcp-server", "--http", "--port", "3000"]
// No type field → automatically inferred as "http" from --http flag
},
"sse-server": {
"command": "npx",
"args": ["-y", "sse-mcp-server", "--sse", "--endpoint", "/mcp"]
// No type field → automatically inferred as "sse" from --sse flag
}
}
}Rule 5: Default Inference
{
"servers": {
"generic-server": {
"command": "npx",
"args": ["-y", "generic-mcp-server"]
// No type field, no transport indicators → defaults to "stdio"
}
}
}- Explicit Type:
"type": "stdio"(highest priority) - URL Detection: Presence of
urlfield →http - Args Analysis: Keywords in
argsarray → corresponding transport - Default Fallback:
commandpresent →stdio(most common case)
- Better User Experience: Transport types visible even when configs don't specify them
- Improved Accuracy: More accurate transport counts in CLI summary
- Backward Compatibility: Works with existing MCP server configurations
- Intelligent Defaults: Reasonable assumptions based on configuration patterns
- Comprehensive Coverage: Handles all common MCP server configuration scenarios
The URL hostname extraction feature (Feature #80) improves the readability of MCP server configurations by displaying only the hostname portion for URL-based servers in the SOURCE column.
export function extractHostname(urlString: string): string {
try {
// Handle URLs without protocol by adding a default one
let urlToParse = urlString
if (!urlToParse.includes('://')) {
urlToParse = `http://${urlToParse}`
}
const url = new URL(urlToParse)
return url.hostname
} catch (error) {
// If URL parsing fails, return the original string
return urlString
}
}- MCPConfigService: Extracts hostname during server configuration conversion
- MCPServerManagerService: Sets source field to hostname when URL is available
- Fallback Handling: Returns original string if URL parsing fails
- HTTP/HTTPS: Standard web protocols
- Custom Protocols: Any protocol supported by Node.js URL constructor
- Protocol-less URLs: Automatically adds
http://for parsing - IP Addresses: Handles both IPv4 and IPv6 addresses
- Port Numbers: Port information is stripped from display
- Query Parameters: Query strings are ignored in hostname extraction
- Cleaner Output: SOURCE column is more readable and concise
- Better UX: Users can quickly identify server location without URL clutter
- Consistent Display: All URL-based servers show consistent hostname format
- Robust Parsing: Handles various URL formats gracefully with fallback
Before: "http://localhost:3000/mcp" → After: "localhost"
Before: "https://api.example.com/v1/mcp" → After: "api.example.com"
Before: "192.168.1.100:8080" → After: "192.168.1.100"
Before: "invalid-url" → After: "invalid-url" (fallback)
- Individual file failures don't stop the entire discovery process
- Unsupported operating systems are handled gracefully
- Missing configuration files are logged but don't cause failures
- File System Errors: File not found, permission denied, etc.
- Parsing Errors: Invalid JSON, malformed configuration
- OS Errors: Unsupported operating system
- Process Errors: Process detection failures
- Clear, actionable error messages
- Appropriate exit codes for different error types
- Verbose logging for debugging via
NODE_DEBUG=ls-mcpenvironment variable
- Each service is tested in isolation
- Mocked dependencies prevent external system access
- Comprehensive error scenario coverage
- Service interactions are tested
- End-to-end workflows are validated
- Real configuration files are used (fixtures)
- High-level tests use mocking to prevent filesystem access
- Service tests use fixture files within the project directory
- CI compatibility is maintained across different operating systems
- Services are designed to be easily extracted to separate npm packages
- Clear interfaces and minimal dependencies
- Consistent error handling and logging patterns
- Support for custom AI application definitions
- Extensible configuration file format support
- Custom output formatters and renderers
- Linux support can be added by extending MCPPathRegistry
- Additional operating systems can be supported
- Platform-specific optimizations can be implemented
- Minimal file system operations
- Efficient path resolution caching
- Parallel processing where possible
- Streaming for large configuration files
- Efficient data structures for server information
- Minimal object creation during processing
- Efficient process list scanning
- Caching of process information
- Timeout handling for slow operations
- Uses Node.js core
util.debuglogfor conditional logging - Debug namespace:
ls-mcp - Enabled via
NODE_DEBUG=ls-mcpenvironment variable - Provides detailed information about file parsing, server detection, and error handling
- Normal mode: Clean output with essential information only
- Debug mode: Verbose logging including file access attempts, parsing results, and error details
- Error mode: Always shows critical errors regardless of debug setting
- File parsing failures and reasons
- Skipped files and access issues
- Server detection results
- Configuration validation outcomes
- Path traversal prevention
- Permission checking for sensitive files
- Safe handling of user-provided paths
- Secure process detection methods
- Minimal process information exposure
- Safe handling of process metadata
- Input sanitization for configuration files
- Safe parsing of untrusted content
- Validation of server command paths