@agentailor/create-mcp-server 0.5.2 → 0.6.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
@@ -30,8 +30,9 @@ npx @agentailor/create-mcp-server --name=my-server
30
30
  | `--name` | `-n` | — | Project name (required in CLI mode) |
31
31
  | `--package-manager` | `-p` | `npm` | Package manager: npm, pnpm, yarn |
32
32
  | `--framework` | `-f` | `sdk` | Framework: sdk, fastmcp |
33
- | `--template` | `-t` | `stateless` | Server mode: stateless, stateful |
34
- | `--oauth` | | `false` | Enable OAuth (sdk+stateful only) |
33
+ | `--stdio` | | `false` | Use stdio transport (for local clients) |
34
+ | `--template` | `-t` | `stateless` | Server mode: stateless, stateful (HTTP only) |
35
+ | `--oauth` | — | `false` | Enable OAuth (sdk+stateful only, incompatible with --stdio) |
35
36
  | `--no-git` | — | `false` | Skip git initialization |
36
37
  | `--help` | `-h` | — | Show help |
37
38
  | `--version` | `-V` | — | Show version |
@@ -39,10 +40,16 @@ npx @agentailor/create-mcp-server --name=my-server
39
40
  **Examples:**
40
41
 
41
42
  ```bash
42
- # Minimal - uses all defaults
43
+ # Minimal - uses all defaults (HTTP streamable)
43
44
  npx @agentailor/create-mcp-server --name=my-server
44
45
 
45
- # Full options
46
+ # stdio server (for local clients)
47
+ npx @agentailor/create-mcp-server --name=my-server --stdio
48
+
49
+ # stdio with FastMCP
50
+ npx @agentailor/create-mcp-server --name=my-server --stdio --framework=fastmcp
51
+
52
+ # Full HTTP options
46
53
  npx @agentailor/create-mcp-server \
47
54
  --name=my-auth-server \
48
55
  --package-manager=pnpm \
@@ -57,11 +64,12 @@ npx @agentailor/create-mcp-server -n my-server -p yarn -f fastmcp
57
64
  ## Features
58
65
 
59
66
  - **Two frameworks** — Official MCP SDK or FastMCP
60
- - **Two server modes** — stateless or stateful with session management
61
- - **Optional OAuth** — OIDC-compliant authentication (SDK only) ([setup guide](docs/oauth-setup.md))
67
+ - **Two transport types** — HTTP (streamable) or stdio (for local cllients)
68
+ - **Two HTTP server modes** — stateless or stateful with session management
69
+ - **Optional OAuth** — OIDC-compliant authentication (SDK HTTP only) ([setup guide](docs/oauth-setup.md))
62
70
  - **Package manager choice** — npm, pnpm, or yarn
63
71
  - **TypeScript ready** — ready to customize
64
- - **Docker ready** — production Dockerfile included
72
+ - **Docker ready** — production Dockerfile included (HTTP transport)
65
73
  - **MCP Inspector** — built-in debugging with `npm run inspect`
66
74
 
67
75
  ## Frameworks
@@ -93,7 +101,22 @@ server.start({ transportType: "httpStream", httpStream: { port: 3000 } });
93
101
 
94
102
  Learn more: [FastMCP Documentation](https://github.com/punkpeye/fastmcp)
95
103
 
96
- ## Server Modes
104
+ ## Transport Types
105
+
106
+ | Feature | HTTP (Streamable HTTP) | stdio |
107
+ |---------|------------------------|-------|
108
+ | Use case | Remote access, cloud deployment | Local clients (Claude Desktop) |
109
+ | Protocol | HTTP/SSE | stdin/stdout |
110
+ | Session management | ✓ (stateful mode) | — |
111
+ | OAuth support | ✓ (SDK stateful) | — |
112
+ | Docker deployment | ✓ | — |
113
+ | Port configuration | ✓ | — |
114
+
115
+ **HTTP**: Deploy as an HTTP server accessible remotely. Choose stateless or stateful mode.
116
+
117
+ **stdio**: Run as a local process. Communicates over stdin/stdout. Ideal for local clients. No HTTP server, no port, no Dockerfile generated.
118
+
119
+ ## Server Modes (HTTP only)
97
120
 
98
121
  | Feature | Stateless (default) | Stateful |
99
122
  |---------|---------------------|----------|
package/dist/cli.d.ts CHANGED
@@ -1,9 +1,10 @@
1
- import type { PackageManager, Framework } from './templates/common/types.js';
1
+ import type { PackageManager, Framework, TransportType } from './templates/common/types.js';
2
2
  export type TemplateType = 'stateless' | 'stateful';
3
3
  export interface CLIOptions {
4
4
  name: string;
5
5
  packageManager: PackageManager;
6
6
  framework: Framework;
7
+ transport: TransportType;
7
8
  template: TemplateType;
8
9
  oauth: boolean;
9
10
  git: boolean;
package/dist/cli.js CHANGED
@@ -23,6 +23,7 @@ export function parseArguments() {
23
23
  .addOption(new Option('-t, --template <type>', 'Template type')
24
24
  .choices(['stateless', 'stateful'])
25
25
  .default('stateless'))
26
+ .option('--stdio', 'Use stdio transport instead of HTTP', false)
26
27
  .option('--oauth', 'Enable OAuth authentication (sdk+stateful only)', false)
27
28
  .option('--no-git', 'Skip git repository initialization');
28
29
  program.parse();
@@ -41,6 +42,15 @@ export function parseArguments() {
41
42
  console.error('Run with --help for all options.\n');
42
43
  process.exit(1);
43
44
  }
45
+ // Validate stdio constraints
46
+ if (opts.stdio && opts.oauth) {
47
+ console.error('\nError: --stdio cannot be combined with --oauth\n');
48
+ process.exit(1);
49
+ }
50
+ if (opts.stdio && opts.template === 'stateful') {
51
+ console.error('\nError: --template=stateful is not applicable with --stdio (stdio is inherently stateless)\n');
52
+ process.exit(1);
53
+ }
44
54
  // Validate OAuth constraint
45
55
  if (opts.oauth && (opts.framework !== 'sdk' || opts.template !== 'stateful')) {
46
56
  console.error('\nError: --oauth is only valid with --framework=sdk and --template=stateful\n');
@@ -52,6 +62,7 @@ export function parseArguments() {
52
62
  name: opts.name,
53
63
  packageManager: opts.packageManager,
54
64
  framework: opts.framework,
65
+ transport: (opts.stdio ? 'stdio' : 'http'),
55
66
  template: opts.template,
56
67
  oauth: opts.oauth,
57
68
  git: opts.git, // Commander handles --no-git -> git: false
package/dist/cli.test.js CHANGED
@@ -32,6 +32,7 @@ describe('CLI argument parsing', () => {
32
32
  const result = parseArguments();
33
33
  expect(result.options?.packageManager).toBe('npm');
34
34
  expect(result.options?.framework).toBe('sdk');
35
+ expect(result.options?.transport).toBe('http');
35
36
  expect(result.options?.template).toBe('stateless');
36
37
  expect(result.options?.oauth).toBe(false);
37
38
  expect(result.options?.git).toBe(true);
@@ -52,6 +53,7 @@ describe('CLI argument parsing', () => {
52
53
  name: 'test-project',
53
54
  packageManager: 'pnpm',
54
55
  framework: 'fastmcp',
56
+ transport: 'http',
55
57
  template: 'stateful',
56
58
  oauth: false,
57
59
  git: false,
@@ -142,4 +144,61 @@ describe('CLI argument parsing', () => {
142
144
  // No args = interactive mode
143
145
  expect(result.mode).toBe('interactive');
144
146
  });
147
+ it('--stdio sets transport to stdio', async () => {
148
+ process.argv = ['node', 'create-mcp-server', '--name=my-stdio-server', '--stdio'];
149
+ const { parseArguments } = await import('./cli.js');
150
+ const result = parseArguments();
151
+ expect(result.mode).toBe('cli');
152
+ expect(result.options?.transport).toBe('stdio');
153
+ expect(result.options?.oauth).toBe(false);
154
+ });
155
+ it('--stdio with --framework=fastmcp is valid', async () => {
156
+ process.argv = [
157
+ 'node',
158
+ 'create-mcp-server',
159
+ '--name=my-server',
160
+ '--stdio',
161
+ '--framework=fastmcp',
162
+ ];
163
+ const { parseArguments } = await import('./cli.js');
164
+ const result = parseArguments();
165
+ expect(result.options?.transport).toBe('stdio');
166
+ expect(result.options?.framework).toBe('fastmcp');
167
+ });
168
+ it('exits with error when --stdio combined with --oauth', async () => {
169
+ process.argv = [
170
+ 'node',
171
+ 'create-mcp-server',
172
+ '--name=test',
173
+ '--stdio',
174
+ '--oauth',
175
+ '--framework=sdk',
176
+ '--template=stateful',
177
+ ];
178
+ let exitCode;
179
+ process.exit = vi.fn((code) => {
180
+ exitCode = code;
181
+ throw new Error('process.exit called');
182
+ });
183
+ const consoleError = vi.spyOn(console, 'error').mockImplementation(() => { });
184
+ const { parseArguments } = await import('./cli.js');
185
+ expect(() => parseArguments()).toThrow('process.exit called');
186
+ expect(exitCode).toBe(1);
187
+ expect(consoleError).toHaveBeenCalledWith(expect.stringContaining('--stdio cannot be combined with --oauth'));
188
+ consoleError.mockRestore();
189
+ });
190
+ it('exits with error when --stdio combined with --template=stateful', async () => {
191
+ process.argv = ['node', 'create-mcp-server', '--name=test', '--stdio', '--template=stateful'];
192
+ let exitCode;
193
+ process.exit = vi.fn((code) => {
194
+ exitCode = code;
195
+ throw new Error('process.exit called');
196
+ });
197
+ const consoleError = vi.spyOn(console, 'error').mockImplementation(() => { });
198
+ const { parseArguments } = await import('./cli.js');
199
+ expect(() => parseArguments()).toThrow('process.exit called');
200
+ expect(exitCode).toBe(1);
201
+ expect(consoleError).toHaveBeenCalledWith(expect.stringContaining('--template=stateful is not applicable with --stdio'));
202
+ consoleError.mockRestore();
203
+ });
145
204
  });
package/dist/index.js CHANGED
@@ -13,6 +13,7 @@ async function main() {
13
13
  projectName: options.name,
14
14
  packageManager: options.packageManager,
15
15
  framework: options.framework,
16
+ transport: options.transport,
16
17
  templateType: options.template,
17
18
  withOAuth: options.oauth,
18
19
  withGitInit: options.git,
@@ -57,25 +57,48 @@ export async function runInteractiveMode() {
57
57
  initial: 0,
58
58
  }, { onCancel });
59
59
  const framework = frameworkResponse.framework || 'sdk';
60
- // Server mode selection
61
- const templateTypeResponse = await prompts({
60
+ // Transport selection
61
+ const transportResponse = await prompts({
62
62
  type: 'select',
63
- name: 'templateType',
64
- message: 'Server mode:',
63
+ name: 'transport',
64
+ message: 'Transport:',
65
65
  choices: [
66
- { title: 'Stateless', value: 'stateless', description: 'Simple HTTP server' },
67
66
  {
68
- title: 'Stateful',
69
- value: 'stateful',
70
- description: 'Session-based server with SSE support',
67
+ title: 'HTTP (Streamable HTTP)',
68
+ value: 'http',
69
+ description: 'Deploy as an HTTP server (recommended for remote access)',
70
+ },
71
+ {
72
+ title: 'stdio',
73
+ value: 'stdio',
74
+ description: 'For local use with local clients like Claude Desktop',
71
75
  },
72
76
  ],
73
77
  initial: 0,
74
78
  }, { onCancel });
75
- const templateType = templateTypeResponse.templateType || 'stateless';
76
- // OAuth prompt - only for SDK stateful template
79
+ const transport = transportResponse.transport || 'http';
80
+ // Server mode selection - only for HTTP transport
81
+ let templateType = 'stateless';
82
+ if (transport === 'http') {
83
+ const templateTypeResponse = await prompts({
84
+ type: 'select',
85
+ name: 'templateType',
86
+ message: 'Server mode:',
87
+ choices: [
88
+ { title: 'Stateless', value: 'stateless', description: 'Simple HTTP server' },
89
+ {
90
+ title: 'Stateful',
91
+ value: 'stateful',
92
+ description: 'Session-based server with SSE support',
93
+ },
94
+ ],
95
+ initial: 0,
96
+ }, { onCancel });
97
+ templateType = templateTypeResponse.templateType || 'stateless';
98
+ }
99
+ // OAuth prompt - only for SDK stateful HTTP template
77
100
  let withOAuth = false;
78
- if (framework === 'sdk' && templateType === 'stateful') {
101
+ if (transport === 'http' && framework === 'sdk' && templateType === 'stateful') {
79
102
  const oauthResponse = await prompts({
80
103
  type: 'confirm',
81
104
  name: 'withOAuth',
@@ -96,6 +119,7 @@ export async function runInteractiveMode() {
96
119
  projectName,
97
120
  packageManager,
98
121
  framework,
122
+ transport,
99
123
  templateType,
100
124
  withOAuth,
101
125
  withGitInit,
@@ -1,4 +1,4 @@
1
- import type { Framework, PackageManager } from './templates/common/types.js';
1
+ import type { Framework, PackageManager, TransportType } from './templates/common/types.js';
2
2
  import type { TemplateType } from './cli.js';
3
3
  export declare const packageManagerCommands: Record<PackageManager, {
4
4
  install: string;
@@ -8,6 +8,7 @@ export interface ProjectConfig {
8
8
  projectName: string;
9
9
  packageManager: PackageManager;
10
10
  framework: Framework;
11
+ transport: TransportType;
11
12
  templateType: TemplateType;
12
13
  withOAuth: boolean;
13
14
  withGitInit: boolean;
@@ -8,6 +8,7 @@ import { getEnvExampleTemplate } from './templates/common/env.example.js';
8
8
  import { getServerTemplate as getSdkStatelessServerTemplate, getIndexTemplate as getSdkStatelessIndexTemplate, getReadmeTemplate as getSdkStatelessReadmeTemplate, } from './templates/sdk/stateless/index.js';
9
9
  import { getServerTemplate as getSdkStatefulServerTemplate, getIndexTemplate as getSdkStatefulIndexTemplate, getReadmeTemplate as getSdkStatefulReadmeTemplate, getAuthTemplate as getSdkAuthTemplate, } from './templates/sdk/stateful/index.js';
10
10
  import { getServerTemplate as getFastMCPServerTemplate, getIndexTemplate as getFastMCPIndexTemplate, getReadmeTemplate as getFastMCPReadmeTemplate, } from './templates/fastmcp/index.js';
11
+ import { getServerTemplate as getSdkStdioServerTemplate, getIndexTemplate as getSdkStdioIndexTemplate, getReadmeTemplate as getSdkStdioReadmeTemplate, } from './templates/sdk/stdio/index.js';
11
12
  import { getDockerfileTemplate, getDockerignoreTemplate } from './templates/deployment/index.js';
12
13
  const sdkTemplateFunctions = {
13
14
  stateless: {
@@ -33,12 +34,13 @@ export const packageManagerCommands = {
33
34
  yarn: { install: 'yarn', dev: 'yarn dev' },
34
35
  };
35
36
  export async function generateProject(config) {
36
- const { projectName, packageManager, framework, templateType, withOAuth, withGitInit } = config;
37
+ const { projectName, packageManager, framework, transport, templateType, withOAuth, withGitInit, } = config;
37
38
  const templateOptions = {
38
- withOAuth,
39
+ withOAuth: transport === 'http' ? withOAuth : false,
39
40
  packageManager,
40
41
  framework,
41
- stateless: templateType === 'stateless',
42
+ stateless: transport === 'stdio' ? true : templateType === 'stateless',
43
+ transport,
42
44
  };
43
45
  const projectPath = join(process.cwd(), projectName);
44
46
  const srcPath = join(projectPath, 'src');
@@ -50,8 +52,12 @@ export async function generateProject(config) {
50
52
  // FastMCP templates
51
53
  filesToWrite.push(writeFile(join(srcPath, 'server.ts'), fastmcpTemplateFunctions.getServerTemplate(projectName)), writeFile(join(srcPath, 'index.ts'), fastmcpTemplateFunctions.getIndexTemplate(templateOptions)), writeFile(join(projectPath, 'README.md'), fastmcpTemplateFunctions.getReadmeTemplate(projectName, templateOptions)));
52
54
  }
55
+ else if (transport === 'stdio') {
56
+ // SDK stdio templates
57
+ filesToWrite.push(writeFile(join(srcPath, 'server.ts'), getSdkStdioServerTemplate(projectName)), writeFile(join(srcPath, 'index.ts'), getSdkStdioIndexTemplate(templateOptions)), writeFile(join(projectPath, 'README.md'), getSdkStdioReadmeTemplate(projectName, templateOptions)));
58
+ }
53
59
  else {
54
- // SDK templates
60
+ // SDK HTTP templates (stateless or stateful)
55
61
  const templates = sdkTemplateFunctions[templateType];
56
62
  filesToWrite.push(writeFile(join(srcPath, 'server.ts'), templates.getServerTemplate(projectName)), writeFile(join(srcPath, 'index.ts'), templates.getIndexTemplate(templateOptions)), writeFile(join(projectPath, 'README.md'), templates.getReadmeTemplate(projectName, templateOptions)));
57
63
  // Conditionally add auth.ts for OAuth-enabled stateful template
@@ -61,8 +67,10 @@ export async function generateProject(config) {
61
67
  }
62
68
  // Common files for all templates
63
69
  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)));
64
- // Deployment files for all templates
65
- filesToWrite.push(writeFile(join(projectPath, 'Dockerfile'), getDockerfileTemplate(templateOptions)), writeFile(join(projectPath, '.dockerignore'), getDockerignoreTemplate()));
70
+ // Deployment files for HTTP transport only (stdio servers are not HTTP services)
71
+ if (transport === 'http') {
72
+ filesToWrite.push(writeFile(join(projectPath, 'Dockerfile'), getDockerfileTemplate(templateOptions)), writeFile(join(projectPath, '.dockerignore'), getDockerignoreTemplate()));
73
+ }
66
74
  // Write all template files
67
75
  await Promise.all(filesToWrite);
68
76
  // Initialize git repository if requested
@@ -76,7 +84,7 @@ export async function generateProject(config) {
76
84
  }
77
85
  const commands = packageManagerCommands[packageManager];
78
86
  const frameworkName = framework === 'fastmcp' ? 'FastMCP' : 'MCP SDK';
79
- console.log(`\nCreated ${projectName} with ${frameworkName} at ${projectPath}`);
87
+ console.log(`\nCreated ${projectName} with ${frameworkName} (${transport}) at ${projectPath}`);
80
88
  console.log(`\nNext steps:`);
81
89
  console.log(` cd ${projectName}`);
82
90
  console.log(` ${commands.install}`);
@@ -1,5 +1,16 @@
1
1
  export function getEnvExampleTemplate(options) {
2
+ const transport = options?.transport ?? 'http';
3
+ if (transport === 'stdio') {
4
+ return `# No environment variables required for stdio transport\n`;
5
+ }
2
6
  const withOAuth = options?.withOAuth ?? false;
