@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 +2 -0
- package/dist/index.js +3 -0
- package/dist/templates/deployment/dockerfile.d.ts +2 -0
- package/dist/templates/deployment/dockerfile.js +64 -0
- package/dist/templates/deployment/dockerignore.d.ts +1 -0
- package/dist/templates/deployment/dockerignore.js +37 -0
- package/dist/templates/deployment/index.d.ts +2 -0
- package/dist/templates/deployment/index.js +2 -0
- package/dist/templates/deployment/templates.test.d.ts +1 -0
- package/dist/templates/deployment/templates.test.js +109 -0
- package/dist/templates/fastmcp/readme.js +13 -1
- package/dist/templates/sdk/stateful/index.js +4 -1
- package/dist/templates/sdk/stateful/readme.js +16 -0
- package/dist/templates/sdk/stateless/index.js +3 -0
- package/dist/templates/sdk/stateless/readme.js +14 -1
- package/package.json +1 -1
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,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 @@
|
|
|
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
|
|
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
|
|
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\`
|