execute method

  1. @override
Future<CommandResult> execute(
  1. String args,
  2. ToolUseContext context
)
override

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');
  }
}