7
+ const isSdk = options?.framework === 'sdk' || !options?.framework;
8
+ const allowedHostsVar = isSdk
9
+ ? `
10
+ # Comma-separated list of additional allowed hosts (for deployment behind reverse proxies)
11
+ # ALLOWED_HOSTS=my-server.example.com,my-server.us-central1.run.app
12
+ `
13
+ : '';
3
14
  const oauthVars = withOAuth
4
15
  ? `
5
16
  # OAuth Configuration
@@ -15,5 +26,5 @@ OAUTH_AUDIENCE=https://your-mcp-server.com
15
26
  `
16
27
  : '';
17
28
  return `PORT=3000
18
- ${oauthVars}`;
29
+ ${allowedHostsVar}${oauthVars}`;
19
30
  }
@@ -1,6 +1,7 @@
1
1
  export function getPackageJsonTemplate(projectName, options) {
2
2
  const withOAuth = options?.withOAuth ?? false;
3
3
  const framework = options?.framework ?? 'sdk';
4
+ const transport = options?.transport ?? 'http';
4
5
  let dependencies;
5
6
  let devDependencies;
6
7
  const commonDevDependencies = {
@@ -21,8 +22,19 @@ export function getPackageJsonTemplate(projectName, options) {
21
22
  ...commonDevDependencies,
22
23
  };
23
24
  }
25
+ else if (transport === 'stdio') {
26
+ // Official SDK stdio - no express needed
27
+ dependencies = {
28
+ '@modelcontextprotocol/sdk': '^1.26.0',
29
+ ...zodDependency,
30
+ ...dotEnvDependency,
31
+ };
32
+ devDependencies = {
33
+ ...commonDevDependencies,
34
+ };
35
+ }
24
36
  else {
25
- // Official SDK dependencies
37
+ // Official SDK HTTP dependencies
26
38
  dependencies = {
27
39
  '@modelcontextprotocol/sdk': '^1.26.0',
28
40
  express: '^5.2.1',
@@ -37,6 +49,13 @@ export function getPackageJsonTemplate(projectName, options) {
37
49
  ...commonDevDependencies,
38
50
  };
39
51
  }
52
+ const inspectScripts = transport === 'stdio'
53
+ ? {
54
+ 'inspect:tools': 'mcp-inspector --cli node dist/index.js --method tools/list',
55
+ 'inspect:prompts': 'mcp-inspector --cli node dist/index.js --method prompts/list',
56
+ 'inspect:resources': 'mcp-inspector --cli node dist/index.js --method resources/list',
57
+ }
58
+ : { inspect: 'mcp-inspector http://localhost:3000/mcp' };
40
59
  const packageJson = {
41
60
  name: projectName,
42
61
  version: '0.1.0',
@@ -46,7 +65,7 @@ export function getPackageJsonTemplate(projectName, options) {
46
65
  build: 'tsc',
47
66
  dev: 'tsc && node dist/index.js',
48
67
  start: 'node dist/index.js',
49
- inspect: 'mcp-inspector http://localhost:3000/mcp',
68
+ ...inspectScripts,
50
69
  },
51
70
  dependencies,
52
71
  devDependencies,
@@ -49,6 +49,32 @@ describe('common templates', () => {
49
49
  const pkg = JSON.parse(template);
50
50
  expect(pkg.devDependencies['@types/express']).toBeDefined();
51
51
  });
52
+ it('should NOT include express or @types/express for SDK stdio', () => {
53
+ const template = getPackageJsonTemplate(projectName, {
54
+ framework: 'sdk',
55
+ transport: 'stdio',
56
+ });
57
+ const pkg = JSON.parse(template);
58
+ expect(pkg.dependencies['express']).toBeUndefined();
59
+ expect(pkg.devDependencies['@types/express']).toBeUndefined();
60
+ expect(pkg.dependencies['@modelcontextprotocol/sdk']).toBeDefined();
61
+ });
62
+ it('should use inspect:tools, inspect:prompts, inspect:resources scripts for stdio transport', () => {
63
+ const template = getPackageJsonTemplate(projectName, {
64
+ framework: 'sdk',
65
+ transport: 'stdio',
66
+ });
67
+ const pkg = JSON.parse(template);
68
+ expect(pkg.scripts['inspect:tools']).toContain('--method tools/list');
69
+ expect(pkg.scripts['inspect:prompts']).toContain('--method prompts/list');
70
+ expect(pkg.scripts['inspect:resources']).toContain('--method resources/list');
71
+ expect(pkg.scripts['inspect']).toBeUndefined();
72
+ });
73
+ it('should use http inspect script for http transport (default)', () => {
74
+ const template = getPackageJsonTemplate(projectName, { framework: 'sdk', transport: 'http' });
75
+ const pkg = JSON.parse(template);
76
+ expect(pkg.scripts.inspect).toContain('http://localhost:3000/mcp');
77
+ });
52
78
  it('should include required scripts', () => {
53
79
  const template = getPackageJsonTemplate(projectName);
54
80
  const pkg = JSON.parse(template);
@@ -89,5 +115,25 @@ describe('common templates', () => {
89
115
  const template = getEnvExampleTemplate();
90
116
  expect(template).toContain('PORT=');
91
117
  });
118
+ it('should include ALLOWED_HOSTS hint for SDK framework', () => {
119
+ const template = getEnvExampleTemplate({ framework: 'sdk' });
120
+ expect(template).toContain('ALLOWED_HOSTS');
121
+ });
122
+ it('should include ALLOWED_HOSTS hint by default (no framework specified)', () => {
123
+ const template = getEnvExampleTemplate();
124
+ expect(template).toContain('ALLOWED_HOSTS');
125
+ });
126
+ it('should NOT include ALLOWED_HOSTS for FastMCP framework', () => {
127
+ const template = getEnvExampleTemplate({ framework: 'fastmcp' });
128
+ expect(template).not.toContain('ALLOWED_HOSTS');
129
+ });
130
+ it('should NOT include PORT for stdio transport', () => {
131
+ const template = getEnvExampleTemplate({ transport: 'stdio' });
132
+ expect(template).not.toContain('PORT=');
133
+ });
134
+ it('should NOT include ALLOWED_HOSTS for stdio transport', () => {
135
+ const template = getEnvExampleTemplate({ transport: 'stdio' });
136
+ expect(template).not.toContain('ALLOWED_HOSTS');
137
+ });
92
138
  });
93
139
  });
@@ -1,5 +1,6 @@
1
1
  export type PackageManager = 'npm' | 'pnpm' | 'yarn';
2
2
  export type Framework = 'sdk' | 'fastmcp';
3
+ export type TransportType = 'http' | 'stdio';
3
4
  /**
4
5
  * Base template options shared across all templates
5
6
  */
@@ -11,12 +12,14 @@ export interface BaseTemplateOptions {
11
12
  */
12
13
  export interface SdkTemplateOptions extends BaseTemplateOptions {
13
14
  withOAuth?: boolean;
15
+ transport?: TransportType;
14
16
  }
15
17
  /**
16
18
  * Template options for FastMCP templates
17
19
  */
18
20
  export interface FastMCPTemplateOptions extends BaseTemplateOptions {
19
21
  stateless?: boolean;
22
+ transport?: TransportType;
20
23
  }
21
24
  /**
22
25
  * Template options for common templates (package.json, env.example)
@@ -26,4 +29,5 @@ export interface CommonTemplateOptions extends BaseTemplateOptions {
26
29
  withOAuth?: boolean;
27
30
  framework?: Framework;
28
31
  stateless?: boolean;
32
+ transport?: TransportType;
29
33
  }
@@ -1,4 +1,13 @@
1
1
  export function getIndexTemplate(options) {
2
+ const transport = options?.transport ?? 'http';
3
+ if (transport === 'stdio') {
4
+ return `import { server } from './server.js';
5
+
6
+ server.start({
7
+ transportType: "stdio",
8
+ });
9
+ `;
10
+ }
2
11
  const stateless = options?.stateless ?? false;
3
12
  const statelessConfig = stateless ? '\n stateless: true,' : '';
4
13
  return `
