@agentailor/create-mcp-server 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,6 +15,7 @@ npx @agentailor/create-mcp-server
15
15
  - **Optional OAuth** — OIDC-compliant authentication (SDK only) ([setup guide](docs/oauth-setup.md))
16
16
  - **Package manager choice** — npm, pnpm, or yarn
17
17
  - **TypeScript ready** — ready to customize
18
+ - **Docker ready** — production Dockerfile included
18
19
  - **MCP Inspector** — built-in debugging with `npm run inspect`
19
20
 
20
21
  ## Frameworks
@@ -67,6 +68,7 @@ my-mcp-server/
67
68
  │ ├── server.ts # MCP server (tools, prompts, resources)
68
69
  │ ├── index.ts # Express app and transport setup
69
70
  │ └── auth.ts # OAuth middleware (if enabled)
71
+ ├── Dockerfile # Production-ready Docker build
70
72
  ├── package.json
71
73
  ├── tsconfig.json
72
74
  ├── .gitignore
package/dist/index.js CHANGED
@@ -10,6 +10,7 @@ import { getEnvExampleTemplate } from './templates/common/env.example.js';
10
10
  import { getServerTemplate as getSdkStatelessServerTemplate, getIndexTemplate as getSdkStatelessIndexTemplate, getReadmeTemplate as getSdkStatelessReadmeTemplate, } from './templates/sdk/stateless/index.js';
11
11
  import { getServerTemplate as getSdkStatefulServerTemplate, getIndexTemplate as getSdkStatefulIndexTemplate, getReadmeTemplate as getSdkStatefulReadmeTemplate, getAuthTemplate as getSdkAuthTemplate, } from './templates/sdk/stateful/index.js';
12
12
  import { getServerTemplate as getFastMCPServerTemplate, getIndexTemplate as getFastMCPIndexTemplate, getReadmeTemplate as getFastMCPReadmeTemplate, } from './templates/fastmcp/index.js';
