@@ -71,6 +71,11 @@ function parseArgs() {
7171 skipTests : false ,
7272 dryRun : false ,
7373 help : false ,
74+ skipBump : false ,
75+ skipBuild : false ,
76+ skipMcpb : false ,
77+ skipGit : false ,
78+ skipNpm : false ,
7479 } ;
7580
7681 for ( const arg of args ) {
@@ -84,6 +89,29 @@ function parseArgs() {
8489 case '--skip-tests' :
8590 options . skipTests = true ;
8691 break ;
92+ case '--skip-bump' :
93+ options . skipBump = true ;
94+ break ;
95+ case '--skip-build' :
96+ options . skipBuild = true ;
97+ break ;
98+ case '--skip-mcpb' :
99+ options . skipMcpb = true ;
100+ break ;
101+ case '--skip-git' :
102+ options . skipGit = true ;
103+ break ;
104+ case '--skip-npm' :
105+ options . skipNpm = true ;
106+ break ;
107+ case '--mcp-only' :
108+ // Skip everything except MCP Registry publish
109+ options . skipBump = true ;
110+ options . skipBuild = true ;
111+ options . skipMcpb = true ;
112+ options . skipGit = true ;
113+ options . skipNpm = true ;
114+ break ;
87115 case '--dry-run' :
88116 options . dryRun = true ;
89117 break ;
@@ -109,6 +137,12 @@ function showHelp() {
109137 console . log ( ' --minor Bump minor version (default: patch)' ) ;
110138 console . log ( ' --major Bump major version (default: patch)' ) ;
111139 console . log ( ' --skip-tests Skip running tests' ) ;
140+ console . log ( ' --skip-bump Skip version bumping' ) ;
141+ console . log ( ' --skip-build Skip building (if tests also skipped)' ) ;
142+ console . log ( ' --skip-mcpb Skip building MCPB bundle' ) ;
143+ console . log ( ' --skip-git Skip git commit and tag' ) ;
144+ console . log ( ' --skip-npm Skip NPM publishing' ) ;
145+ console . log ( ' --mcp-only Only publish to MCP Registry (skip all other steps)' ) ;
112146 console . log ( ' --dry-run Simulate the release without publishing' ) ;
113147 console . log ( ' --help, -h Show this help message' ) ;
114148 console . log ( '' ) ;
@@ -117,6 +151,7 @@ function showHelp() {
117151 console . log ( ' node scripts/publish-release.cjs --minor # Minor release (0.2.16 -> 0.3.0)' ) ;
118152 console . log ( ' node scripts/publish-release.cjs --major # Major release (0.2.16 -> 1.0.0)' ) ;
119153 console . log ( ' node scripts/publish-release.cjs --dry-run # Test without publishing' ) ;
154+ console . log ( ' node scripts/publish-release.cjs --mcp-only # Only publish to MCP Registry' ) ;
120155}
121156
122157// Main release function
@@ -153,140 +188,169 @@ async function publishRelease() {
153188 }
154189
155190 try {
191+ let newVersion = currentVersion ;
192+
156193 // Step 1: Bump version
157- printStep ( 'Step 1/7: Bumping version...' ) ;
158- const bumpCommand = options . bumpType === 'minor' ? 'npm run bump:minor' :
159- options . bumpType === 'major ' ? 'npm run bump:major ' :
160- ' npm run bump' ;
161- exec ( bumpCommand ) ;
162-
163- const newPackageJson = JSON . parse ( fs . readFileSync ( packageJsonPath , 'utf8' ) ) ;
164- const newVersion = newPackageJson . version ;
165- printSuccess ( `Version bumped: ${ currentVersion } → ${ newVersion } ` ) ;
166- console . log ( '' ) ;
167-
168- // Step 2: Build project
169- printStep ( 'Step 2/7: Building project... ' ) ;
170- exec ( 'npm run build ') ;
171- printSuccess ( 'Project built successfully' ) ;
194+ if ( ! options . skipBump ) {
195+ printStep ( 'Step 1/6: Bumping version...' ) ;
196+ const bumpCommand = options . bumpType === 'minor ' ? 'npm run bump:minor ' :
197+ options . bumpType === 'major' ? ' npm run bump:major' :
198+ 'npm run bump' ;
199+ exec ( bumpCommand ) ;
200+
201+ const newPackageJson = JSON . parse ( fs . readFileSync ( packageJsonPath , 'utf8' ) ) ;
202+ newVersion = newPackageJson . version ;
203+ printSuccess ( `Version bumped: ${ currentVersion } → ${ newVersion } ` ) ;
204+ console . log ( '' ) ;
205+ } else {
206+ printWarning ( 'Step 1/6: Version bump skipped ' ) ;
207+ console . log ( ' ') ;
208+ }
172209 console . log ( '' ) ;
173210
174- // Step 3 : Run tests (unless skipped)
175- if ( ! options . skipTests ) {
176- printStep ( 'Step 3/7 : Running tests...' ) ;
211+ // Step 2 : Run tests (unless skipped) - tests also build the project
212+ if ( ! options . skipTests && ! options . skipBuild ) {
213+ printStep ( 'Step 2/6 : Running tests (includes build) ...' ) ;
177214 exec ( 'npm test' ) ;
178215 printSuccess ( 'All tests passed' ) ;
216+ } else if ( ! options . skipBuild ) {
217+ printWarning ( 'Step 2/6: Tests skipped - building project...' ) ;
218+ exec ( 'npm run build' ) ;
219+ printSuccess ( 'Project built successfully' ) ;
179220 } else {
180- printWarning ( 'Step 3/7 : Tests skipped' ) ;
221+ printWarning ( 'Step 2/6 : Tests and build skipped' ) ;
181222 }
182223 console . log ( '' ) ;
183224
184- // Step 4: Build MCPB bundle
185- printStep ( 'Step 4/7: Building MCPB bundle...' ) ;
186- exec ( 'npm run build:mcpb' ) ;
187- printSuccess ( 'MCPB bundle created' ) ;
225+ // Step 3: Build MCPB bundle
226+ if ( ! options . skipMcpb ) {
227+ printStep ( 'Step 3/6: Building MCPB bundle...' ) ;
228+ exec ( 'npm run build:mcpb' ) ;
229+ printSuccess ( 'MCPB bundle created' ) ;
230+ } else {
231+ printWarning ( 'Step 3/6: MCPB bundle build skipped' ) ;
232+ }
188233 console . log ( '' ) ;
189234
190- // Step 5: Commit and tag
191- printStep ( 'Step 5/7: Creating git commit and tag...' ) ;
235+ // Step 4: Commit and tag
236+ if ( ! options . skipGit ) {
237+ printStep ( 'Step 4/6: Creating git commit and tag...' ) ;
192238
193- // Check if there are changes to commit
194- const gitStatus = execSilent ( 'git status --porcelain' , { ignoreError : true } ) ;
195- const hasChanges = gitStatus . includes ( 'package.json' ) ||
196- gitStatus . includes ( 'server.json' ) ||
197- gitStatus . includes ( 'src/version.ts' ) ;
198-
199- if ( ! hasChanges ) {
200- printWarning ( 'No changes to commit (version files already committed)' ) ;
201- } else {
202- exec ( 'git add package.json server.json src/version.ts' ) ;
203-
204- const commitMsg = `Release v${ newVersion }
239+ // Check if there are changes to commit
240+ const gitStatus = execSilent ( 'git status --porcelain' , { ignoreError : true } ) ;
241+ const hasChanges = gitStatus . includes ( 'package.json' ) ||
242+ gitStatus . includes ( 'server.json' ) ||
243+ gitStatus . includes ( 'src/version.ts' ) ;
244+
245+ if ( ! hasChanges ) {
246+ printWarning ( 'No changes to commit (version files already committed)' ) ;
247+ } else {
248+ exec ( 'git add package.json server.json src/version.ts' ) ;
249+
250+ const commitMsg = `Release v${ newVersion }
205251
206252Automated release commit with version bump from ${ currentVersion } to ${ newVersion } ` ;
207253
254+ if ( options . dryRun ) {
255+ printWarning ( `Would commit: ${ commitMsg . split ( '\n' ) [ 0 ] } ` ) ;
256+ } else {
257+ exec ( `git commit -m "${ commitMsg } "` ) ;
258+ printSuccess ( 'Changes committed' ) ;
259+ }
260+ }
261+
262+ // Create and push tag
263+ const tagName = `v${ newVersion } ` ;
264+
208265 if ( options . dryRun ) {
209- printWarning ( `Would commit: ${ commitMsg . split ( '\n' ) [ 0 ] } ` ) ;
266+ printWarning ( `Would create tag: ${ tagName } ` ) ;
267+ printWarning ( `Would push to origin: main and ${ tagName } ` ) ;
210268 } else {
211- exec ( `git commit -m "${ commitMsg } "` ) ;
212- printSuccess ( 'Changes committed' ) ;
269+ exec ( `git tag ${ tagName } ` ) ;
270+ exec ( 'git push origin main' ) ;
271+ exec ( `git push origin ${ tagName } ` ) ;
272+ printSuccess ( `Tag ${ tagName } created and pushed` ) ;
213273 }
214- }
215-
216- // Create and push tag
217- const tagName = `v${ newVersion } ` ;
218-
219- if ( options . dryRun ) {
220- printWarning ( `Would create tag: ${ tagName } ` ) ;
221- printWarning ( `Would push to origin: main and ${ tagName } ` ) ;
222274 } else {
223- exec ( `git tag ${ tagName } ` ) ;
224- exec ( 'git push origin main' ) ;
225- exec ( `git push origin ${ tagName } ` ) ;
226- printSuccess ( `Tag ${ tagName } created and pushed` ) ;
275+ printWarning ( 'Step 4/6: Git commit and tag skipped' ) ;
227276 }
228277 console . log ( '' ) ;
229278
230- // Step 6: Publish to NPM
231- printStep ( 'Step 6/7: Publishing to NPM...' ) ;
232-
233- // Check NPM authentication
234- const npmUser = execSilent ( 'npm whoami' , { ignoreError : true } ) . trim ( ) ;
235- if ( ! npmUser ) {
236- printError ( 'Not logged into NPM. Please run "npm login" first.' ) ;
237- process . exit ( 1 ) ;
238- }
239- printSuccess ( `NPM user: ${ npmUser } ` ) ;
240-
241- if ( options . dryRun ) {
242- printWarning ( 'Would publish to NPM: npm publish' ) ;
243- printWarning ( 'Skipping NPM publish (dry run)' ) ;
244- } else {
245- exec ( 'npm publish' ) ;
246- printSuccess ( 'Published to NPM' ) ;
279+ // Step 5: Publish to NPM
280+ if ( ! options . skipNpm ) {
281+ printStep ( 'Step 5/6: Publishing to NPM...' ) ;
247282
248- // Verify NPM publication
249- await new Promise ( resolve => setTimeout ( resolve , 3000 ) ) ; // Wait 3 seconds
250- const npmVersion = execSilent ( 'npm view @wonderwhy-er/desktop-commander version' , { ignoreError : true } ) . trim ( ) ;
251- if ( npmVersion === newVersion ) {
252- printSuccess ( `NPM publication verified: v${ npmVersion } ` ) ;
283+ // Check NPM authentication
284+ const npmUser = execSilent ( 'npm whoami' , { ignoreError : true } ) . trim ( ) ;
285+ if ( ! npmUser ) {
286+ printError ( 'Not logged into NPM. Please run "npm login" first.' ) ;
287+ process . exit ( 1 ) ;
288+ }
289+ printSuccess ( `NPM user: ${ npmUser } ` ) ;
290+
291+ if ( options . dryRun ) {
292+ printWarning ( 'Would publish to NPM: npm publish' ) ;
293+ printWarning ( 'Skipping NPM publish (dry run)' ) ;
253294 } else {
254- printWarning ( `NPM version mismatch: expected ${ newVersion } , got ${ npmVersion } (may take a moment to propagate)` ) ;
295+ exec ( 'npm publish' ) ;
296+ printSuccess ( 'Published to NPM' ) ;
297+
298+ // Verify NPM publication
299+ await new Promise ( resolve => setTimeout ( resolve , 3000 ) ) ; // Wait 3 seconds
300+ const npmVersion = execSilent ( 'npm view @wonderwhy-er/desktop-commander version' , { ignoreError : true } ) . trim ( ) ;
301+ if ( npmVersion === newVersion ) {
302+ printSuccess ( `NPM publication verified: v${ npmVersion } ` ) ;
303+ } else {
304+ printWarning ( `NPM version mismatch: expected ${ newVersion } , got ${ npmVersion } (may take a moment to propagate)` ) ;
305+ }
255306 }
307+ } else {
308+ printWarning ( 'Step 5/6: NPM publish skipped' ) ;
256309 }
257310 console . log ( '' ) ;
258311
259- // Step 7 : Publish to MCP Registry
260- printStep ( 'Step 7/7 : Publishing to MCP Registry...' ) ;
312+ // Step 6 : Publish to MCP Registry
313+ printStep ( 'Step 6/6 : Publishing to MCP Registry...' ) ;
261314
262315 // Check if mcp-publisher is installed
263- const hasMcpPublisher = execSilent ( 'mcp-publisher --version ' , { ignoreError : true } ) ;
316+ const hasMcpPublisher = execSilent ( 'which mcp-publisher' , { ignoreError : true } ) ;
264317 if ( ! hasMcpPublisher ) {
265318 printError ( 'mcp-publisher not found. Install it with: brew install mcp-publisher' ) ;
319+ printError ( 'Or check your PATH if already installed.' ) ;
266320 process . exit ( 1 ) ;
267321 }
268322
269323 if ( options . dryRun ) {
270324 printWarning ( 'Would publish to MCP Registry: mcp-publisher publish' ) ;
271325 printWarning ( 'Skipping MCP Registry publish (dry run)' ) ;
272326 } else {
273- exec ( 'mcp-publisher publish' ) ;
274- printSuccess ( 'Published to MCP Registry' ) ;
275-
276- // Verify MCP Registry publication
277- await new Promise ( resolve => setTimeout ( resolve , 3000 ) ) ; // Wait 3 seconds
278327 try {
279- const mcpResponse = execSilent ( 'curl -s "https://registry.modelcontextprotocol.io/v0/servers?search=io.github.wonderwhy-er/desktop-commander"' ) ;
280- const mcpData = JSON . parse ( mcpResponse ) ;
281- const mcpVersion = mcpData . servers ?. [ 0 ] ?. version || 'unknown' ;
328+ exec ( 'mcp-publisher publish' ) ;
329+ printSuccess ( 'Published to MCP Registry' ) ;
282330
283- if ( mcpVersion === newVersion ) {
284- printSuccess ( `MCP Registry publication verified: v${ mcpVersion } ` ) ;
285- } else {
286- printWarning ( `MCP Registry version: ${ mcpVersion } (expected ${ newVersion } , may take a moment to propagate)` ) ;
331+ // Verify MCP Registry publication
332+ await new Promise ( resolve => setTimeout ( resolve , 3000 ) ) ; // Wait 3 seconds
333+ try {
334+ const mcpResponse = execSilent ( 'curl -s "https://registry.modelcontextprotocol.io/v0/servers?search=io.github.wonderwhy-er/desktop-commander"' ) ;
335+ const mcpData = JSON . parse ( mcpResponse ) ;
336+ const mcpVersion = mcpData . servers ?. [ 0 ] ?. version || 'unknown' ;
337+
338+ if ( mcpVersion === newVersion ) {
339+ printSuccess ( `MCP Registry publication verified: v${ mcpVersion } ` ) ;
340+ } else {
341+ printWarning ( `MCP Registry version: ${ mcpVersion } (expected ${ newVersion } , may take a moment to propagate)` ) ;
342+ }
343+ } catch ( error ) {
344+ printWarning ( 'Could not verify MCP Registry publication' ) ;
287345 }
288346 } catch ( error ) {
289- printWarning ( 'Could not verify MCP Registry publication' ) ;
347+ printError ( 'MCP Registry publish failed!' ) ;
348+ if ( error . message . includes ( '401' ) || error . message . includes ( 'expired' ) ) {
349+ printError ( 'Authentication token expired. Please run: mcp-publisher login github' ) ;
350+ } else if ( error . message . includes ( '422' ) ) {
351+ printError ( 'Validation error in server.json. Check the error message above for details.' ) ;
352+ }
353+ throw error ;
290354 }
291355 }
292356 console . log ( '' ) ;
0 commit comments