Skip to content

Commit 772213b

Browse files
authored
fix package manager generated in readme and add fallback port (#10)
* fix package manager generated in readme and add fallback port * bump version to 0.2.1
1 parent 6a9cdf1 commit 772213b

File tree

10 files changed

+148
-32
lines changed

10 files changed

+148
-32
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@agentailor/create-mcp-server",
3-
"version": "0.2.0",
3+
"version": "0.2.1",
44
"description": "Create a new MCP (Model Context Protocol) server project",
55
"type": "module",
66
"bin": {

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ async function main() {
148148
);
149149
const withGitInit = gitInitResponse.withGitInit ?? false;
150150

151-
const templateOptions: TemplateOptions = { withOAuth };
151+
const templateOptions: TemplateOptions = { withOAuth, packageManager };
152152
const templates = templateFunctions[templateType];
153153

154154
const projectPath = join(process.cwd(), projectName);

src/templates/common/env.example.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export interface TemplateOptions {
22
withOAuth?: boolean;
3+
packageManager?: 'npm' | 'pnpm' | 'yarn';
34
}
45

56
export function getEnvExampleTemplate(options?: TemplateOptions): string {

src/templates/common/package.json.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export interface TemplateOptions {
22
withOAuth?: boolean;
3+
packageManager?: 'npm' | 'pnpm' | 'yarn';
34
}
45

56
export function getPackageJsonTemplate(projectName: string, options?: TemplateOptions): string {

src/templates/stateful-streamable-http/index.ts

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export interface TemplateOptions {
22
withOAuth?: boolean;
3+
packageManager?: 'npm' | 'pnpm' | 'yarn';
34
}
45

56
export function getIndexTemplate(options?: TemplateOptions): string {
@@ -39,11 +40,6 @@ import { getServer } from './server.js';`;
3940
? `app.delete('/mcp', authMiddleware, async (req: Request, res: Response) => {`
4041
: `app.delete('/mcp', async (req: Request, res: Response) => {`;
4142

42-
const startupLog = withOAuth
43-
? `console.log(\`MCP Stateful HTTP Server listening on port \${PORT}\`);
44-
console.log(\`OAuth metadata available at \${getOAuthMetadataUrl()}\`);`
45-
: `console.log(\`MCP Stateful HTTP Server listening on port \${PORT}\`);`;
46-
4743
return `${imports}
4844
4945
${appSetup}
@@ -156,16 +152,32 @@ ${
156152
? `// Start the server
157153
const PORT = process.env.PORT || 3000;
158154
155+
function startServer(port: number | string): void {
156+
const server = app.listen(port, () => {
157+
console.log(\`MCP Stateful HTTP Server listening on port \${port}\`);
158+
console.log(\`OAuth metadata available at \${getOAuthMetadataUrl()}\`);
159+
});
160+
161+
server.on('error', (error: NodeJS.ErrnoException) => {
162+
if (error.code === 'EADDRINUSE') {
163+
const randomPort = Math.floor(Math.random() * (65535 - 49152) + 49152);
164+
console.log(\`Port \${port} is in use, trying port \${randomPort}...\`);
165+
startServer(randomPort);
166+
} else {
167+
console.error('Failed to start server:', error);
168+
process.exit(1);
169+
}
170+
});
171+
}
172+
159173
async function main() {
160174
// Validate OAuth configuration and fetch OIDC discovery document
161175
await validateOAuthConfig();
162176
163177
// Setup OAuth metadata routes (must be after validateOAuthConfig)
164178
setupAuthMetadataRouter(app);
165179
166-
app.listen(PORT, () => {
167-
${startupLog}
168-
});
180+
startServer(PORT);
169181
}
170182
171183
main().catch((error) => {
@@ -174,9 +186,25 @@ main().catch((error) => {
174186
});`
175187
: `// Start the server
176188
const PORT = process.env.PORT || 3000;
177-
app.listen(PORT, () => {
178-
${startupLog}
179-
});`
189+
190+
function startServer(port: number | string): void {
191+
const server = app.listen(port, () => {
192+
console.log(\`MCP Stateful HTTP Server listening on port \${port}\`);
193+
});
194+
195+
server.on('error', (error: NodeJS.ErrnoException) => {
196+
if (error.code === 'EADDRINUSE') {
197+
const randomPort = Math.floor(Math.random() * (65535 - 49152) + 49152);
198+
console.log(\`Port \${port} is in use, trying port \${randomPort}...\`);
199+
startServer(randomPort);
200+
} else {
201+
console.error('Failed to start server:', error);
202+
process.exit(1);
203+
}
204+
});
205+
}
206+
207+
startServer(PORT);`
180208
}
181209
182210
// Handle server shutdown

src/templates/stateful-streamable-http/readme.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@ import type { TemplateOptions } from './index.js';
22

33
export function getReadmeTemplate(projectName: string, options?: TemplateOptions): string {
44
const withOAuth = options?.withOAuth ?? false;
5+
const packageManager = options?.packageManager ?? 'npm';
6+
7+
const commands = {
8+
npm: { install: 'npm install', dev: 'npm run dev', build: 'npm run build', start: 'npm start' },
9+
pnpm: { install: 'pnpm install', dev: 'pnpm dev', build: 'pnpm build', start: 'pnpm start' },
10+
yarn: { install: 'yarn', dev: 'yarn dev', build: 'yarn build', start: 'yarn start' },
11+
}[packageManager];
512

613
const description = withOAuth
714
? 'A stateful streamable HTTP MCP (Model Context Protocol) server with session management and OAuth authentication.'
@@ -93,14 +100,14 @@ This project was created with [@agentailor/create-mcp-server](https://www.npmjs.
93100
94101
\`\`\`bash
95102
# Install dependencies
96-
npm install
103+
${commands.install}
97104
98105
# Build and run in development
99-
npm run dev
106+
${commands.dev}
100107
101108
# Or build and start separately
102-
npm run build
103-
npm start
109+
${commands.build}
110+
${commands.start}
104111
\`\`\`
105112
106113
The server will start on port 3000 by default. You can change this by setting the \`PORT\` environment variable.

src/templates/stateful-streamable-http/templates.test.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,12 @@ describe('stateful-streamable-http templates', () => {
101101
expect(template).toContain(`# ${projectName}`);
102102
});
103103

104-
it('should include getting started instructions', () => {
104+
it('should include getting started instructions with npm by default', () => {
105105
const template = getReadmeTemplate(projectName);
106106
expect(template).toContain('npm install');
107107
expect(template).toContain('npm run dev');
108+
expect(template).toContain('npm run build');
109+
expect(template).toContain('npm start');
108110
});
109111

110112
it('should document the /mcp endpoint', () => {
@@ -131,6 +133,34 @@ describe('stateful-streamable-http templates', () => {
131133
});
132134
});
133135

136+
describe('getReadmeTemplate with package manager', () => {
137+
it('should use npm commands when packageManager is npm', () => {
138+
const template = getReadmeTemplate(projectName, { packageManager: 'npm' });
139+
expect(template).toContain('npm install');
140+
expect(template).toContain('npm run dev');
141+
expect(template).toContain('npm run build');
142+
expect(template).toContain('npm start');
143+
});
144+
145+
it('should use pnpm commands when packageManager is pnpm', () => {
146+
const template = getReadmeTemplate(projectName, { packageManager: 'pnpm' });
147+
expect(template).toContain('pnpm install');
148+
expect(template).toContain('pnpm dev');
149+
expect(template).toContain('pnpm build');
150+
expect(template).toContain('pnpm start');
151+
expect(template).not.toContain('npm run');
152+
});
153+
154+
it('should use yarn commands when packageManager is yarn', () => {
155+
const template = getReadmeTemplate(projectName, { packageManager: 'yarn' });
156+
expect(template).toContain('yarn\n');
157+
expect(template).toContain('yarn dev');
158+
expect(template).toContain('yarn build');
159+
expect(template).toContain('yarn start');
160+
expect(template).not.toContain('npm run');
161+
});
162+
});
163+
134164
describe('getIndexTemplate with OAuth', () => {
135165
it('should include auth imports when OAuth enabled', () => {
136166
const template = getIndexTemplate({ withOAuth: true });

src/templates/streamable-http/index.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export interface TemplateOptions {
22
withOAuth?: boolean;
3+
packageManager?: 'npm' | 'pnpm' | 'yarn';
34
}
45

56
// Options parameter added for type consistency with stateful template (OAuth not supported in stateless)
@@ -70,13 +71,25 @@ app.delete('/mcp', async (req: Request, res: Response) => {
7071
7172
// Start the server
7273
const PORT = process.env.PORT || 3000;
73-
app.listen(PORT, (error) => {
74-
if (error) {
75-
console.error('Failed to start server:', error);
76-
process.exit(1);
77-
}
78-
console.log(\`MCP Streamable HTTP Server listening on port \${PORT}\`);
79-
});
74+
75+
function startServer(port: number | string): void {
76+
const server = app.listen(port, () => {
77+
console.log(\`MCP Streamable HTTP Server listening on port \${port}\`);
78+
});
79+
80+
server.on('error', (error: NodeJS.ErrnoException) => {
81+
if (error.code === 'EADDRINUSE') {
82+
const randomPort = Math.floor(Math.random() * (65535 - 49152) + 49152);
83+
console.log(\`Port \${port} is in use, trying port \${randomPort}...\`);
84+
startServer(randomPort);
85+
} else {
86+
console.error('Failed to start server:', error);
87+
process.exit(1);
88+
}
89+
});
90+
}
91+
92+
startServer(PORT);
8093
8194
// Handle server shutdown
8295
process.on('SIGINT', async () => {

src/templates/streamable-http/readme.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
import type { TemplateOptions } from './index.js';
22

3-
// Options parameter added for type consistency with stateful template (OAuth not supported in stateless)
4-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
53
export function getReadmeTemplate(projectName: string, options?: TemplateOptions): string {
4+
const packageManager = options?.packageManager ?? 'npm';
5+
6+
const commands = {
7+
npm: { install: 'npm install', dev: 'npm run dev', build: 'npm run build', start: 'npm start' },
8+
pnpm: { install: 'pnpm install', dev: 'pnpm dev', build: 'pnpm build', start: 'pnpm start' },
9+
yarn: { install: 'yarn', dev: 'yarn dev', build: 'yarn build', start: 'yarn start' },
10+
}[packageManager];
11+
612
return `# ${projectName}
713
814
A stateless streamable HTTP MCP (Model Context Protocol) server.
@@ -15,14 +21,14 @@ This project was created with [@agentailor/create-mcp-server](https://www.npmjs.
1521
1622
\`\`\`bash
1723
# Install dependencies
18-
npm install
24+
${commands.install}
1925
2026
# Build and run in development
21-
npm run dev
27+
${commands.dev}
2228
2329
# Or build and start separately
24-
npm run build
25-
npm start
30+
${commands.build}
31+
${commands.start}
2632
\`\`\`
2733
2834
The server will start on port 3000 by default. You can change this by setting the \`PORT\` environment variable.

src/templates/streamable-http/templates.test.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,12 @@ describe('streamable-http templates', () => {
7777
expect(template).toContain(`# ${projectName}`);
7878
});
7979

80-
it('should include getting started instructions', () => {
80+
it('should include getting started instructions with npm by default', () => {
8181
const template = getReadmeTemplate(projectName);
8282
expect(template).toContain('npm install');
8383
expect(template).toContain('npm run dev');
84+
expect(template).toContain('npm run build');
85+
expect(template).toContain('npm start');
8486
});
8587

8688
it('should document the /mcp endpoint', () => {
@@ -93,4 +95,32 @@ describe('streamable-http templates', () => {
9395
expect(template).toContain('stateless');
9496
});
9597
});
98+
99+
describe('getReadmeTemplate with package manager', () => {
100+
it('should use npm commands when packageManager is npm', () => {
101+
const template = getReadmeTemplate(projectName, { packageManager: 'npm' });
102+
expect(template).toContain('npm install');
103+
expect(template).toContain('npm run dev');
104+
expect(template).toContain('npm run build');
105+
expect(template).toContain('npm start');
106+
});
107+
108+
it('should use pnpm commands when packageManager is pnpm', () => {
109+
const template = getReadmeTemplate(projectName, { packageManager: 'pnpm' });
110+
expect(template).toContain('pnpm install');
111+
expect(template).toContain('pnpm dev');
112+
expect(template).toContain('pnpm build');
113+
expect(template).toContain('pnpm start');
114+
expect(template).not.toContain('npm run');
115+
});
116+
117+
it('should use yarn commands when packageManager is yarn', () => {
118+
const template = getReadmeTemplate(projectName, { packageManager: 'yarn' });
119+
expect(template).toContain('yarn\n');
120+
expect(template).toContain('yarn dev');
121+
expect(template).toContain('yarn build');
122+
expect(template).toContain('yarn start');
123+
expect(template).not.toContain('npm run');
124+
});
125+
});
96126
});

0 commit comments

Comments
 (0)