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.
A foundation for developing custom Model Context Protocol (MCP) servers in TypeScript. Provides a complete layered architecture pattern, working example tools, and developer infrastructure to connect AI assistants with external APIs and data sources.
Model 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.
# Clone the repository
git clone https://github.com/aashari/boilerplate-mcp-server.git
cd boilerplate-mcp-server
# Install dependencies
npm install
# Run in different modes:
# 1. CLI Mode - Execute commands directly
npm run cli -- get-ip-details 8.8.8.8
# 2. STDIO Transport - For direct AI assistant integration
npm run mcp:stdio
# 3. HTTP Transport - For web-based integrations (default)
npm run mcp:http
# 4. Development with MCP Inspector
npm run mcp:inspect
TRANSPORT_MODE=stdio npm run build && node dist/index.js
http://localhost:3000/mcp
http://localhost:3000/
TRANSPORT_MODE=http npm run build && node dist/index.js
src/
โโโ cli/ # Command-line interfaces
โ โโโ index.ts # CLI entry point
โ โโโ *.cli.ts # Feature-specific CLI modules
โโโ controllers/ # Business logic
โ โโโ *.controller.ts # Feature controllers
โโโ services/ # External API interactions
โ โโโ *.service.ts # Service modules
โโโ tools/ # MCP tool definitions
โ โโโ *.tool.ts # Tool implementations
โ โโโ *.types.ts # Tool argument schemas
โโโ resources/ # MCP resource definitions
โ โโโ *.resource.ts # Resource implementations
โโโ types/ # Type definitions
โ โโโ common.types.ts # Shared type definitions
โโโ utils/ # Shared utilities
โ โโโ logger.util.ts # Structured logging
โ โโโ error.util.ts # Error handling
โ โโโ ... # Other utility modules
โโโ index.ts # Server entry point
The boilerplate follows a clean, layered architecture that promotes maintainability and clear separation of concerns:
src/cli/*.cli.ts
)commander
for argument parsing, call controllers, handle errors with handleCliError
<feature>.cli.ts
src/tools/*.tool.ts
)zod
for schema validation, call controllers, format responses for MCP<feature>.tool.ts
with types in <feature>.types.ts
src/controllers/*.controller.ts
)ControllerResponse
objects, throw errors with context<feature>.controller.ts
with optional <feature>.formatter.ts
src/services/*.service.ts
)<feature>.service.ts
or vendor.<vendor>.<feature>.service.ts
src/utils/*.util.ts
)# Development
npm run build # Build TypeScript
npm run clean # Clean build artifacts
# Running different modes
npm run cli -- [command] # Run CLI commands
npm run mcp:stdio # Run with STDIO transport
npm run mcp:http # Run with HTTP transport (default)
npm run mcp:inspect # Run with MCP Inspector
# Development modes
npm run dev:stdio # STDIO with inspector
npm run dev:http # HTTP in development mode
# Testing
npm test # Run all tests
npm run test:coverage # Generate coverage report
# Code Quality
npm run lint # Run ESLint
npm run format # Format with Prettier
TRANSPORT_MODE
: Set to stdio
or http
(default: http
)PORT
: HTTP server port (default: 3000
)DEBUG
: Enable debug logging (default: false
)IPAPI_API_TOKEN
: API token for ip-api.com (optional)MCP Inspector: Visual tool for testing your MCP tools
npm run mcp:inspect
Debug Logging: Enable with DEBUG=true
environment variable
Create ~/.mcp/configs.json
:
{
"boilerplate": {
"environments": {
"DEBUG": "true",
"TRANSPORT_MODE": "http",
"PORT": "3000"
}
}
}
Create a new service in src/services/
to interact with your external API:
// src/services/example.service.ts
import { Logger } from '../utils/logger.util.js';
const logger = Logger.forContext('services/example.service.ts');
export async function getData(param: string): Promise<any> {
logger.debug('Getting data', { param });
// API interaction code here
return { result: 'example data' };
}
Add a controller in src/controllers/
to handle business logic:
// src/controllers/example.controller.ts
import { Logger } from '../utils/logger.util.js';
import * as exampleService from '../services/example.service.js';
import { formatMarkdown } from '../utils/formatter.util.js';
import { handleControllerError } from '../utils/error-handler.util.js';
import { ControllerResponse } from '../types/common.types.js';
const logger = Logger.forContext('controllers/example.controller.ts');
export interface GetDataOptions {
param?: string;
}
export async function getData(
options: GetDataOptions = {},
): Promise<ControllerResponse> {
try {
logger.debug('Getting data with options', options);
const data = await exampleService.getData(options.param || 'default');
const content = formatMarkdown(data);
return { content };
} catch (error) {
throw handleControllerError(error, {
entityType: 'ExampleData',
operation: 'getData',
source: 'controllers/example.controller.ts',
});
}
}
Create a tool definition in src/tools/
:
// src/tools/example.tool.ts
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
import { Logger } from '../utils/logger.util.js';
import { formatErrorForMcpTool } from '../utils/error.util.js';
import * as exampleController from '../controllers/example.controller.js';
const logger = Logger.forContext('tools/example.tool.ts');
const GetDataArgs = z.object({
param: z.string().optional().describe('Optional parameter'),
});
type GetDataArgsType = z.infer<typeof GetDataArgs>;
async function handleGetData(args: GetDataArgsType) {
try {
logger.debug('Tool get_data called', args);
const result = await exampleController.getData({
param: args.param,
});
return {
content: [{ type: 'text' as const, text: result.content }],
};
} catch (error) {
logger.error('Tool get_data failed', error);
return formatErrorForMcpTool(error);
}
}
export function register(server: McpServer) {
server.tool(
'get_data',
`Gets data from the example API, optionally using \`param\`.
Use this to fetch example data. Returns formatted data as Markdown.`,
GetDataArgs.shape,
handleGetData,
);
}
Create a CLI command in src/cli/
:
// src/cli/example.cli.ts
import { program } from 'commander';
import { Logger } from '../utils/logger.util.js';
import * as exampleController from '../controllers/example.controller.js';
import { handleCliError } from '../utils/error-handler.util.js';
const logger = Logger.forContext('cli/example.cli.ts');
program
.command('get-data')
.description('Get example data')
.option('--param <value>', 'Optional parameter')
.action(async (options) => {
try {
logger.debug('CLI get-data called', options);
const result = await exampleController.getData({
param: options.param,
});
console.log(result.content);
} catch (error) {
handleCliError(error);
}
});
Update the entry points to register your new components:
// In src/cli/index.ts
import '../cli/example.cli.js';
// In src/index.ts (for the tool)
import exampleTool from './tools/example.tool.js';
// Then in registerTools function:
exampleTool.register(server);
Update package.json with your details:
{
"name": "your-mcp-server-name",
"version": "1.0.0",
"description": "Your custom MCP server",
"author": "Your Name",
// Other fields...
}
Update README.md with your tool documentation
Build: npm run build
Test: npm run mcp:stdio
and npm run mcp:http
Publish: npm publish
No configuration available
Related projects feature coming soon
Will recommend related projects based on sub-categories