execute method
Execute the command.
Implementation
@override
Future<CommandResult> execute(String args, ToolUseContext context) async {
// Parse options.
final McpAddOptions options;
try {
options = McpAddOptions.parse(args);
} on ArgumentError catch (e) {
return TextCommandResult(e.message);
}
try {
final scope = ensureConfigScope(options.scope);
final transport = ensureTransport(options.transport);
final cwd = getCwd();
// XAA fail-fast: validate at add-time, not auth-time.
if (options.xaa && !isXaaEnabled()) {
return const TextCommandResult(
'Error: --xaa requires MAGE_ENABLE_XAA=1 in your environment',
);
}
if (options.xaa) {
final missing = <String>[];
if (options.clientId == null) missing.add('--client-id');
if (!options.clientSecret) missing.add('--client-secret');
if (!hasXaaIdpSettings()) {
missing.add(
"'neomage mcp xaa setup' (settings.xaaIdp not configured)",
);
}
if (missing.isNotEmpty) {
return TextCommandResult(
'Error: --xaa requires: ${missing.join(', ')}',
);
}
}
// Check if transport was explicitly provided.
final transportExplicit = options.transport != null;
// Check if the command looks like a URL (likely incorrect usage).
final cmd = options.commandOrUrl;
final looksLikeUrl =
cmd.startsWith('http://') ||
cmd.startsWith('https://') ||
cmd.startsWith('localhost') ||
cmd.endsWith('/sse') ||
cmd.endsWith('/mcp');
final output = StringBuffer();
if (transport == McpTransport.sse) {
final headers = options.headers != null
? parseHeaders(options.headers!)
: null;
final oauth = buildOAuthConfig(
clientId: options.clientId,
callbackPort: options.callbackPort,
xaa: options.xaa,
);
final serverConfig = SseMcpServerConfig(
url: cmd,
headers: headers,
oauth: oauth,
);
await addMcpConfig(options.name, serverConfig, scope, cwd);
output.writeln(
'Added SSE MCP server ${options.name} with URL: $cmd '
'to ${scope.name} config',
);
if (headers != null && headers.isNotEmpty) {
output.writeln(
'Headers: ${const JsonEncoder.withIndent(' ').convert(headers)}',
);
}
} else if (transport == McpTransport.http) {
final headers = options.headers != null
? parseHeaders(options.headers!)
: null;
final oauth = buildOAuthConfig(
clientId: options.clientId,
callbackPort: options.callbackPort,
xaa: options.xaa,
);
final serverConfig = HttpMcpServerConfig(
url: cmd,
headers: headers,
oauth: oauth,
);
await addMcpConfig(options.name, serverConfig, scope, cwd);
output.writeln(
'Added HTTP MCP server ${options.name} with URL: $cmd '
'to ${scope.name} config',
);
if (headers != null && headers.isNotEmpty) {
output.writeln(
'Headers: ${const JsonEncoder.withIndent(' ').convert(headers)}',
);
}
} else {
// stdio transport
if (options.clientId != null ||
options.clientSecret ||
options.callbackPort != null ||
options.xaa) {
output.writeln(
'Warning: --client-id, --client-secret, --callback-port, and '
'--xaa are only supported for HTTP/SSE transports and will be '
'ignored for stdio.',
);
}
// Warn if this looks like a URL but transport wasn't explicitly
// specified.
if (!transportExplicit && looksLikeUrl) {
output.writeln();
output.writeln(
'Warning: The command "$cmd" looks like a URL, but is being '
'interpreted as a stdio server as --transport was not specified.',
);
output.writeln(
'If this is an HTTP server, use: /mcp-add --transport http '
'${options.name} $cmd',
);
output.writeln(
'If this is an SSE server, use: /mcp-add --transport sse '
'${options.name} $cmd',
);
}
final env = parseEnvVars(options.envVars);
final serverConfig = StdioMcpServerConfig(
command: cmd,
args: options.args,
env: env.isNotEmpty ? env : null,
);
await addMcpConfig(options.name, serverConfig, scope, cwd);
output.writeln(
'Added stdio MCP server ${options.name} with command: '
'$cmd ${options.args.join(' ')} to ${scope.name} config',
);
}
output.writeln('File modified: ${describeMcpConfigFilePath(scope)}');
return TextCommandResult(output.toString().trimRight());
} catch (e) {
return TextCommandResult('Error: $e');
}
}