13
+ import { getDockerfileTemplate, getDockerignoreTemplate } from './templates/deployment/index.js';
13
14
  const sdkTemplateFunctions = {
14
15
  stateless: {
15
16
  getServerTemplate: getSdkStatelessServerTemplate,
@@ -153,6 +154,8 @@ async function main() {
153
154
  }
154
155
  // Common files for all templates
155
156
  filesToWrite.push(writeFile(join(projectPath, 'package.json'), getPackageJsonTemplate(projectName, templateOptions)), writeFile(join(projectPath, 'tsconfig.json'), getTsconfigTemplate()), writeFile(join(projectPath, '.gitignore'), getGitignoreTemplate()), writeFile(join(projectPath, '.env.example'), getEnvExampleTemplate(templateOptions)));
157
+ // Deployment files for all templates
158
+ filesToWrite.push(writeFile(join(projectPath, 'Dockerfile'), getDockerfileTemplate(templateOptions)), writeFile(join(projectPath, '.dockerignore'), getDockerignoreTemplate()));
156
159
  // Write all template files
157
160
  await Promise.all(filesToWrite);
158
161
  // Initialize git repository if requested
@@ -0,0 +1,2 @@
1
+ import type { BaseTemplateOptions } from '../common/types.js';
2
+ export declare function getDockerfileTemplate(options?: BaseTemplateOptions): string;
@@ -0,0 +1,64 @@
1
+ const packageManagerConfigs = {
2
+ npm: {
3
+ lockFile: 'package-lock.json',
4
+ install: 'npm ci',
5
+ installProd: 'npm ci --omit=dev',
6
+ build: 'npm run build',
7
+ },
8
+ pnpm: {
9
+ lockFile: 'pnpm-lock.yaml',
10
+ install: 'pnpm install --frozen-lockfile',
11
+ installProd: 'pnpm install --frozen-lockfile --prod',
12
+ build: 'pnpm run build',
13
+ setup: 'RUN corepack enable && corepack prepare pnpm@latest --activate',
14
+ },
15
+ yarn: {
16
+ lockFile: 'yarn.lock',
17
+ install: 'yarn install --frozen-lockfile',
18
+ installProd: 'yarn install --frozen-lockfile --production',
19
+ build: 'yarn build',
20
+ setup: 'RUN corepack enable',
21
+ },
22
+ };
23
+ export function getDockerfileTemplate(options) {
24
+ const packageManager = options?.packageManager ?? 'npm';
25
+ const config = packageManagerConfigs[packageManager];
26
+ const setupStep = config.setup ? `\n${config.setup}\n` : '';
27
+ return `# Multi-stage build for production
28
+ FROM node:20-alpine AS builder
29
+
30
+ WORKDIR /app
31
+ ${setupStep}
32
+ # Copy package files
33
+ COPY package.json ${config.lockFile} ./
34
+
35
+ # Install all dependencies (including dev)
36
+ RUN ${config.install}
37
+
38
+ # Copy source code
39
+ COPY . .
40
+
41
+ # Build the application
42
+ RUN ${config.build}
43
+
44
+ # Production stage
45
+ FROM node:20-alpine AS production
46
+
47
+ WORKDIR /app
48
+ ${setupStep}
49
+ # Copy package files
50
+ COPY package.json ${config.lockFile} ./
51
+
52
+ # Install production dependencies only
53
+ RUN ${config.installProd}
54
+
55
+ # Copy built application from builder stage
56
+ COPY --from=builder /app/dist ./dist
57
+
58
+ # Expose the port the app runs on
59
+ EXPOSE 3000
60
+
61
+ # Start the application
62
+ CMD ["node", "dist/index.js"]
63
+ `;
64
+ }
@@ -0,0 +1 @@
1
+ export declare function getDockerignoreTemplate(): string;
@@ -0,0 +1,37 @@
1
+ export function getDockerignoreTemplate() {
2
+ return `# Dependencies
3
+ node_modules/
4
+
5
+ # Build output (rebuilt in container)
6
+ dist/
7
+
8
+ # Git
9
+ .git/
10
+ .gitignore
11
+
12
+ # Environment files (should be set in container)
13
+ .env
14
+ .env.*
15
+
16
+ # Documentation
17
+ *.md
18
+
19
+ # IDE
20
+ .vscode/
21
+ .idea/
22
+
23
+ # OS files
24
+ .DS_Store
25
+ Thumbs.db
26
+
27
+ # Logs
28
+ *.log
29
+ npm-debug.log*
30
+
31
+ # Test files
32
+ *.test.ts
33
+ *.spec.ts
34
+ __tests__/
35
+ coverage/
36
+ `;
37
+ }
@@ -0,0 +1,2 @@
1
+ export { getDockerfileTemplate } from './dockerfile.js';
2
+ export { getDockerignoreTemplate } from './dockerignore.js';
@@ -0,0 +1,2 @@
1
+ export { getDockerfileTemplate } from './dockerfile.js';
2
+ export { getDockerignoreTemplate } from './dockerignore.js';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,109 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { getDockerfileTemplate } from './dockerfile.js';
3
+ import { getDockerignoreTemplate } from './dockerignore.js';
4
+ describe('deployment templates', () => {
5
+ describe('getDockerfileTemplate', () => {
6
+ it('should use multi-stage build', () => {
7
+ const template = getDockerfileTemplate();
8
+ expect(template).toContain('AS builder');
9
+ expect(template).toContain('AS production');
10
+ });
11
+ it('should use Node 20 Alpine', () => {
12
+ const template = getDockerfileTemplate();
13
+ expect(template).toContain('FROM node:20-alpine');
14
+ });
15
+ it('should copy dist folder from builder', () => {
16
+ const template = getDockerfileTemplate();
17
+ expect(template).toContain('COPY --from=builder /app/dist ./dist');
18
+ });
19
+ it('should expose port 3000', () => {
20
+ const template = getDockerfileTemplate();
21
+ expect(template).toContain('EXPOSE 3000');
22
+ });
23
+ it('should run node dist/index.js', () => {
24
+ const template = getDockerfileTemplate();
25
+ expect(template).toContain('CMD ["node", "dist/index.js"]');
26
+ });
27
+ describe('npm (default)', () => {
28
+ it('should use npm ci for install', () => {
29
+ const template = getDockerfileTemplate();
30
+ expect(template).toContain('RUN npm ci');
31
+ });
32
+ it('should use npm ci --omit=dev for production', () => {
33
+ const template = getDockerfileTemplate();
34
+ expect(template).toContain('npm ci --omit=dev');
35
+ });
36
+ it('should copy package-lock.json', () => {
37
+ const template = getDockerfileTemplate();
38
+ expect(template).toContain('COPY package.json package-lock.json');
39
+ });
40
+ it('should use npm run build', () => {
41
+ const template = getDockerfileTemplate();
42
+ expect(template).toContain('RUN npm run build');
43
+ });
44
+ });
45
+ describe('pnpm', () => {
46
+ it('should use pnpm install --frozen-lockfile', () => {
47
+ const template = getDockerfileTemplate({ packageManager: 'pnpm' });
48
+ expect(template).toContain('RUN pnpm install --frozen-lockfile');
49
+ });
50
+ it('should use pnpm install --frozen-lockfile --prod for production', () => {
51
+ const template = getDockerfileTemplate({ packageManager: 'pnpm' });
52
+ expect(template).toContain('pnpm install --frozen-lockfile --prod');
53
+ });
54
+ it('should copy pnpm-lock.yaml', () => {
55
+ const template = getDockerfileTemplate({ packageManager: 'pnpm' });
56
+ expect(template).toContain('COPY package.json pnpm-lock.yaml');
57
+ });
58
+ it('should enable corepack for pnpm', () => {
59
+ const template = getDockerfileTemplate({ packageManager: 'pnpm' });
60
+ expect(template).toContain('corepack enable');
61
+ expect(template).toContain('corepack prepare pnpm');
62
+ });
63
+ it('should use pnpm run build', () => {
64
+ const template = getDockerfileTemplate({ packageManager: 'pnpm' });
65
+ expect(template).toContain('RUN pnpm run build');
66
+ });
67
+ });
68
+ describe('yarn', () => {
69
+ it('should use yarn install --frozen-lockfile', () => {
70
+ const template = getDockerfileTemplate({ packageManager: 'yarn' });
71
+ expect(template).toContain('RUN yarn install --frozen-lockfile');
72
+ });
73
+ it('should use yarn install --frozen-lockfile --production for production', () => {
74
+ const template = getDockerfileTemplate({ packageManager: 'yarn' });
75
+ expect(template).toContain('yarn install --frozen-lockfile --production');
76
+ });
77
+ it('should copy yarn.lock', () => {
78
+ const template = getDockerfileTemplate({ packageManager: 'yarn' });
79
+ expect(template).toContain('COPY package.json yarn.lock');
80
+ });
81
+ it('should enable corepack for yarn', () => {
82
+ const template = getDockerfileTemplate({ packageManager: 'yarn' });
83
+ expect(template).toContain('corepack enable');
84
+ });
85
+ it('should use yarn build', () => {
86
+ const template = getDockerfileTemplate({ packageManager: 'yarn' });
87
+ expect(template).toContain('RUN yarn build');
88
+ });
89
+ });
90
+ });
91
+ describe('getDockerignoreTemplate', () => {
92
+ it('should ignore node_modules', () => {
93
+ const template = getDockerignoreTemplate();
94
+ expect(template).toContain('node_modules/');
95
+ });
96
+ it('should ignore dist folder', () => {
97
+ const template = getDockerignoreTemplate();
98
+ expect(template).toContain('dist/');
99
+ });
100
+ it('should ignore .env files', () => {
101
+ const template = getDockerignoreTemplate();
102
+ expect(template).toContain('.env');
103
+ });
104
+ it('should ignore .git folder', () => {
105
+ const template = getDockerignoreTemplate();
106
+ expect(template).toContain('.git/');
107
+ });
108
+ });
109
+ });
@@ -34,9 +34,10 @@ ${cmd.start}
34
34
 
35
35
  The server will start on port 3000 by default. You can change this by setting the \`PORT\` environment variable.
36
36
 
37
- ## API Endpoint
37
+ ## API Endpoints
38
38
 
39
39
  - **POST /mcp** - Main MCP endpoint for JSON-RPC messages
40
+ - **GET /health** - Health check endpoint (returns 200 OK)
40
41
 
41
42
  ## Included Examples
42
43
 
@@ -63,11 +64,22 @@ ${projectName}/
63
64
  ├── src/
64
65
  │ ├── server.ts # FastMCP server definition (tools, prompts, resources)
65
66
  │ └── index.ts # Server startup configuration
67
+ ├── Dockerfile # Multi-stage Docker build
66
68
  ├── package.json
67
69
  ├── tsconfig.json
68
70
  └── README.md
69
71
  \`\`\`
70
72
 
73
+ ## Deployment
74
+
75
+ ### Docker
76
+
77
+ Build and run the Docker container:
78
+
79
+ \`\`\`bash
80
+ docker build -t ${projectName} .
81
+ docker run -p 3000:3000 ${projectName}
82
+ \`\`\`
71
83
  ## Customization
72
84
 
73
85
  - Add new tools, prompts, and resources in \`src/server.ts\`
@@ -19,7 +19,10 @@ import { createMcpExpressApp } from '@modelcontextprotocol/sdk/server/express.js
19
19
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
20
20
  import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
21
21
  import { getServer } from './server.js';`;
22
- const appSetup = `const app = createMcpExpressApp();`;
22
+ const appSetup = `const app = createMcpExpressApp();
23
+
24
+ // Health check endpoint for container orchestration
25
+ app.get('/health', (_, res) => res.sendStatus(200));`;
23
26
  const postRoute = withOAuth
24
27
  ? `app.post('/mcp', authMiddleware, async (req: Request, res: Response) => {`
25
28
  : `app.post('/mcp', async (req: Request, res: Response) => {`;
@@ -63,6 +63,7 @@ ${projectName}/
63
63
  │ ├── server.ts # MCP server definition (tools, prompts, resources)
64
64
  │ ├── index.ts # Express app and stateful HTTP transport setup
65
65
  │ └── auth.ts # OAuth configuration and middleware
66
+ ├── Dockerfile # Multi-stage Docker build
66
67
  ├── package.json
67
68
  ├── tsconfig.json
68
69
  └── README.md
@@ -72,10 +73,23 @@ ${projectName}/
72
73
  ├── src/
73
74
  │ ├── server.ts # MCP server definition (tools, prompts, resources)
74
75
  │ └── index.ts # Express app and stateful HTTP transport setup
76
+ ├── Dockerfile # Multi-stage Docker build
75
77
  ├── package.json
76
78
  ├── tsconfig.json
77
79
  └── README.md
78
80
  \`\`\``;
81
+ const deploymentSection = `
82
+ ## Deployment
83
+
84
+ ### Docker
85
+
86
+ Build and run the Docker container:
87
+
88
+ \`\`\`bash
89
+ docker build -t ${projectName} .
90
+ docker run -p 3000:3000 ${projectName}
91
+ \`\`\`
92
+ `;
79
93
  const customizationOAuthNote = withOAuth
80
94
  ? '\n- Configure OAuth scopes and token verification in `src/auth.ts`'
81
95
  : '';
@@ -112,6 +126,7 @@ ${oauthSection}
112
126
  - Requires \`mcp-session-id\` header${apiEndpointsOAuthNote}
113
127
  - **DELETE /mcp** - Terminate a session
114
128
  - Requires \`mcp-session-id\` header${apiEndpointsOAuthNote}
129
+ - **GET /health** - Health check endpoint (returns 200 OK)
115
130
 
116
131
  ## Session Management
117
132
 
@@ -144,6 +159,7 @@ This server comes with example implementations to help you get started:
144
159
  ## Project Structure
145
160
 
146
161
  ${projectStructure}
162
+ ${deploymentSection}
147
163
 
148
164
  ## Customization
149
165
 
@@ -8,6 +8,9 @@ import { getServer } from './server.js';
8
8
 
9
9
  const app = createMcpExpressApp();
10
10
 
11
+ // Health check endpoint for container orchestration
12
+ app.get('/health', (_, res) => res.sendStatus(200));
13
+
11
14
  app.post('/mcp', async (req: Request, res: Response) => {
12
15
  const server = getServer();
13
16
  try {
@@ -29,9 +29,10 @@ ${commands.start}
29
29
 
30
30
  The server will start on port 3000 by default. You can change this by setting the \`PORT\` environment variable.
31
31
 
32
- ## API Endpoint
32
+ ## API Endpoints
33
33
 
34
34
  - **POST /mcp** - Main MCP endpoint for JSON-RPC messages
35
+ - **GET /health** - Health check endpoint (returns 200 OK)
35
36
 
36
37
  ## Included Examples
37
38
 
@@ -58,11 +59,23 @@ ${projectName}/
58
59
  ├── src/
59
60
  │ ├── server.ts # MCP server definition (tools, prompts, resources)
60
61
  │ └── index.ts # Express app and HTTP transport setup
62
+ ├── Dockerfile # Multi-stage Docker build
61
63
  ├── package.json
62
64
  ├── tsconfig.json
63
65
  └── README.md
64
66
  \`\`\`
65
67
 
68
+ ## Deployment
69
+
70
+ ### Docker
71
+
72
+ Build and run the Docker container:
73
+
74
+ \`\`\`bash
75
+ docker build -t ${projectName} .
76
+ docker run -p 3000:3000 ${projectName}
77
+ \`\`\`
78
+
66
79
  ## Customization
67
80
 
68
81
  - Add new tools, prompts, and resources in \`src/server.ts\`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentailor/create-mcp-server",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Create a new MCP (Model Context Protocol) server project",
5
5
  "type": "module",
6
6
  "bin": {