@@ -1,6 +1,7 @@
1
1
  export function getReadmeTemplate(projectName, options) {
2
2
  const packageManager = options?.packageManager ?? 'npm';
3
3
  const stateless = options?.stateless ?? false;
4
+ const transport = options?.transport ?? 'http';
4
5
  const commands = {
5
6
  npm: {
6
7
  install: 'npm install',
@@ -8,6 +9,9 @@ export function getReadmeTemplate(projectName, options) {
8
9
  build: 'npm run build',
9
10
  start: 'npm start',
10
11
  inspect: 'npm run inspect',
12
+ inspectTools: 'npm run inspect:tools',
13
+ inspectPrompts: 'npm run inspect:prompts',
14
+ inspectResources: 'npm run inspect:resources',
11
15
  },
12
16
  pnpm: {
13
17
  install: 'pnpm install',
@@ -15,6 +19,9 @@ export function getReadmeTemplate(projectName, options) {
15
19
  build: 'pnpm build',
16
20
  start: 'pnpm start',
17
21
  inspect: 'pnpm inspect',
22
+ inspectTools: 'pnpm inspect:tools',
23
+ inspectPrompts: 'pnpm inspect:prompts',
24
+ inspectResources: 'pnpm inspect:resources',
18
25
  },
19
26
  yarn: {
20
27
  install: 'yarn',
@@ -22,9 +29,101 @@ export function getReadmeTemplate(projectName, options) {
22
29
  build: 'yarn build',
23
30
  start: 'yarn start',
24
31
  inspect: 'yarn inspect',
32
+ inspectTools: 'yarn inspect:tools',
33
+ inspectPrompts: 'yarn inspect:prompts',
34
+ inspectResources: 'yarn inspect:resources',
25
35
  },
26
36
  };
27
37
  const cmd = commands[packageManager];
38
+ if (transport === 'stdio') {
39
+ return `# ${projectName}
40
+
41
+ A stdio MCP server built with FastMCP.
42
+
43
+ ## About
44
+
45
+ This project was created with [@agentailor/create-mcp-server](https://www.npmjs.com/package/@agentailor/create-mcp-server) using [FastMCP](https://github.com/punkpeye/fastmcp).
46
+
47
+ ## Getting Started
48
+
49
+ \`\`\`bash
50
+ # Install dependencies
51
+ ${cmd.install}
52
+
53
+ # Build and run
54
+ ${cmd.dev}
55
+
56
+ # Or build and start separately
57
+ ${cmd.build}
58
+ ${cmd.start}
59
+ \`\`\`
60
+
61
+ ## Testing with MCP Inspector
62
+
63
+ This project includes [MCP Inspector](https://github.com/modelcontextprotocol/inspector) as a dev dependency. Build the project first (\`${cmd.build}\`), then use the inspect scripts:
64
+
65
+ \`\`\`bash
66
+ # List tools
67
+ ${cmd.inspectTools}
68
+
69
+ # List prompts
70
+ ${cmd.inspectPrompts}
71
+
72
+ # List resources
73
+ ${cmd.inspectResources}
74
+ \`\`\`
75
+
76
+ You can also call tools directly:
77
+
78
+ \`\`\`bash
79
+ # Call a tool
80
+ npx @modelcontextprotocol/inspector --cli node dist/index.js --method tools/call --tool-name start-notification-stream --tool-arg interval=100 --tool-arg count=5
81
+
82
+ # Call a tool with JSON arguments
83
+ npx @modelcontextprotocol/inspector --cli node dist/index.js --method tools/call --tool-name start-notification-stream --tool-arg 'options={"interval": 100, "count": 5}'
84
+ \`\`\`
85
+
86
+ ## Included Examples
87
+
88
+ This server comes with example implementations to help you get started:
89
+
90
+ ### Prompts
91
+
92
+ - **greeting-template** - A simple greeting prompt that takes a name parameter
93
+
94
+ ### Tools
95
+
96
+ - **start-notification-stream** - Sends periodic notifications for testing. Parameters:
97
+ - \`interval\`: Milliseconds between notifications (default: 100)
98
+ - \`count\`: Number of notifications to send (default: 10, use 0 for unlimited)
99
+
100
+ ### Resources
101
+
102
+ - **greeting-resource** - A simple text resource at \`https://example.com/greetings/default\`
103
+
104
+ ## Project Structure
105
+
106
+ \`\`\`
107
+ ${projectName}/
108
+ ├── src/
109
+ │ ├── server.ts # FastMCP server definition (tools, prompts, resources)
110
+ │ └── index.ts # Server startup configuration
111
+ ├── package.json
112
+ ├── tsconfig.json
113
+ └── README.md
114
+ \`\`\`
115
+
116
+ ## Customization
117
+
118
+ - Add new tools, prompts, and resources in \`src/server.ts\`
119
+ - Modify transport configuration in \`src/index.ts\`
120
+
121
+ ## Learn More
122
+
123
+ - [FastMCP](https://github.com/punkpeye/fastmcp) - The framework powering this server
124
+ - [Model Context Protocol](https://modelcontextprotocol.io/)
125
+ `;
126
+ }
28
127
  const modeDescription = stateless
29
128
  ? 'A stateless streamable HTTP MCP server built with FastMCP.'
30
129
  : 'A stateful streamable HTTP MCP server built with FastMCP.';
@@ -54,6 +54,12 @@ describe('fastmcp templates', () => {
54
54
  const template = getIndexTemplate({ stateless: true });
55
55
  expect(template).toContain('stateless: true');
56
56
  });
57
+ it('should use stdio transportType when transport is stdio', () => {
58
+ const template = getIndexTemplate({ transport: 'stdio' });
59
+ expect(template).toContain('transportType: "stdio"');
60
+ expect(template).not.toContain('httpStream');
61
+ expect(template).not.toContain('PORT');
62
+ });
57
63
  });
58
64
  describe('getReadmeTemplate', () => {
59
65
  it('should include project name', () => {
@@ -83,5 +89,21 @@ describe('fastmcp templates', () => {
83
89
  const template = getReadmeTemplate(projectName, { stateless: true });
84
90
  expect(template).toContain('stateless');
85
91
  });
92
+ it('should NOT include HTTP endpoint section for stdio', () => {
93
+ const template = getReadmeTemplate(projectName, { transport: 'stdio' });
94
+ expect(template).not.toContain('POST /mcp');
95
+ expect(template).not.toContain('GET /health');
96
+ });
97
+ it('should include MCP Inspector CLI commands for stdio', () => {
98
+ const template = getReadmeTemplate(projectName, { transport: 'stdio' });
99
+ expect(template).toContain('inspect:tools');
100
+ expect(template).toContain('inspect:prompts');
101
+ expect(template).toContain('inspect:resources');
102
+ });
103
+ it('should NOT include Docker section for stdio', () => {
104
+ const template = getReadmeTemplate(projectName, { transport: 'stdio' });
105
+ expect(template).not.toContain('docker build');
106
+ expect(template).not.toContain('Dockerfile');
107
+ });
86
108
  });
87
109
  });
@@ -14,13 +14,18 @@ import {
14
14
  getOAuthMetadataUrl,
15
15
  validateOAuthConfig,
16
16
  } from './auth.js';`
17
- : `import { type Request, type Response } from 'express';
17
+ : `import 'dotenv/config';
18
+ import { type Request, type Response } from 'express';
18
19
  import { randomUUID } from 'node:crypto';
19
20
  import { createMcpExpressApp } from '@modelcontextprotocol/sdk/server/express.js';
20
21
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
21
22
  import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
22
23
  import { getServer } from './server.js';`;
23
- const appSetup = `const app = createMcpExpressApp();
24
+ const appSetup = `const allowedHosts = process.env.ALLOWED_HOSTS?.split(',') ?? [];
25
+
26
+ const app = createMcpExpressApp({
27
+ allowedHosts: ['localhost', '127.0.0.1', '[::1]', ...allowedHosts],
28
+ });
24
29
 
25
30
  // Health check endpoint for container orchestration
26
31
  app.get('/health', (_, res) => res.sendStatus(200));`;
@@ -133,7 +133,7 @@ ${commands.build}
133
133
  ${commands.start}
134
134
  \`\`\`
135
135
 
136
- The server will start on port 3000 by default. You can change this by setting the \`PORT\` environment variable.
136
+ The server will start on port 3000 by default. You can change this by setting the \`PORT\` environment variable. To allow additional hosts (e.g. when deploying behind a reverse proxy), set \`ALLOWED_HOSTS\` as a comma-separated list.
137
137
 
138
138
  ## Testing with MCP Inspector
139
139
 
@@ -36,7 +36,7 @@ describe('sdk/stateful templates', () => {
36
36
  it('should use createMcpExpressApp', () => {
37
37
  const template = getIndexTemplate();
38
38
  expect(template).toContain('createMcpExpressApp');
39
- expect(template).toContain('const app = createMcpExpressApp()');
39
+ expect(template).toContain('const app = createMcpExpressApp({');
40
40
  });
41
41
  it('should configure /mcp endpoint', () => {
42
42
  const template = getIndexTemplate();
@@ -46,7 +46,12 @@ describe('sdk/stateful templates', () => {
46
46
  });
47
47
  it('should use PORT from environment variable', () => {
48
48
  const template = getIndexTemplate();
49
- expect(template).toContain('process.env.PORT || 3000');
49
+ expect(template).toContain('process.env.PORT');
50
+ });
51
+ it('should pass allowedHosts to createMcpExpressApp', () => {
52
+ const template = getIndexTemplate();
53
+ expect(template).toContain('allowedHosts');
54
+ expect(template).toContain("ALLOWED_HOSTS?.split(',')");
50
55
  });
51
56
  // Stateful-specific tests
52
57
  it('should use session ID header', () => {
@@ -7,7 +7,11 @@ import { createMcpExpressApp } from '@modelcontextprotocol/sdk/server/express.js
7
7
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
8
8
  import { getServer } from './server.js';
9
9
 
10
- const app = createMcpExpressApp();
10
+ const allowedHosts = process.env.ALLOWED_HOSTS?.split(',') ?? [];
11
+
12
+ const app = createMcpExpressApp({
13
+ allowedHosts: ['localhost', '127.0.0.1', '[::1]', ...allowedHosts],
14
+ });
11
15
 
12
16
  // Health check endpoint for container orchestration
13
17
  app.get('/health', (_, res) => res.sendStatus(200));
@@ -45,7 +45,7 @@ ${commands.build}
45
45
  ${commands.start}
46
46
  \`\`\`
47
47
 
48
- The server will start on port 3000 by default. You can change this by setting the \`PORT\` environment variable.
48
+ The server will start on port 3000 by default. You can change this by setting the \`PORT\` environment variable. To allow additional hosts (e.g. when deploying behind a reverse proxy), set \`ALLOWED_HOSTS\` as a comma-separated list.
49
49
 
50
50
  ## Testing with MCP Inspector
51
51
 
@@ -36,7 +36,7 @@ describe('sdk/stateless templates', () => {
36
36
  it('should use createMcpExpressApp', () => {
37
37
  const template = getIndexTemplate();
38
38
  expect(template).toContain('createMcpExpressApp');
39
- expect(template).toContain('const app = createMcpExpressApp()');
39
+ expect(template).toContain('const app = createMcpExpressApp({');
40
40
  });
41
41
  it('should configure /mcp endpoint', () => {
42
42
  const template = getIndexTemplate();
@@ -46,7 +46,12 @@ describe('sdk/stateless templates', () => {
46
46
  });
47
47
  it('should use PORT from environment variable', () => {
48
48
  const template = getIndexTemplate();
49
- expect(template).toContain('process.env.PORT || 3000');
49
+ expect(template).toContain('process.env.PORT');
50
+ });
51
+ it('should pass allowedHosts to createMcpExpressApp', () => {
52
+ const template = getIndexTemplate();
53
+ expect(template).toContain('allowedHosts');
54
+ expect(template).toContain("ALLOWED_HOSTS?.split(',')");
50
55
  });
51
56
  // Stateless-specific tests
52
57
  it('should use undefined sessionIdGenerator for stateless mode', () => {
@@ -0,0 +1,5 @@
1
+ export type { SdkTemplateOptions as TemplateOptions } from '../../common/types.js';
2
+ import type { SdkTemplateOptions } from '../../common/types.js';
3
+ export declare function getIndexTemplate(_options?: SdkTemplateOptions): string;
4
+ export { getServerTemplate } from './server.js';
5
+ export { getReadmeTemplate } from './readme.js';
@@ -0,0 +1,21 @@
1
+ // Options parameter kept for type consistency with other SDK templates
2
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
3
+ export function getIndexTemplate(_options) {
4
+ return `import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+ import { getServer } from './server.js';
6
+
7
+ async function main() {
8
+ const server = getServer();
9
+ const transport = new StdioServerTransport();
10
+ await server.connect(transport);
11
+ console.error("MCP Server running on stdio");
12
+ }
13
+
14
+ main().catch((error) => {
15
+ console.error("Fatal error in main():", error);
16
+ process.exit(1);
17
+ });
18
+ `;
19
+ }
20
+ export { getServerTemplate } from './server.js';
21
+ export { getReadmeTemplate } from './readme.js';
@@ -0,0 +1,2 @@
1
+ import type { TemplateOptions } from './index.js';
2
+ export declare function getReadmeTemplate(projectName: string, options?: TemplateOptions): string;
@@ -0,0 +1,119 @@
1
+ export function getReadmeTemplate(projectName, options) {
2
+ const packageManager = options?.packageManager ?? 'npm';
3
+ const commands = {
4
+ npm: {
5
+ install: 'npm install',
6
+ dev: 'npm run dev',
7
+ build: 'npm run build',
8
+ start: 'npm start',
9
+ inspectTools: 'npm run inspect:tools',
10
+ inspectPrompts: 'npm run inspect:prompts',
11
+ inspectResources: 'npm run inspect:resources',
12
+ },
13
+ pnpm: {
14
+ install: 'pnpm install',
15
+ dev: 'pnpm dev',
16
+ build: 'pnpm build',
17
+ start: 'pnpm start',
18
+ inspectTools: 'pnpm inspect:tools',
19
+ inspectPrompts: 'pnpm inspect:prompts',
20
+ inspectResources: 'pnpm inspect:resources',
21
+ },
22
+ yarn: {
23
+ install: 'yarn',
24
+ dev: 'yarn dev',
25
+ build: 'yarn build',
26
+ start: 'yarn start',
27
+ inspectTools: 'yarn inspect:tools',
28
+ inspectPrompts: 'yarn inspect:prompts',
29
+ inspectResources: 'yarn inspect:resources',
30
+ },
31
+ }[packageManager];
32
+ return `# ${projectName}
33
+
34
+ A stdio MCP (Model Context Protocol) server using the official MCP SDK.
35
+
36
+ ## About
37
+
38
+ This project was created with [@agentailor/create-mcp-server](https://www.npmjs.com/package/@agentailor/create-mcp-server).
39
+
40
+ ## Getting Started
41
+
42
+ \`\`\`bash
43
+ # Install dependencies
44
+ ${commands.install}
45
+
46
+ # Build and run
47
+ ${commands.dev}
48
+
49
+ # Or build and start separately
50
+ ${commands.build}
51
+ ${commands.start}
52
+ \`\`\`
53
+
54
+ ## Testing with MCP Inspector
55
+
56
+ This project includes [MCP Inspector](https://github.com/modelcontextprotocol/inspector) as a dev dependency. Build the project first (\`${commands.build}\`), then use the inspect scripts:
57
+
58
+ \`\`\`bash
59
+ # List tools
60
+ ${commands.inspectTools}
61
+
62
+ # List prompts
63
+ ${commands.inspectPrompts}
64
+
65
+ # List resources
66
+ ${commands.inspectResources}
67
+ \`\`\`
68
+
69
+ You can also call tools directly:
70
+
71
+ \`\`\`bash
72
+ # Call a tool
73
+ npx @modelcontextprotocol/inspector --cli node dist/index.js --method tools/call --tool-name start-notification-stream --tool-arg interval=100 --tool-arg count=5
74
+
75
+ # Call a tool with JSON arguments
76
+ npx @modelcontextprotocol/inspector --cli node dist/index.js --method tools/call --tool-name start-notification-stream --tool-arg 'options={"interval": 100, "count": 5}'
77
+ \`\`\`
78
+
79
+ ## Included Examples
80
+
81
+ This server comes with example implementations to help you get started:
82
+
83
+ ### Prompts
84
+
85
+ - **greeting-template** - A simple greeting prompt that takes a name parameter
86
+
87
+ ### Tools
88
+
89
+ - **start-notification-stream** - Sends periodic notifications for testing. Parameters:
90
+ - \`interval\`: Milliseconds between notifications (default: 100)
91
+ - \`count\`: Number of notifications to send (default: 10, use 0 for unlimited)
92
+
93
+ ### Resources
94
+
95
+ - **greeting-resource** - A simple text resource at \`https://example.com/greetings/default\`
96
+
97
+ ## Project Structure
98
+
99
+ \`\`\`
100
+ ${projectName}/
101
+ ├── src/
102
+ │ ├── server.ts # MCP server definition (tools, prompts, resources)
103
+ │ └── index.ts # stdio transport startup
104
+ ├── package.json
105
+ ├── tsconfig.json
106
+ └── README.md
107
+ \`\`\`
108
+
109
+ ## Customization
110
+
111
+ - Add new tools, prompts, and resources in \`src/server.ts\`
112
+ - Modify transport configuration in \`src/index.ts\`
113
+
114
+ ## Learn More
115
+
116
+ - [Model Context Protocol](https://modelcontextprotocol.io/)
117
+ - [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)
118
+ `;
119
+ }
@@ -0,0 +1 @@
1
+ export { getServerTemplate } from '../stateless/server.js';
@@ -0,0 +1,2 @@
1
+ // Re-export from stateless template - server logic is identical for stdio
2
+ export { getServerTemplate } from '../stateless/server.js';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,111 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { getServerTemplate, getIndexTemplate, getReadmeTemplate } from './index.js';
3
+ describe('sdk/stdio templates', () => {
4
+ const projectName = 'test-project';
5
+ describe('getServerTemplate', () => {
6
+ it('should include project name in server config', () => {
7
+ const template = getServerTemplate(projectName);
8
+ expect(template).toContain(`name: '${projectName}'`);
9
+ });
10
+ it('should use correct SDK imports', () => {
11
+ const template = getServerTemplate(projectName);
12
+ expect(template).toContain("from '@modelcontextprotocol/sdk/types.js'");
13
+ expect(template).toContain("from '@modelcontextprotocol/sdk/server/mcp.js'");
14
+ });
15
+ it('should include example prompt', () => {
16
+ const template = getServerTemplate(projectName);
17
+ expect(template).toContain('greeting-template');
18
+ expect(template).toContain('registerPrompt');
19
+ });
20
+ it('should include example tool', () => {
21
+ const template = getServerTemplate(projectName);
22
+ expect(template).toContain('start-notification-stream');
23
+ expect(template).toContain('registerTool');
24
+ });
25
+ it('should include example resource', () => {
26
+ const template = getServerTemplate(projectName);
27
+ expect(template).toContain('greeting-resource');
28
+ expect(template).toContain('registerResource');
29
+ });
30
+ });
31
+ describe('getIndexTemplate', () => {
32
+ it('should import StdioServerTransport', () => {
33
+ const template = getIndexTemplate();
34
+ expect(template).toContain('StdioServerTransport');
35
+ expect(template).toContain('@modelcontextprotocol/sdk/server/stdio.js');
36
+ });
37
+ it('should NOT import express or createMcpExpressApp', () => {
38
+ const template = getIndexTemplate();
39
+ expect(template).not.toContain('express');
40
+ expect(template).not.toContain('createMcpExpressApp');
41
+ });
42
+ it('should NOT reference PORT', () => {
43
+ const template = getIndexTemplate();
44
+ expect(template).not.toContain('PORT');
45
+ expect(template).not.toContain('process.env.PORT');
46
+ });
47
+ it('should connect server to StdioServerTransport', () => {
48
+ const template = getIndexTemplate();
49
+ expect(template).toContain('new StdioServerTransport()');
50
+ expect(template).toContain('server.connect(transport)');
51
+ });
52
+ it('should log to stderr, not stdout', () => {
53
+ const template = getIndexTemplate();
54
+ expect(template).toContain('console.error("MCP Server running on stdio")');
55
+ });
56
+ it('should wrap startup in async main with error handling', () => {
57
+ const template = getIndexTemplate();
58
+ expect(template).toContain('async function main()');
59
+ expect(template).toContain('main().catch');
60
+ expect(template).toContain('process.exit(1)');
61
+ });
62
+ });
63
+ describe('getReadmeTemplate', () => {
64
+ it('should include project name', () => {
65
+ const template = getReadmeTemplate(projectName);
66
+ expect(template).toContain(`# ${projectName}`);
67
+ });
68
+ it('should describe stdio transport', () => {
69
+ const template = getReadmeTemplate(projectName);
70
+ expect(template).toContain('stdio');
71
+ });
72
+ it('should include MCP Inspector CLI commands', () => {
73
+ const template = getReadmeTemplate(projectName);
74
+ expect(template).toContain('inspect:tools');
75
+ expect(template).toContain('inspect:prompts');
76
+ expect(template).toContain('inspect:resources');
77
+ });
78
+ it('should NOT mention HTTP API endpoints', () => {
79
+ const template = getReadmeTemplate(projectName);
80
+ expect(template).not.toContain('POST /mcp');
81
+ expect(template).not.toContain('GET /health');
82
+ });
83
+ it('should NOT mention PORT or ALLOWED_HOSTS', () => {
84
+ const template = getReadmeTemplate(projectName);
85
+ expect(template).not.toContain('PORT');
86
+ expect(template).not.toContain('ALLOWED_HOSTS');
87
+ });
88
+ it('should NOT include Docker deployment section', () => {
89
+ const template = getReadmeTemplate(projectName);
90
+ expect(template).not.toContain('docker build');
91
+ expect(template).not.toContain('Dockerfile');
92
+ });
93
+ it('should use npm commands by default', () => {
94
+ const template = getReadmeTemplate(projectName);
95
+ expect(template).toContain('npm install');
96
+ expect(template).toContain('npm run dev');
97
+ });
98
+ it('should use pnpm commands when specified', () => {
99
+ const template = getReadmeTemplate(projectName, { packageManager: 'pnpm' });
100
+ expect(template).toContain('pnpm install');
101
+ expect(template).toContain('pnpm dev');
102
+ expect(template).not.toContain('npm run');
103
+ });
104
+ it('should use yarn commands when specified', () => {
105
+ const template = getReadmeTemplate(projectName, { packageManager: 'yarn' });
106
+ expect(template).toContain('yarn\n');
107
+ expect(template).toContain('yarn dev');
108
+ expect(template).not.toContain('npm run');
109
+ });
110
+ });
111
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentailor/create-mcp-server",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "Create a new MCP (Model Context Protocol) server project",
5
5
  "type": "module",
6
6
  "bin": {