{"id":26289950,"url":"https://github.com/aashari/boilerplate-mcp-server","last_synced_at":"2026-02-17T10:40:43.051Z","repository":{"id":283817630,"uuid":"953003162","full_name":"aashari/boilerplate-mcp-server","owner":"aashari","description":"TypeScript Model Context Protocol (MCP) server boilerplate providing IP lookup tools/resources. Includes CLI support and extensible structure for connecting AI systems (LLMs) to external data sources like ip-api.com. Ideal template for creating new MCP integrations via Node.js.","archived":false,"fork":false,"pushed_at":"2026-02-04T03:48:58.000Z","size":2102,"stargazers_count":66,"open_issues_count":13,"forks_count":22,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-04T14:53:57.423Z","etag":null,"topics":["ai","ai-integration","anthropic","boilerplate","claude","llm","mcp","mcp-server","model-context-protocol","resources","server","tooling","tools","typescript"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aashari.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-03-22T10:49:06.000Z","updated_at":"2026-02-04T03:49:00.000Z","dependencies_parsed_at":"2025-05-03T00:23:16.510Z","dependency_job_id":"5678b863-e8ae-498b-8b59-629250498eb2","html_url":"https://github.com/aashari/boilerplate-mcp-server","commit_stats":null,"previous_names":["aashari/boilerplate-mcp-server"],"tags_count":68,"template":false,"template_full_name":null,"purl":"pkg:github/aashari/boilerplate-mcp-server","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aashari%2Fboilerplate-mcp-server","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aashari%2Fboilerplate-mcp-server/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aashari%2Fboilerplate-mcp-server/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aashari%2Fboilerplate-mcp-server/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aashari","download_url":"https://codeload.github.com/aashari/boilerplate-mcp-server/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aashari%2Fboilerplate-mcp-server/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29540277,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-17T08:11:05.436Z","status":"ssl_error","status_checked_at":"2026-02-17T08:09:38.860Z","response_time":100,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","ai-integration","anthropic","boilerplate","claude","llm","mcp","mcp-server","model-context-protocol","resources","server","tooling","tools","typescript"],"created_at":"2025-03-14T23:17:26.683Z","updated_at":"2026-02-17T10:40:43.043Z","avatar_url":"https://github.com/aashari.png","language":"TypeScript","funding_links":[],"categories":["📚 Projects (1974 total)","APIs and HTTP Requests","🤖 AI/ML","Developer Tools"],"sub_categories":["MCP Servers"],"readme":"# Boilerplate MCP Server\n\nA production-ready foundation for developing custom Model Context Protocol (MCP) servers in TypeScript. Provides a complete layered architecture pattern, working example implementation, and comprehensive developer infrastructure to connect AI assistants with external APIs and data sources.\n\n[![NPM Version](https://img.shields.io/npm/v/@aashari/boilerplate-mcp-server)](https://www.npmjs.com/package/@aashari/boilerplate-mcp-server)\n[![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC)\n\n\u003e **Latest Update (Feb 2026)**: Updated to MCP SDK 1.26.0 and Zod 4.3.6 with continued modern `registerTool` patterns and streamable HTTP improvements. See [docs/MODERNIZATION.md](docs/MODERNIZATION.md) for details.\n\n## Features\n\n- **Security First**: DNS rebinding protection, localhost-only binding, secure error handling\n- **Dual Transport Support**: STDIO and Streamable HTTP transports with automatic fallback\n- **Layered Architecture**: Clean separation between CLI, tools, resources, prompts, controllers, services, and utilities\n- **Type Safety**: Full TypeScript implementation with Zod v4.3.6 schema validation\n- **All MCP Primitives**: Tools, resources, and prompts (with examples)\n- **ResourceLink Pattern**: Token-efficient resource references for large responses\n- **TOON Output Format**: Token-Oriented Object Notation for 30-60% fewer tokens than JSON\n- **JMESPath Filtering**: Extract only needed fields from responses to reduce token costs\n- **Raw Response Logging**: Automatic logging of large API responses to `/tmp/mcp/\u003cproject\u003e/` with truncation guidance\n- **Modern SDK**: Uses MCP SDK v1.26.0 with `registerTool` API pattern (ready for v2 migration)\n- **Complete IP Address Example**: Tools, resources, prompts, and CLI commands for IP geolocation\n- **Comprehensive Testing**: Unit and integration tests with coverage reporting (47 tests passing)\n- **Production Tooling**: ESLint, Prettier, semantic-release, and MCP Inspector integration\n- **Error Handling**: Structured error handling with `isError` field and contextual logging\n- **Security Documentation**: Complete [SECURITY.md](SECURITY.md) with authentication implementation guides\n\n## What is MCP?\n\nModel Context Protocol (MCP) is an open standard for securely connecting AI systems to external tools and data sources. This boilerplate implements the MCP specification with a clean, layered architecture that can be extended to build custom MCP servers for any API or data source.\n\n## Prerequisites\n\n- **Node.js** (\u003e=20.x): [Download](https://nodejs.org/)\n- **Git**: For version control\n\n## Quick Start\n\n```bash\n# Clone the repository\ngit clone https://github.com/aashari/boilerplate-mcp-server.git\ncd boilerplate-mcp-server\n\n# Install dependencies\nnpm install\n\n# Build the project\nnpm run build\n\n# Run in different modes:\n\n# 1. CLI Mode - Execute commands directly\nnpm run cli -- get-ip-details 8.8.8.8\nnpm run cli -- get-ip-details                                            # Get your current IP\nnpm run cli -- get-ip-details 1.1.1.1 -e                                 # With extended data\nnpm run cli -- get-ip-details 8.8.8.8 --jq \"{ip: query, country: country}\"  # JMESPath filter\nnpm run cli -- get-ip-details 8.8.8.8 -o json                            # JSON output\n\n# 2. STDIO Transport - For AI assistant integration (Claude Desktop, Cursor)\nnpm run mcp:stdio\n\n# 3. HTTP Transport - For web-based integrations\nnpm run mcp:http\n\n# 4. Development with MCP Inspector\nnpm run mcp:inspect                         # Auto-opens browser with debugging UI\n```\n\n## Transport Modes\n\n### STDIO Transport\n- JSON-RPC communication via stdin/stdout\n- Used by Claude Desktop, Cursor AI, and other local AI assistants\n- Run with: `TRANSPORT_MODE=stdio node dist/index.js`\n\n### Streamable HTTP Transport\n- HTTP-based transport with Server-Sent Events (SSE)\n- Supports multiple concurrent connections and web integrations\n- Runs on port 3000 by default (configurable via `PORT` env var)\n- MCP Endpoint: `http://localhost:3000/mcp`\n- Health Check: `http://localhost:3000/` → Returns server version\n- Run with: `TRANSPORT_MODE=http node dist/index.js`\n\n## Security 🔒\n\n**This boilerplate implements production-ready security measures:**\n\n### ✅ Built-In Protection\n\n1. **DNS Rebinding Protection**: Origin header validation prevents malicious websites from accessing your localhost server\n2. **Localhost-Only Binding**: Server explicitly binds to `127.0.0.1` (not accessible from network)\n3. **Secure Error Handling**: Error responses include `isError: true` flag and don't leak sensitive information\n\n### 🔐 Security Best Practices\n\n- **Local Development**: No authentication required (localhost-only + DNS rebinding protection)\n- **Network Deployment**: Authentication REQUIRED - see [SECURITY.md](SECURITY.md) for implementation guides\n- **Production**: Use mTLS, OAuth 2.0, or bearer tokens (detailed in [SECURITY.md](SECURITY.md))\n\n**📖 Complete security documentation:** [SECURITY.md](SECURITY.md)\n\n**🔍 Security audit report:** [docs/AUDIT-2025-01-13.md](docs/AUDIT-2025-01-13.md)\n\n## Output Formats\n\n### TOON Format (Default)\n\nTOON (Token-Oriented Object Notation) is a human-readable format optimized for LLMs, reducing token usage by 30-60% compared to JSON:\n\n```\nstatus: success\nquery: 8.8.8.8\ncountry: United States\ncity: Ashburn\nlat: 39.03\nlon: -77.5\n```\n\n### JSON Format\n\nStandard JSON output when `--output-format json` is specified:\n\n```json\n{\n  \"status\": \"success\",\n  \"query\": \"8.8.8.8\",\n  \"country\": \"United States\",\n  \"city\": \"Ashburn\"\n}\n```\n\n### JMESPath Filtering\n\nUse `--jq` to extract only needed fields, reducing token costs:\n\n```bash\n# Extract specific fields\nnpm run cli -- get-ip-details 8.8.8.8 --jq \"{ip: query, country: country}\"\n\n# Output:\n# ip: 8.8.8.8\n# country: United States\n\n# Nested structure\nnpm run cli -- get-ip-details 8.8.8.8 --jq \"{location: {city: city, coords: {lat: lat, lon: lon}}}\"\n```\n\nSee [JMESPath documentation](https://jmespath.org) for more filter examples.\n\n## Architecture Overview\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eProject Structure (Click to expand)\u003c/b\u003e\u003c/summary\u003e\n\n```\nsrc/\n├── cli/                    # Command-line interfaces\n│   ├── index.ts            # CLI entry point with Commander setup\n│   └── ipaddress.cli.ts    # IP address CLI commands\n├── controllers/            # Business logic orchestration  \n│   ├── ipaddress.controller.ts    # IP lookup business logic\n│   └── ipaddress.formatter.ts     # Response formatting\n├── services/               # External API interactions\n│   ├── vendor.ip-api.com.service.ts  # ip-api.com service\n│   └── vendor.ip-api.com.types.ts    # Service type definitions\n├── tools/                  # MCP tool definitions (AI interface)\n│   ├── ipaddress.tool.ts   # IP lookup tool (inline content)\n│   ├── ipaddress-link.tool.ts  # IP lookup with ResourceLink pattern\n│   └── ipaddress.types.ts  # Tool argument schemas\n├── resources/              # MCP resource definitions\n│   └── ipaddress.resource.ts # IP lookup resource (URI: ip://address)\n├── prompts/                # MCP prompt definitions\n│   └── analysis.prompt.ts  # IP analysis prompt templates\n├── types/                  # Global type definitions\n│   └── common.types.ts     # Shared interfaces (ControllerResponse, etc.)\n├── utils/                  # Shared utilities\n│   ├── logger.util.ts      # Contextual logging system\n│   ├── error.util.ts       # MCP-specific error formatting\n│   ├── error-handler.util.ts # Error handling utilities\n│   ├── config.util.ts      # Environment configuration\n│   ├── constants.util.ts   # Version and package constants\n│   ├── formatter.util.ts   # Markdown formatting and response truncation\n│   ├── toon.util.ts        # TOON format encoding\n│   ├── jq.util.ts          # JMESPath filtering\n│   ├── response.util.ts    # Raw API response logging\n│   └── transport.util.ts   # HTTP transport utilities\n└── index.ts                # Server entry point (dual transport)\n```\n\n\u003c/details\u003e\n\n## Layered Architecture\n\nThe boilerplate follows a clean, layered architecture with 6 distinct layers that promotes maintainability and clear separation of concerns:\n\n### 1. CLI Layer (`src/cli/`)\n\n- **Purpose**: Command-line interfaces for direct tool usage and testing\n- **Implementation**: Commander-based argument parsing with contextual error handling\n- **Example**: `get-ip-details [ipAddress] --include-extended-data --no-use-https`\n- **Pattern**: Register commands → Parse arguments → Call controllers → Handle errors\n\n### 2. Tools Layer (`src/tools/`)\n\n- **Purpose**: MCP tool definitions that AI assistants can invoke\n- **Implementation**: Zod schema validation with structured responses\n- **Example**: `ip_get_details` tool with optional IP address and configuration options\n- **Pattern**: Define schema → Validate args → Call controller → Format MCP response\n\n### 3. Resources Layer (`src/resources/`)\n\n- **Purpose**: MCP resources providing contextual data accessible via URIs\n- **Implementation**: Uses `registerResource` API with `ResourceTemplate` for parameterized URIs\n- **Example**: `ip://{ipAddress}` resource template providing IP geolocation data\n- **Pattern**: Register URI template → Extract variables → Return formatted content\n\n### 4. Controllers Layer (`src/controllers/`)\n\n- **Purpose**: Business logic orchestration with comprehensive error handling\n- **Implementation**: Options validation, fallback logic, response formatting\n- **Example**: IP lookup with HTTPS fallback, test environment detection, API token validation\n- **Pattern**: Validate inputs → Apply defaults → Call services → Format responses\n\n### 5. Services Layer (`src/services/`)\n\n- **Purpose**: Direct external API interactions with minimal business logic\n- **Implementation**: HTTP transport utilities with structured error handling\n- **Example**: ip-api.com API calls with authentication and field selection\n- **Pattern**: Build requests → Make API calls → Validate responses → Return raw data\n\n### 6. Utils Layer (`src/utils/`)\n\n- **Purpose**: Shared functionality across all layers\n- **Key Components**:\n  - `logger.util.ts`: Contextual logging (file:method context)\n  - `error.util.ts`: MCP-specific error formatting\n  - `error-handler.util.ts`: Error handling and context building\n  - `transport.util.ts`: HTTP/API utilities with retry logic\n  - `config.util.ts`: Environment configuration management\n  - `constants.util.ts`: Version and package constants\n  - `formatter.util.ts`: Markdown formatting and response truncation\n  - `toon.util.ts`: TOON format encoding (token-efficient output)\n  - `jq.util.ts`: JMESPath filtering for response transformation\n  - `response.util.ts`: Raw API response logging to `/tmp/mcp/\u003cproject\u003e/`\n\n## Developer Guide\n\n### Development Scripts\n\n```bash\n# Build and Clean\nnpm run build               # Build TypeScript to dist/\nnpm run clean               # Remove dist/ and coverage/\nnpm run prepare             # Build + ensure executable permissions (for npm publish)\n\n# CLI Testing\nnpm run cli -- get-ip-details 8.8.8.8                    # Test specific IP\nnpm run cli -- get-ip-details --include-extended-data    # Test with extended data\nnpm run cli -- get-ip-details --no-use-https             # Test with HTTP\n\n# MCP Server Modes\nnpm run mcp:stdio           # STDIO transport for AI assistants\nnpm run mcp:http            # HTTP transport on port 3000\nnpm run mcp:inspect         # HTTP + auto-open MCP Inspector\n\n# Testing\nnpm test                    # Run all tests (Jest)\nnpm run test:coverage       # Generate coverage report\nnpm run test:cli            # Run CLI-specific tests\n\n# Code Quality\nnpm run lint                # ESLint with TypeScript rules\nnpm run format              # Prettier formatting\n```\n\n### Environment Variables\n\n#### Core Configuration\n- `TRANSPORT_MODE`: Transport mode (`stdio` | `http`, default: `stdio`)\n- `PORT`: HTTP server port (default: `3000`)\n- `DEBUG`: Enable debug logging (`true` | `false`, default: `false`)\n- `NODE_ENV`: Node environment (`development` | `production`, default: `development`)\n\n#### IP API Configuration\n- `IPAPI_API_TOKEN`: API token for ip-api.com extended data (optional, free tier available)\n\n#### Example `.env` File\n```bash\n# Core configuration\nTRANSPORT_MODE=http\nPORT=3000\nDEBUG=true\nNODE_ENV=development\n\n# External API Keys\nIPAPI_API_TOKEN=your_token_here\n```\n\n### Debugging Tools\n\n- **MCP Inspector**: Visual tool for testing your MCP tools\n  - Run server with `npm run mcp:inspect`\n  - Open the URL shown in terminal\n  - Test your tools interactively\n\n- **Debug Logging**: Enable with `DEBUG=true` environment variable\n\n- **Raw Response Logging**: Large API responses (\u003e40,000 characters) are automatically logged\n  - Responses saved to `/tmp/mcp/\u003cproject-name\u003e/` directory\n  - Filename format: `\u003ctimestamp\u003e-\u003crandom\u003e.txt`\n  - Includes request details, response data, and performance metrics\n  - Truncated responses include guidance on accessing the full raw file\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eConfiguration (Click to expand)\u003c/b\u003e\u003c/summary\u003e\n\nCreate `~/.mcp/configs.json`:\n\n```json\n{\n  \"boilerplate\": {\n    \"environments\": {\n      \"DEBUG\": \"true\",\n      \"TRANSPORT_MODE\": \"http\",\n      \"PORT\": \"3000\"\n    }\n  }\n}\n```\n\n\u003c/details\u003e\n\n## Building Custom Tools\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eStep-by-Step Tool Implementation Guide (Click to expand)\u003c/b\u003e\u003c/summary\u003e\n\n### 1. Define Service Layer\n\nCreate a new service in `src/services/` following the vendor-specific naming pattern:\n\n```typescript\n// src/services/vendor.example-api.service.ts\nimport { Logger } from '../utils/logger.util.js';\nimport { fetchApi } from '../utils/transport.util.js';\nimport { ExampleApiResponse, ExampleApiRequestOptions } from './vendor.example-api.types.js';\nimport { createApiError, McpError } from '../utils/error.util.js';\n\nconst serviceLogger = Logger.forContext('services/vendor.example-api.service.ts');\n\nasync function get(\n\tparam?: string,\n\toptions: ExampleApiRequestOptions = {}\n): Promise\u003cExampleApiResponse\u003e {\n\tconst methodLogger = serviceLogger.forMethod('get');\n\tmethodLogger.debug(`Calling Example API with param: ${param}`);\n\n\ttry {\n\t\tconst url = `https://api.example.com/${param || 'default'}`;\n\t\tconst rawData = await fetchApi\u003cExampleApiResponse\u003e(url, {\n\t\t\theaders: options.apiKey ? { 'Authorization': `Bearer ${options.apiKey}` } : {}\n\t\t});\n\n\t\tmethodLogger.debug('Received successful response from Example API');\n\t\treturn rawData;\n\t} catch (error) {\n\t\tmethodLogger.error('Service error fetching data', error);\n\t\t\n\t\tif (error instanceof McpError) {\n\t\t\tthrow error;\n\t\t}\n\t\t\n\t\tthrow createApiError(\n\t\t\t'Unexpected service error while fetching data',\n\t\t\tundefined,\n\t\t\terror\n\t\t);\n\t}\n}\n\nexport default { get };\n```\n\n### 2. Create Controller\n\nAdd a controller in `src/controllers/` to handle business logic with error context:\n\n```typescript\n// src/controllers/example.controller.ts\nimport { Logger } from '../utils/logger.util.js';\nimport exampleService from '../services/vendor.example-api.service.js';\nimport { formatExample } from './example.formatter.js';\nimport { handleControllerError, buildErrorContext } from '../utils/error-handler.util.js';\nimport { ControllerResponse } from '../types/common.types.js';\nimport { config } from '../utils/config.util.js';\n\nconst logger = Logger.forContext('controllers/example.controller.ts');\n\nexport interface GetDataOptions {\n\tparam?: string;\n\tincludeMetadata?: boolean;\n}\n\nasync function getData(\n\toptions: GetDataOptions = {}\n): Promise\u003cControllerResponse\u003e {\n\tconst methodLogger = logger.forMethod('getData');\n\tmethodLogger.debug(`Getting data for param: ${options.param || 'default'}`, options);\n\n\ttry {\n\t\t// Apply business logic and defaults\n\t\tconst apiKey = config.get('EXAMPLE_API_TOKEN');\n\t\t\n\t\t// Call service layer\n\t\tconst data = await exampleService.get(options.param, {\n\t\t\tapiKey,\n\t\t\tincludeMetadata: options.includeMetadata ?? false\n\t\t});\n\t\t\n\t\t// Format response\n\t\tconst formattedContent = formatExample(data);\n\t\treturn { content: formattedContent };\n\t\t\n\t} catch (error) {\n\t\tthrow handleControllerError(\n\t\t\terror,\n\t\t\tbuildErrorContext(\n\t\t\t\t'ExampleData',\n\t\t\t\t'getData',\n\t\t\t\t'controllers/example.controller.ts@getData',\n\t\t\t\toptions.param || 'default',\n\t\t\t\t{ options }\n\t\t\t)\n\t\t);\n\t}\n}\n\nexport default { getData };\n```\n\n### 3. Implement MCP Tool\n\nCreate a tool definition in `src/tools/` following the registration pattern:\n\n```typescript\n// src/tools/example.tool.ts\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { Logger } from '../utils/logger.util.js';\nimport { formatErrorForMcpTool } from '../utils/error.util.js';\nimport exampleController from '../controllers/example.controller.js';\n\nconst logger = Logger.forContext('tools/example.tool.ts');\n\n// Define Zod schema for tool arguments\nconst GetDataSchema = z.object({\n\tparam: z.string().optional().describe('Optional parameter for the API call'),\n\tincludeMetadata: z.boolean().optional().default(false)\n\t\t.describe('Whether to include additional metadata in the response')\n});\n\nasync function handleGetData(args: Record\u003cstring, unknown\u003e) {\n\tconst methodLogger = logger.forMethod('handleGetData');\n\t\n\ttry {\n\t\tmethodLogger.debug('Tool example_get_data called', args);\n\n\t\t// Validate arguments with Zod\n\t\tconst validatedArgs = GetDataSchema.parse(args);\n\n\t\t// Call controller\n\t\tconst result = await exampleController.getData({\n\t\t\tparam: validatedArgs.param,\n\t\t\tincludeMetadata: validatedArgs.includeMetadata\n\t\t});\n\n\t\t// Return MCP-formatted response\n\t\treturn {\n\t\t\tcontent: [\n\t\t\t\t{\n\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\ttext: result.content\n\t\t\t\t}\n\t\t\t]\n\t\t};\n\t} catch (error) {\n\t\tmethodLogger.error('Tool example_get_data failed', error);\n\t\treturn formatErrorForMcpTool(error);\n\t}\n}\n\n// Registration function using the modern registerTool API (SDK v1.23.0)\nfunction registerTools(server: McpServer) {\n\tconst registerLogger = logger.forMethod('registerTools');\n\tregisterLogger.debug('Registering example tools...');\n\n\t// SDK best practices: 'title' for UI display name, 'description' for detailed info\n\tserver.registerTool(\n\t\t'example_get_data',\n\t\t{\n\t\t\ttitle: 'Get Example Data',  // Display name for UI (e.g., 'Get Example Data')\n\t\t\tdescription: `Gets data from the Example API with optional parameter.\nUse this tool to fetch example data. Returns formatted data as Markdown.`,\n\t\t\tinputSchema: GetDataSchema,\n\t\t},\n\t\thandleGetData\n\t);\n\n\tregisterLogger.debug('Example tools registered successfully');\n}\n\nexport default { registerTools };\n```\n\n### 4. Add CLI Support\n\nCreate a CLI command in `src/cli/` following the Commander pattern:\n\n```typescript\n// src/cli/example.cli.ts\nimport { Command } from 'commander';\nimport { Logger } from '../utils/logger.util.js';\nimport exampleController from '../controllers/example.controller.js';\nimport { handleCliError } from '../utils/error.util.js';\n\nconst logger = Logger.forContext('cli/example.cli.ts');\n\nfunction register(program: Command) {\n\tconst methodLogger = logger.forMethod('register');\n\tmethodLogger.debug('Registering example CLI commands...');\n\n\tprogram\n\t\t.command('get-data')\n\t\t.description('Gets data from the Example API')\n\t\t.argument('[param]', 'Optional parameter for the API call')\n\t\t.option('-m, --include-metadata', 'Include additional metadata in response')\n\t\t.action(async (param, options) =\u003e {\n\t\t\tconst actionLogger = logger.forMethod('action:get-data');\n\t\t\t\n\t\t\ttry {\n\t\t\t\tactionLogger.debug('CLI get-data called', { param, options });\n\n\t\t\t\tconst result = await exampleController.getData({\n\t\t\t\t\tparam,\n\t\t\t\t\tincludeMetadata: options.includeMetadata || false\n\t\t\t\t});\n\n\t\t\t\tconsole.log(result.content);\n\t\t\t} catch (error) {\n\t\t\t\thandleCliError(error);\n\t\t\t}\n\t\t});\n\n\tmethodLogger.debug('Example CLI commands registered successfully');\n}\n\nexport default { register };\n```\n\n### 5. Register Components\n\nUpdate the entry points to register your new components:\n\n```typescript\n// 1. Register CLI in src/cli/index.ts\nimport exampleCli from './example.cli.js';\n\nexport async function runCli(args: string[]) {\n\t// ... existing setup code ...\n\t\n\t// Register CLI commands\n\texampleCli.register(program);  // Add this line\n\t\n\t// ... rest of function\n}\n\n// 2. Register Tools in src/index.ts\nimport exampleTools from './tools/example.tool.js';\n\n// In the startServer function, after existing registrations:\nexampleTools.registerTools(serverInstance);\n```\n\n### 6. Add MCP Resource (Optional)\n\nCreate a resource in `src/resources/` using the modern `registerResource` API:\n\n```typescript\n// src/resources/example.resource.ts\nimport { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { Logger } from '../utils/logger.util.js';\nimport exampleController from '../controllers/example.controller.js';\nimport { formatErrorForMcpResource } from '../utils/error.util.js';\n\nconst logger = Logger.forContext('resources/example.resource.ts');\n\nfunction registerResources(server: McpServer) {\n\tconst registerLogger = logger.forMethod('registerResources');\n\tregisterLogger.debug('Registering example resources...');\n\n\t// Use registerResource with ResourceTemplate for parameterized URIs (SDK v1.23.0)\n\tserver.registerResource(\n\t\t'example-data',\n\t\tnew ResourceTemplate('example://{param}', { list: undefined }),\n\t\t{\n\t\t\ttitle: 'Example Data',  // Display name for UI\n\t\t\tdescription: 'Retrieve example data by parameter'\n\t\t},\n\t\tasync (uri, variables) =\u003e {\n\t\t\tconst methodLogger = logger.forMethod('exampleResource');\n\t\t\ttry {\n\t\t\t\t// Extract parameter from template variables\n\t\t\t\tconst param = variables.param as string | undefined;\n\n\t\t\t\tmethodLogger.debug('Example resource called', { uri: uri.href, param });\n\n\t\t\t\tconst result = await exampleController.getData({ param });\n\n\t\t\t\treturn {\n\t\t\t\t\tcontents: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\turi: uri.href,\n\t\t\t\t\t\t\ttext: result.content,\n\t\t\t\t\t\t\tmimeType: 'text/markdown'\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t};\n\t\t\t} catch (error) {\n\t\t\t\tmethodLogger.error('Resource error', error);\n\t\t\t\treturn formatErrorForMcpResource(error, uri.href);\n\t\t\t}\n\t\t}\n\t);\n\n\tregisterLogger.debug('Example resources registered successfully');\n}\n\nexport default { registerResources };\n```\n\n\u003c/details\u003e\n\n## IP Address Example Implementation\n\nThe boilerplate includes a complete IP address geolocation example demonstrating all layers:\n\n### Available Tools \u0026 Commands\n\n**CLI Commands:**\n```bash\nnpm run cli -- get-ip-details                                            # Get current public IP (TOON format)\nnpm run cli -- get-ip-details 8.8.8.8                                    # Get details for specific IP\nnpm run cli -- get-ip-details 1.1.1.1 -e                                 # Short form with extended data\nnpm run cli -- get-ip-details 1.1.1.1 --include-extended-data           # Long form with extended data\nnpm run cli -- get-ip-details 8.8.8.8 --no-use-https                    # Force HTTP (for free tier)\nnpm run cli -- get-ip-details 8.8.8.8 -o json                            # JSON output (short form)\nnpm run cli -- get-ip-details 8.8.8.8 --output-format json              # JSON output (long form)\nnpm run cli -- get-ip-details 8.8.8.8 --jq \"{ip: query, country: country}\"  # JMESPath filtered output\n```\n\n**MCP Tools:**\n- `ip_get_details` - IP geolocation lookup for AI assistants\n- `ip_get_details_link` - Same lookup + resource link output for resource-aware clients\n\nBoth tools share the same parameters:\n- `ipAddress` (optional): IP address to lookup (omit for current device's public IP)\n- `includeExtendedData` (optional, default: `false`): Include ASN, host, organization data (requires API token)\n- `useHttps` (optional, default: `true`): Use HTTPS for API calls\n- `jq` (optional): JMESPath expression to filter/transform response\n- `outputFormat` (optional, default: `\"toon\"`): Output format - \"toon\" or \"json\"\n\nOutput behavior:\n- `ip_get_details`: returns one `text` content block\n- `ip_get_details_link`: returns the same first `text` block plus a `resource_link` block (`ip://\u003cresolved-ip\u003e`)\n\n**MCP Resources:**\n- `ip://{ipAddress}` - IP details resource template (e.g., `ip://8.8.8.8`, `ip://1.1.1.1`)\n  - Returns IP geolocation data in Markdown format\n  - Uses TOON format by default for token efficiency\n\n### Features Demonstrated\n\n- **TOON Output**: Token-efficient format (30-60% fewer tokens than JSON)\n- **JMESPath Filtering**: Extract only needed fields to reduce costs\n- **Fallback Logic**: HTTPS → HTTP fallback for free tier users\n- **Environment Detection**: Different behavior in test vs production\n- **API Token Support**: Optional token for extended data (ASN, mobile detection, etc.)\n- **Error Handling**: Structured errors for private/reserved IP addresses\n\n### Configuration Options\n\n```bash\n# Optional - for extended data features\nIPAPI_API_TOKEN=your_token_from_ip-api.com\n\n# Development\nDEBUG=true                    # Enable detailed logging\nTRANSPORT_MODE=http          # Use HTTP transport\nPORT=3001                    # Custom port\n```\n\n## Publishing Your MCP Server\n\n1. **Customize Package Details:**\n   ```json\n   {\n     \"name\": \"your-mcp-server-name\",\n     \"version\": \"1.0.0\", \n     \"description\": \"Your custom MCP server\",\n     \"author\": \"Your Name\",\n     \"keywords\": [\"mcp\", \"your-domain\", \"ai-integration\"]\n   }\n   ```\n\n2. **Update Documentation:** Replace IP address examples with your use case\n3. **Test Thoroughly:**\n   ```bash\n   npm run build \u0026\u0026 npm test\n   npm run cli -- your-command\n   npm run mcp:stdio    # Test with MCP Inspector\n   ```\n4. **Release:** Push conventional commits to `main` and let semantic-release publish automatically via GitHub Actions (OIDC trusted publishing).\n\n## Testing Strategy\n\nThe boilerplate includes comprehensive testing infrastructure:\n\n### Test Structure\n```\ntests/               # Not present - tests are in src/\nsrc/\n├── **/*.test.ts     # Co-located with source files\n├── utils/           # Utility function tests\n├── controllers/     # Business logic tests  \n├── services/        # API integration tests\n└── cli/             # CLI command tests\n```\n\n### Testing Best Practices\n\n- **Unit Tests**: Test utilities and pure functions (`*.util.test.ts`)\n- **Controller Tests**: Test business logic with mocked service calls\n- **Service Tests**: Test API integration with real/mocked HTTP calls\n- **CLI Tests**: Test command parsing and execution\n- **Test Environment Detection**: Automatic test mode handling in controllers\n\n### Running Tests\n\n```bash\nnpm test                    # Run all tests\nnpm run test:coverage       # Generate coverage report  \nnpm run test:cli           # CLI-specific tests only\n```\n\n### Coverage Goals\n- Target: \u003e80% test coverage\n- Focus on business logic (controllers) and utilities\n- Mock external services appropriately\n\n## License\n\n[ISC License](https://opensource.org/licenses/ISC)\n\n## MCP SDK v2 Preparation\n\n⚠️ **Note**: MCP SDK v2 is in development (expected stable Q1 2026). This boilerplate is ready for migration with minimal changes needed.\n\n**Key v2 Changes**:\n- Package split: `@modelcontextprotocol/server` and `@modelcontextprotocol/client`\n- Optional middleware packages for Express, Hono, Node.js HTTP\n- Same core API patterns (this boilerplate already uses modern APIs)\n\nSee [MODERNIZATION.md](MODERNIZATION.md) for detailed migration guide and timeline.\n\n## Resources \u0026 Documentation\n\n### MCP Protocol Resources\n- [MCP Specification](https://modelcontextprotocol.io/specification) - Latest protocol specification\n- [MCP SDK Documentation](https://github.com/modelcontextprotocol/typescript-sdk) - TypeScript SDK v1.23.0\n- [MCP Inspector](https://github.com/modelcontextprotocol/inspector) - Visual debugging tool\n- [MCP Concepts](https://modelcontextprotocol.io/docs/concepts) - Tools, resources, transports\n\n### Implementation References\n- [Anthropic MCP Announcement](https://www.anthropic.com/news/model-context-protocol)\n- [Awesome MCP Servers](https://github.com/wong2/awesome-mcp-servers) - Community examples\n- [TypeScript Documentation](https://www.typescriptlang.org/docs/)\n\n### Your MCP Server Ecosystem\n- [All @aashari MCP Servers](https://www.npmjs.com/~aashari) - NPM packages\n- [GitHub Repositories](https://github.com/aashari?tab=repositories\u0026q=mcp-server) - Source code","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faashari%2Fboilerplate-mcp-server","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faashari%2Fboilerplate-mcp-server","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faashari%2Fboilerplate-mcp-server/lists"}