1- // creator-server.ts
21import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js" ;
32import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js" ;
43import { TEMPLATE_MCP_SERVER } from "./template.js" ;
@@ -34,15 +33,6 @@ const server = new McpServer({
3433
3534const execAsync = promisify ( exec ) ;
3635
37- server . resource ( "template" , "mcp-template://default" , async ( uri ) => ( {
38- contents : [
39- {
40- uri : uri . href ,
41- text : TEMPLATE_MCP_SERVER ,
42- } ,
43- ] ,
44- } ) ) ;
45-
4636server . prompt ( "system prompt" , { } , ( ) => ( {
4737 messages : [
4838 {
@@ -286,9 +276,6 @@ server.tool(
286276 } ;
287277 }
288278
289- // This is the critical step: We write the file to disk FIRST, before doing anything else.
290- // This ensures that even if the context window limit is reached during the conversation,
291- // the file has already been saved and can be accessed in a new conversation.
292279 console . log (
293280 `[createMcpServer] Writing server "${ serverName } " to file: ${ filePath } `
294281 ) ;
@@ -311,7 +298,6 @@ server.tool(
311298 }
312299 }
313300
314- // Add line numbers to the code for better reference
315301 const lines = serverCode . split ( "\n" ) ;
316302 const numberedLines = lines . map (
317303 ( line , index ) => `${ ( index + 1 ) . toString ( ) . padStart ( 4 , " " ) } | ${ line } `
@@ -569,15 +555,13 @@ server.tool(
569555 } ;
570556 }
571557
572- // For full update, just replace the entire file
573558 if ( updateType === "full" ) {
574559 await fs . writeFile ( filePath , code ) ;
575560
576561 const updateDetails = description
577562 ? `\nUpdate details: ${ description } `
578563 : "" ;
579564
580- // Add line numbers to the updated content
581565 const updatedLines = code . split ( "\n" ) ;
582566 const numberedLines = updatedLines . map (
583567 ( line , index ) => `${ ( index + 1 ) . toString ( ) . padStart ( 4 , " " ) } | ${ line } `
@@ -594,11 +578,9 @@ server.tool(
594578 } ;
595579 }
596580
597- // For section or add updates, read the current file
598581 const serverCode = await fs . readFile ( filePath , "utf-8" ) ;
599582 const lines = serverCode . split ( "\n" ) ;
600583
601- // Handle section update
602584 if ( updateType === "section" ) {
603585 if ( ! startLine || ! endLine ) {
604586 return {
@@ -612,7 +594,6 @@ server.tool(
612594 } ;
613595 }
614596
615- // Validate line numbers
616597 if ( startLine > lines . length || endLine > lines . length ) {
617598 return {
618599 content : [
@@ -668,7 +649,6 @@ server.tool(
668649 } ;
669650 }
670651
671- // Handle add update
672652 if ( updateType === "add" ) {
673653 if ( ! insertAfterLine ) {
674654 return {
@@ -682,7 +662,6 @@ server.tool(
682662 } ;
683663 }
684664
685- // Validate line number
686665 if ( insertAfterLine > lines . length ) {
687666 return {
688667 content : [
@@ -695,23 +674,18 @@ server.tool(
695674 } ;
696675 }
697676
698- // Split the new code into lines
699677 const newLines = code . split ( "\n" ) ;
700678
701- // Insert the new code after the specified line
702679 const updatedLines = [
703680 ...lines . slice ( 0 , insertAfterLine ) ,
704681 ...newLines ,
705682 ...lines . slice ( insertAfterLine ) ,
706683 ] ;
707684
708- // Join the lines back together
709685 const updatedCode = updatedLines . join ( "\n" ) ;
710686
711- // Write the updated code back to the file
712687 await fs . writeFile ( filePath , updatedCode ) ;
713688
714- // Add line numbers to the updated content
715689 const numberedLines = updatedLines . map (
716690 ( line , index ) => `${ ( index + 1 ) . toString ( ) . padStart ( 4 , " " ) } | ${ line } `
717691 ) ;
@@ -731,7 +705,6 @@ server.tool(
731705 } ;
732706 }
733707
734- // This should never happen due to zod validation, but just in case
735708 return {
736709 content : [
737710 {
@@ -790,7 +763,6 @@ server.tool(
790763
791764 const serverCode = await fs . readFile ( filePath , "utf-8" ) ;
792765
793- // Add line numbers to the code for better reference
794766 const lines = serverCode . split ( "\n" ) ;
795767 const numberedLines = lines . map (
796768 ( line , index ) => `${ ( index + 1 ) . toString ( ) . padStart ( 4 , " " ) } | ${ line } `
@@ -845,11 +817,9 @@ server.tool(
845817 } ;
846818 }
847819
848- // Use the project directory where the server.js file is located
849820 const projectDir = CURRENT_DIR ;
850821 log ( `Project directory: ${ projectDir } ` ) ;
851822
852- // Check if package.json exists in the project directory, and create one if it doesn't
853823 const packageJsonPath = path . join ( projectDir , "package.json" ) ;
854824 let packageJson ;
855825
@@ -867,24 +837,20 @@ server.tool(
867837 devDependencies : { } ,
868838 } ;
869839
870- // Write the initial package.json
871840 await fs . writeFile (
872841 packageJsonPath ,
873842 JSON . stringify ( packageJson , null , 2 )
874843 ) ;
875844 }
876845
877- // Ensure dependencies object exists
878846 if ( ! packageJson . dependencies ) {
879847 packageJson . dependencies = { } ;
880848 }
881849
882- // Ensure devDependencies object exists
883850 if ( ! packageJson . devDependencies ) {
884851 packageJson . devDependencies = { } ;
885852 }
886853
887- // Filter out already installed dependencies
888854 const missingDependencies = dependencies . filter (
889855 ( dep ) => ! packageJson . dependencies [ dep . split ( "@" ) [ 0 ] ]
890856 ) ;
@@ -900,20 +866,17 @@ server.tool(
900866 } ;
901867 }
902868
903- // Prepare dependency strings
904869 const dependencyString = missingDependencies . join ( " " ) ;
905870
906871 log (
907872 `Installing dependencies in directory ${ projectDir } : ${ dependencyString } `
908873 ) ;
909874
910875 try {
911- // Change to the project directory before running npm
912876 const originalDir = process . cwd ( ) ;
913877 process . chdir ( projectDir ) ;
914878 log ( `Changed working directory to: ${ projectDir } ` ) ;
915879
916- // Install regular dependencies
917880 const { stdout, stderr } = await execAsync (
918881 `npm install ${ dependencyString } --save`
919882 ) ;
@@ -924,7 +887,6 @@ server.tool(
924887
925888 log ( `Regular dependencies installation output: ${ stdout } ` ) ;
926889
927- // Try to install type definitions as dev dependencies
928890 log ( `Checking for TypeScript type definitions...` ) ;
929891 let installedTypes = [ ] ;
930892
@@ -933,13 +895,11 @@ server.tool(
933895 const typePackage = `@types/${ basePackage } ` ;
934896
935897 try {
936- // Check if type package exists
937898 log ( `Checking if ${ typePackage } exists...` ) ;
938899 const { stdout : typeVersionOutput } = await execAsync (
939900 `npm view ${ typePackage } version`
940901 ) ;
941902
942- // If we reach here, the package exists, so install it
943903 if ( typeVersionOutput && typeVersionOutput . trim ( ) ) {
944904 log ( `Installing ${ typePackage } as dev dependency...` ) ;
945905 await execAsync ( `npm install ${ typePackage } --save-dev` ) ;
@@ -950,18 +910,15 @@ server.tool(
950910 }
951911 }
952912
953- // Return to original directory
954913 process . chdir ( originalDir ) ;
955914 log ( `Returned to original directory: ${ originalDir } ` ) ;
956915
957- // Read the updated package.json to confirm what was installed
958916 const updatedPackageJsonContent = await fs . readFile (
959917 packageJsonPath ,
960918 "utf-8"
961919 ) ;
962920 const updatedPackageJson = JSON . parse ( updatedPackageJsonContent ) ;
963921
964- // Get the list of successfully installed dependencies
965922 const installedDeps = Object . keys (
966923 updatedPackageJson . dependencies || { }
967924 ) . filter ( ( key ) =>
@@ -989,7 +946,6 @@ server.tool(
989946 ] ,
990947 } ;
991948 } catch ( installError ) {
992- // Ensure we change back to original directory if there was an error
993949 try {
994950 const currentDir = process . cwd ( ) ;
995951 if ( currentDir !== originalDir ) {
@@ -1033,7 +989,6 @@ server.tool(
1033989 }
1034990) ;
1035991
1036- // Helper function to recursively copy directories
1037992async function copyRecursive ( src , dest ) {
1038993 const stats = await fs . stat ( src ) ;
1039994
@@ -1051,28 +1006,24 @@ async function copyRecursive(src, dest) {
10511006 }
10521007}
10531008
1054- // Tool to analyze server dependencies
10551009server . tool (
10561010 "analyzeServerDependencies" ,
10571011 {
10581012 serverName : z . string ( ) . min ( 1 ) ,
10591013 } ,
10601014 async ( { serverName } ) => {
10611015 try {
1062- // Strip .js extension if it's already included in the serverName
10631016 const nameWithoutExtension = serverName . endsWith ( ".js" )
10641017 ? serverName . slice ( 0 , - 3 )
10651018 : serverName ;
10661019
1067- // Sanitize the server name for use as a filename
10681020 const sanitizedName = nameWithoutExtension . replace (
10691021 / [ ^ a - z A - Z 0 - 9 - _ ] / g,
10701022 "_"
10711023 ) ;
10721024 const filename = `${ sanitizedName } .js` ;
10731025 const filePath = path . join ( SERVERS_DIR , filename ) ;
10741026
1075- // Check if the server exists
10761027 const exists = await fileExists ( filePath ) ;
10771028 if ( ! exists ) {
10781029 return {
@@ -1086,10 +1037,8 @@ server.tool(
10861037 } ;
10871038 }
10881039
1089- // Read the server file content
10901040 const serverCode = await fs . readFile ( filePath , "utf-8" ) ;
10911041
1092- // Simple regex to find import statements
10931042 const importRegex = / i m p o r t \s + (?: [ \w \s { } , * ] + f r o m \s + ) ? [ ' " ] ( [ ^ ' " ] + ) [ ' " ] / g;
10941043 const imports = [ ] ;
10951044 let match ;
@@ -1098,7 +1047,6 @@ server.tool(
10981047 imports . push ( match [ 1 ] ) ;
10991048 }
11001049
1101- // Filter out built-in Node.js modules and SDK imports
11021050 const nodeBuiltins = [
11031051 "fs" ,
11041052 "path" ,
@@ -1112,7 +1060,6 @@ server.tool(
11121060 const sdkImports = [ "@modelcontextprotocol/sdk" ] ;
11131061
11141062 const externalDependencies = imports . filter ( ( imp ) => {
1115- // Check if it's not a relative import, built-in module, or SDK import
11161063 const isRelative =
11171064 imp . startsWith ( "./" ) || imp . startsWith ( "../" ) || imp . startsWith ( "/" ) ;
11181065 const isBuiltin = nodeBuiltins . some (
@@ -1125,18 +1072,15 @@ server.tool(
11251072 return ! isRelative && ! isBuiltin && ! isSdk ;
11261073 } ) ;
11271074
1128- // Extract package names (remove trailing paths)
11291075 const packageNames = externalDependencies . map ( ( dep ) => {
11301076 const parts = dep . split ( "/" ) ;
11311077 if ( dep . startsWith ( "@" ) ) {
1132- // Handle scoped packages like @org /package
11331078 return `${ parts [ 0 ] } /${ parts [ 1 ] } ` ;
11341079 } else {
11351080 return parts [ 0 ] ;
11361081 }
11371082 } ) ;
11381083
1139- // Remove duplicates
11401084 const uniquePackages = [ ...new Set ( packageNames ) ] ;
11411085
11421086 return {
@@ -1200,13 +1144,10 @@ server.tool(
12001144 } ;
12011145 }
12021146
1203- // Run the server using Node.js and capture stdout/stderr
12041147 const { spawn } = await import ( "child_process" ) ;
12051148
1206- // Run without passing environment variables
12071149 const nodeProcess = spawn ( "node" , [ filePath ] , {
12081150 stdio : [ "pipe" , "pipe" , "pipe" ] ,
1209- // Not passing environment variables
12101151 } ) ;
12111152
12121153 let stdout = "" ;
@@ -1222,12 +1163,10 @@ server.tool(
12221163 hasError = true ;
12231164 } ) ;
12241165
1225- // Set a timeout to kill the process after the specified time
12261166 const timeoutId = setTimeout ( ( ) => {
12271167 nodeProcess . kill ( ) ;
12281168 } , timeout ) ;
12291169
1230- // Wait for the process to exit or timeout
12311170 await new Promise ( ( resolve ) => {
12321171 nodeProcess . on ( "exit" , ( code ) => {
12331172 clearTimeout ( timeoutId ) ;
@@ -1238,7 +1177,6 @@ server.tool(
12381177 } ) ;
12391178 } ) ;
12401179
1241- // Prepare the response
12421180 let resultText = "" ;
12431181 if ( hasError ) {
12441182 resultText = `Server "${ nameWithoutExtension } " encountered errors during execution:\n\n` ;
@@ -1308,7 +1246,6 @@ async function registerServerWithClaude(serverName, serverPath) {
13081246 config . mcpServers = { } ;
13091247 }
13101248
1311- // Create server entry with command, args, and empty env object for future environment variables
13121249 config . mcpServers [ serverName ] = {
13131250 command : "node" ,
13141251 args : [ serverPath ] ,
0 commit comments