Build MCP servers in minutes, not hours. MCP-Atom is a zero-boilerplate framework that automatically discovers, validates, and registers your tools, prompts, and resources by simply reading your file structure and JSDoc comments.
The Model Context Protocol (MCP) is an open protocol that enables AI assistants to securely access external tools, data sources, and capabilities. Think of it as a standardized way for AI applications like Claude Desktop to connect to your services, databases, APIs, and custom functionality.
Traditional MCP server development requires:
- ❌ Manual registration of every tool, prompt, and resource
- ❌ Writing JSON Schema definitions by hand
- ❌ Boilerplate code for each endpoint
- ❌ Maintaining type definitions separately from implementation
MCP-Atom eliminates all of this. Just write files with JSDoc comments, and everything is automatically discovered, validated, and registered.
- 🚀 Zero Boilerplate: No manual
server.registerTool()calls - 📝 JSDoc-Driven: Schemas extracted automatically from your comments
- 🔍 Auto-Discovery: Drop files in folders, they're automatically registered
- ✅ Type-Safe: Automatic Zod schema generation with validation
- 🎯 Convention Over Configuration: File structure = API structure
npm installCreate a register/ folder with three subfolders:
register/
├── tools/ # Executable functions
├── prompts/ # Prompt templates
└── resources/ # Data sources (files, APIs, etc.)
Create a file in register/tools/ with a JSDoc comment and default export. The tool will be automatically registered with:
- ✅ Input validation based on your JSDoc types
- ✅ Output schema
- ✅ Title and description from JSDoc
- ✅ Full type safety
MCP-Atom supports two transport modes: Stdio (default) and Streamable HTTP.
The default transport uses standard input/output, which is ideal for local development and integration with MCP clients like Claude Desktop.
npm startOr use the MCP Inspector to test:
npm run start:inspectFor web-based clients or remote access, use the Streamable HTTP transport. This enables:
- HTTP/HTTPS connections
- Server-Sent Events (SSE) for real-time updates
- Session management with resumability
- CORS support for browser-based clients
Enable HTTP transport using the dedicated script:
npm run start:httpOr manually set environment variables:
# Option 1: Use TRANSPORT environment variable
TRANSPORT=http npm start
# Option 2: Use USE_HTTP flag
USE_HTTP=true npm startConfigure the port (default: 3001):
PORT=8080 npm run start:http
# Or with manual env var:
PORT=8080 TRANSPORT=http npm startThe server will listen on http://localhost:8080/mcp (or your specified port, default: 3001).
Endpoints:
POST /mcp- MCP requests (initialization and subsequent requests)GET /mcp- SSE stream for server-to-client notificationsDELETE /mcp- Session termination
Headers:
mcp-session-id- Session identifier (required for GET/DELETE, optional for initial POST)last-event-id- For resuming SSE streams after disconnection
Example with HTTP:
# Start server on port 3001 (default)
npm run start:http
# Or with custom port
PORT=8080 npm run start:httpTesting with HTTP:
You can use the MCP Inspector with HTTP transport:
# Start server with HTTP transport
npm run start:http
# Then in another terminal, run the inspector
npm run start:inspectOr manually configure MCP Inspector:
-
Start your server with HTTP transport:
npm run start:http # Or with custom port: PORT=8080 npm run start:http -
In MCP Inspector, configure the connection:
- Connection Type: Select
Direct(for direct connection to your server) - URL:
http://localhost:3001/mcp(or your configured port, e.g.,http://localhost:8080/mcp) - Request Timeout:
300000(5 minutes, default) - Reset Timeout on Progress:
True(recommended) - Maximum Total Timeout:
60000(1 minute, default) - Inspector Proxy Address: Leave empty (only needed if Connection Type is "via proxy")
- Proxy Session Token: Leave empty (only needed if Connection Type is "via proxy")
Note: When using
Directconnection type, you do not need to fill in the Proxy Address or Proxy Session Token fields. Those are only required when usingvia proxyconnection type. - Connection Type: Select
-
Connect - The Inspector will automatically handle session initialization and SSE streams.
Or connect any MCP-compatible client to http://localhost:3001/mcp (or your configured port).
| Variable | Description | Default | Example |
|---|---|---|---|
TRANSPORT |
Transport mode: stdio or http |
stdio |
TRANSPORT=http npm start or npm run start:http |
USE_HTTP |
Enable HTTP transport (alternative to TRANSPORT=http) |
false |
USE_HTTP=true npm start |
PORT |
Port number for HTTP transport | 3001 |
PORT=8080 npm run start:http |
| Feature | Stdio | Streamable HTTP |
|---|---|---|
| Use Case | Local development, Claude Desktop | Web clients, remote access |
| Connection | Process stdio | HTTP/HTTPS |
| Real-time Updates | ✅ | ✅ (via SSE) |
| Session Management | Single session | Multiple concurrent sessions |
| Resumability | ❌ | ✅ (via Last-Event-ID) |
| CORS Support | N/A | ✅ |
| Port Required | ❌ | ✅ (default: 3001) |
Tools are executable functions that perform actions. They accept input parameters and return results.
Location: register/tools/*.js
Format:
/**
* Tool Title - Brief description
*
* @param {{ param1: type1, param2?: type2 }} input
* @returns {{ result: type }}
*/
export default ({ param1, param2 }) => {
// Your implementation
return { result: /* ... */ };
}Optional Parameters: Use ? in the type definition:
@param {{ name: string, style?: string }} inputComplex Types: Supports arrays, nested objects, unions, and more:
@param {{
items: string[],
config: { timeout: number, retry: boolean },
status: "active" | "inactive"
}} inputReturning Metadata: Return an array [output, ...meta] to include additional content. The first element is the main output, subsequent elements are metadata items (resource links, images, etc.).
📖 See register/README.md for detailed examples of all tool types.
Prompts are template generators that create messages for AI assistants.
Location: register/prompts/*.js
Format:
/**
* Prompt Title - Brief description
*
* @param {{ arg1: type1, arg2?: type2 }} input
* @returns {{ messages: Array<{ role: string, content: { type: string, text: string } }> }}
*/
export default ({ arg1, arg2 = "default" }) => {
return {
messages: [
{
role: "user",
content: {
type: "text",
text: `Your prompt text using ${arg1} and ${arg2}`
}
}
]
};
}Note: Prompts must return an object with a messages array following the MCP prompt format.
📖 See register/README.md for detailed prompt examples.
Resources are data sources that can be read by AI assistants. They can be static or dynamic with parameters.
Location: register/resources/{protocol}/*.js
Static Resource:
/**
* Resource Title - Brief description
*
* @returns {{ ... }}
*/
export default () => {
return { /* your data */ };
}Dynamic Resource with Parameters:
Use folder names with {parameter} syntax to create parameterized resources:
/**
* Resource Title - Brief description
*
* @param {{ paramName: type }} input
* @returns {type}
* @mime text/plain
*/
export default ({ paramName }) => {
return /* your data */;
}This creates a resource accessible at {protocol}://{path} where parameters are extracted from the folder structure.
Resource URI Structure:
- Folder path:
resources/github/repos/{owner}/{repo}.js - Resource URI:
github://repos/{owner}/{repo} - Registration name:
github-repos-owner-repo
Autocomplete Support:
Add an autocomplete export to provide intelligent completions. Each function receives value (current input) and context (with previously resolved parameters) and returns a filtered array of suggestions.
MIME Types: Specify content type with @mime tag:
/**
* @mime application/json
*/📖 See register/README.md for detailed resource examples including autocomplete patterns.
MCP-Atom automatically converts JSDoc types to Zod schemas. Supported types:
string,number,boolean,null,any,unknownDate(converted to ISO string)
string[]orArray<string>- arrays{ key: value }- objects
"value1" | "value2"- unions{ prop?: type }- optional propertiesPromise<{ result: type }>- promises (unwrapped)
// Simple object
@param {{ name: string, age: number }} input
// With optional fields
@param {{ name: string, email?: string }} input
// Nested objects
@param {{
user: { id: string, name: string },
settings: { theme: "light" | "dark" }
}} input
// Arrays
@param {{ tags: string[], items: Array<{ id: number }> }} input
// Complex nested structure
@param {{
config: {
api: {
baseUrl: string,
timeout: number,
retry: boolean
},
features: {
enabled: boolean,
options: string[]
}
}
}} input📖 See register/README.md for more type examples and patterns.
The register/ folder structure determines your API structure:
- Tools:
register/tools/*.js- each file becomes a tool - Prompts:
register/prompts/*.js- each file becomes a prompt - Resources:
register/resources/{protocol}/*.js- folder structure maps to resource URIs- Static:
resources/config/app.js→config://app - Dynamic:
resources/users/{userId}/profile.js→users://{userId}/profile
- Static:
- Discovery: MCP-Atom scans the
register/folder recursively - Parsing: Each
.jsfile is analyzed for:- JSDoc comments (title, description, types)
- Default export function
- Optional
autocompleteexport (for resources)
- Schema Generation: JSDoc types are converted to Zod schemas
- Registration: Tools, prompts, and resources are automatically registered with the MCP server
- Validation: All inputs are validated against generated schemas
- Always include JSDoc: Every file needs a JSDoc block with
@paramand@returns - Use descriptive titles: The first line becomes the tool/prompt/resource title
- Document optional parameters: Use
?in type definitions for optional fields - Keep functions pure when possible: Easier to test and reason about
- Handle errors gracefully: Return meaningful error messages
- Use TypeScript-style JSDoc: The parser understands TypeScript syntax
All handlers support async/await. Simply use async in your function declaration.
Tools can return an array where the first element is the output and subsequent elements are metadata (resource links, images, etc.).
"No default export found": Every file must have export default
"No JSDoc found": Every file needs a JSDoc comment block
Type parsing errors: Check your JSDoc syntax matches the examples
Resource not found: Verify folder structure matches resources/{protocol}/...
"Port already in use": Change the port using PORT=8080 npm run start:http
"CORS errors": The server is configured with CORS enabled by default (origin: "*"). For production, consider restricting origins.
"Session not found": Ensure you're sending the mcp-session-id header for GET/DELETE requests after initialization
"Connection refused": Verify the server is running and the port matches your client configuration
When using HTTP transport in production:
-
CORS Configuration: The default CORS setting allows all origins (
origin: "*"). Restrict this in production:// In index.js, update the CORS configuration app.use(cors({ origin: process.env.ALLOWED_ORIGINS?.split(',') || ['https://yourdomain.com'], // ... other options }));
-
Authentication: Add authentication middleware before the MCP routes if needed
-
HTTPS: Use a reverse proxy (nginx, Caddy) or load balancer to add HTTPS
-
Port Security: Ensure your firewall only allows necessary ports
- 📖 See
register/README.mdfor detailed examples of all file types with real code - 🔧 Check out
builderBot/readme.mdfor internal implementation details - 📚 Read the MCP Specification for protocol details