@@ -11,6 +11,10 @@ class CreateCommand extends Command<int> {
1111 @override
1212 final description = 'Creates a new MCP server project.' ;
1313
14+ @override
15+ String get invocation =>
16+ 'mcp_dart create <package_name> [project_path] [arguments]' ;
17+
1418 CreateCommand ({Logger ? logger}) : _logger = logger ?? Logger () {
1519 argParser.addOption (
1620 'template' ,
@@ -25,68 +29,122 @@ class CreateCommand extends Command<int> {
2529
2630 @override
2731 Future <int > run () async {
32+ final String packageName;
33+ final String projectPath;
34+
2835 if (argResults! .rest.isEmpty) {
29- _logger.err ('Usage: mcp_dart create <project_name> [arguments]' );
36+ packageName = _logger.prompt (
37+ 'What is the project name?' ,
38+ defaultValue: 'mcp_server' ,
39+ );
40+ projectPath = packageName;
41+ } else {
42+ packageName = argResults! .rest.first;
43+ projectPath =
44+ argResults! .rest.length > 1 ? argResults! .rest[1 ] : packageName;
45+ }
46+
47+ if (! _isValidPackageName (packageName)) {
48+ _logger.err (
49+ 'Error: "$packageName " is not a valid package name.\n\n '
50+ 'Package names should be all lowercase, with underscores to separate words, '
51+ 'e.g. "mcp_server". Use only basic Latin letters and Arabic digits: [a-z0-9_]. '
52+ 'Also, make sure the name is a valid Dart identifier -- that is, it '
53+ "doesn't start with digits and isn't a reserved word." ,
54+ );
3055 return ExitCode .usage.code;
3156 }
3257
33- final projectName = argResults! .rest.first;
34- final directory = Directory (projectName);
58+ final directory = Directory (projectPath);
3559
3660 if (directory.existsSync ()) {
37- _logger.err ('Error: Directory "$projectName " already exists.' );
61+ _logger.err ('Error: Directory "$projectPath " already exists.' );
3862 return ExitCode .cantCreate.code;
3963 }
4064
4165 final templateArg = argResults! ['template' ] as String ;
4266 final brick = _resolveBrick (templateArg);
4367
4468 final generator = await MasonGenerator .fromBrick (brick);
45- final progress = _logger.progress ('Creating $projectName ' );
69+ final progress = _logger.progress ('Creating $projectPath ' );
4670
4771 await generator.generate (
4872 DirectoryGeneratorTarget (directory),
49- vars: < String , dynamic > {'name' : projectName },
73+ vars: < String , dynamic > {'name' : packageName },
5074 );
5175 progress.complete ();
5276
53- _logger.info ('Running dart pub get...' );
54- var result = await Process .run (
77+ await _runCommand (
5578 'dart' ,
5679 ['pub' , 'get' ],
5780 workingDirectory: directory.path,
58- runInShell : true ,
81+ label : 'Running pub get' ,
5982 );
6083
61- if (result.exitCode != 0 ) {
62- _logger.err ('Error running pub get:' );
63- _logger.err (result.stderr.toString ());
64- return result.exitCode;
65- }
66-
6784 // Auto-add mcp_dart to ensure latest version
68- _logger.info ('Adding latest mcp_dart dependency...' );
69- result = await Process .run (
85+ await _runCommand (
7086 'dart' ,
7187 ['pub' , 'add' , 'mcp_dart' ],
7288 workingDirectory: directory.path,
73- runInShell : true ,
89+ label : 'Adding mcp_dart dependency' ,
7490 );
7591
76- if (result.exitCode != 0 ) {
77- _logger.err ('Error adding mcp_dart:' );
78- _logger.err (result.stderr.toString ());
79- return result.exitCode;
80- }
92+ // Run dart format
93+ await _runCommand (
94+ 'dart' ,
95+ ['format' , '.' ],
96+ workingDirectory: directory.path,
97+ label: 'Formatting code' ,
98+ );
8199
82- _logger.success ('\n Success! Created $projectName .' );
100+ _logger.success ('\n Success! Created $projectPath .' );
83101 _logger.info ('Run your server with:' );
84- _logger.info (' cd $projectName ' );
102+ if (projectPath != '.' ) {
103+ _logger.info (' cd $projectPath ' );
104+ }
85105 _logger.info (' dart run bin/server.dart' );
86106
87107 return ExitCode .success.code;
88108 }
89109
110+ Future <void > _runCommand (
111+ String executable,
112+ List <String > arguments, {
113+ required String workingDirectory,
114+ required String label,
115+ }) async {
116+ final progress = _logger.progress (label);
117+ try {
118+ final result = await Process .run (
119+ executable,
120+ arguments,
121+ workingDirectory: workingDirectory,
122+ runInShell: true ,
123+ );
124+
125+ if (result.exitCode != 0 ) {
126+ progress.fail ();
127+ _logger.err ('Error running $label :' );
128+ _logger.err (result.stderr.toString ());
129+ throw ProcessException (
130+ executable,
131+ arguments,
132+ result.stderr.toString (),
133+ result.exitCode,
134+ );
135+ }
136+ progress.complete ();
137+ } catch (_) {
138+ progress.fail ();
139+ rethrow ;
140+ }
141+ }
142+
143+ bool _isValidPackageName (String name) {
144+ if (name.isEmpty) return false ;
145+ return RegExp (r'^[a-z][a-z0-9_]*$' ).hasMatch (name);
146+ }
147+
90148 Brick _resolveBrick (String template) {
91149 const resolver = TemplateResolver ();
92150 return resolver.resolve (template).toBrick ();
0 commit comments