Drop package rpc (#488)

diff --git a/analysis_options.yaml b/analysis_options.yaml
index e773966..a812034 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -4,11 +4,11 @@
   strong-mode:
     implicit-casts: false
   exclude:
-    - 'dart-sdk/**'
-    - 'third_party/**'
-    - 'doc/generated/**'
-    - 'flutter/**'
-    - 'lib/src/protos/**'
+    - "dart-sdk/**"
+    - "third_party/**"
+    - "doc/generated/**"
+    - "flutter/**"
+    - "lib/src/protos/**"
 
 linter:
   rules:
diff --git a/lib/services_dev.dart b/lib/services_dev.dart
index ea620f5..3a0c77c 100644
--- a/lib/services_dev.dart
+++ b/lib/services_dev.dart
@@ -6,19 +6,16 @@
 library services_dev;
 
 import 'dart:async';
-import 'dart:convert';
 import 'dart:io';
 
 import 'package:args/args.dart';
 import 'package:logging/logging.dart';
-import 'package:rpc/rpc.dart';
 import 'package:shelf/shelf.dart';
 import 'package:shelf/shelf_io.dart' as shelf;
 
 import 'src/common.dart';
-import 'src/common_server.dart';
+import 'src/common_server_api.dart';
 import 'src/common_server_impl.dart';
-import 'src/common_server_proto.dart';
 import 'src/flutter_web.dart';
 import 'src/sdk_manager.dart';
 import 'src/server_cache.dart';
@@ -43,19 +40,6 @@
 
   final sdk = sdkPath;
 
-  void printExit(String doc) {
-    print(doc);
-    exit(0);
-  }
-
-  if (result['discovery'] as bool) {
-    final serverUrl = result['server-url'] as String;
-    EndpointsServer.generateDiscovery(SdkManager.flutterSdk, serverUrl)
-        .then(printExit);
-
-    return;
-  }
-
   Logger.root.level = Level.FINER;
   Logger.root.onRecord.listen((LogRecord record) {
     print(record);
@@ -79,43 +63,16 @@
     });
   }
 
-  static Future<String> generateDiscovery(
-      FlutterSdk flutterSdk, String serverUrl) async {
-    final flutterWebManager = FlutterWebManager(flutterSdk);
-    final commonServerImpl = CommonServerImpl(
-      sdkPath,
-      flutterWebManager,
-      _ServerContainer(),
-      _Cache(),
-    );
-    final commonServer = CommonServer(commonServerImpl);
-    await commonServerImpl.init();
-    final apiServer = ApiServer(apiPrefix: '/api', prettyPrint: true)
-      ..addApi(commonServer);
-    apiServer.enableDiscoveryApi();
-
-    final uri = Uri.parse('/api/discovery/v1/apis/dartservices/v1/rest');
-    final request = HttpApiRequest('GET', uri, <String, dynamic>{},
-        Stream<List<int>>.fromIterable(<List<int>>[]));
-    final response = await apiServer.handleHttpApiRequest(request);
-    return utf8.decode(await response.body.first);
-  }
-
   final int port;
   HttpServer server;
 
   Pipeline pipeline;
   Handler handler;
 
-  ApiServer apiServer;
-  bool discoveryEnabled;
-  CommonServer commonServer;
-  CommonServerProto commonServerProto;
+  CommonServerApi commonServerApi;
   FlutterWebManager flutterWebManager;
 
   EndpointsServer._(String sdkPath, this.port) {
-    discoveryEnabled = false;
-
     flutterWebManager = FlutterWebManager(SdkManager.flutterSdk);
     final commonServerImpl = CommonServerImpl(
       sdkPath,
@@ -123,50 +80,14 @@
       _ServerContainer(),
       _Cache(),
     );
-    commonServer = CommonServer(commonServerImpl);
-    commonServerProto = CommonServerProto(commonServerImpl);
+    commonServerApi = CommonServerApi(commonServerImpl);
     commonServerImpl.init();
 
-    apiServer = ApiServer(apiPrefix: '/api', prettyPrint: true)
-      ..addApi(commonServer);
-
-    pipeline = Pipeline()
+    pipeline = const Pipeline()
         .addMiddleware(logRequests())
         .addMiddleware(_createCustomCorsHeadersMiddleware());
 
-    handler = pipeline.addHandler((request) {
-      if (request.requestedUri.path.startsWith(PROTO_API_URL_PREFIX)) {
-        return commonServerProto.router.handler(request);
-      }
-      return _apiHandler(request);
-    });
-  }
-
-  Future<Response> _apiHandler(Request request) {
-    if (!discoveryEnabled) {
-      apiServer.enableDiscoveryApi();
-      discoveryEnabled = true;
-    }
-
-    // NOTE: We could read in the request body here and parse it similar to the
-    // _parseRequest method to determine content-type and dispatch to e.g. a
-    // plain text handler if we want to support that.
-    final apiRequest = HttpApiRequest(
-        request.method, request.requestedUri, request.headers, request.read());
-
-    // Promote text/plain requests to application/json.
-    if (apiRequest.headers['content-type'] == 'text/plain; charset=utf-8') {
-      apiRequest.headers['content-type'] = 'application/json; charset=utf-8';
-    }
-
-    return apiServer
-        .handleHttpApiRequest(apiRequest)
-        .then((HttpApiResponse apiResponse) {
-      // TODO(jcollins-g): use sendApiResponse helper?
-      return Response(apiResponse.status,
-          body: apiResponse.body,
-          headers: Map<String, String>.from(apiResponse.headers));
-    });
+    handler = pipeline.addHandler(commonServerApi.router.handler);
   }
 
   Response printUsage(Request request, dynamic e, StackTrace stackTrace) {
diff --git a/lib/services_gae.dart b/lib/services_gae.dart
index b063207..e580b71 100644
--- a/lib/services_gae.dart
+++ b/lib/services_gae.dart
@@ -9,19 +9,16 @@
 
 import 'package:appengine/appengine.dart' as ae;
 import 'package:logging/logging.dart';
-import 'package:rpc/rpc.dart' as rpc;
 import 'package:shelf/shelf_io.dart' as shelf_io;
 
 import 'src/common.dart';
-import 'src/common_server.dart';
+import 'src/common_server_api.dart';
 import 'src/common_server_impl.dart';
-import 'src/common_server_proto.dart';
 import 'src/flutter_web.dart';
 import 'src/sdk_manager.dart';
 import 'src/server_cache.dart';
 
-const String _API = '/api';
-const String _API_V1_PREFIX = '/api/dartservices/v1';
+const String _API_PREFIX = '/api/dartservices/';
 const String _healthCheck = '/_ah/health';
 const String _readynessCheck = '/_ah/ready';
 
@@ -63,10 +60,8 @@
   final String redisServerUri;
 
   bool discoveryEnabled;
-  rpc.ApiServer apiServer;
-  CommonServer commonServer;
   CommonServerImpl commonServerImpl;
-  CommonServerProto commonServerProto;
+  CommonServerApi commonServerApi;
 
   GaeServer(this.sdkPath, this.redisServerUri) {
     hierarchicalLoggingEnabled = true;
@@ -87,11 +82,7 @@
               io.Platform.environment['GAE_VERSION'],
             ),
     );
-    commonServer = CommonServer(commonServerImpl);
-    commonServerProto = CommonServerProto(commonServerImpl);
-    // Enabled pretty printing of returned json for debuggability.
-    apiServer = rpc.ApiServer(apiPrefix: _API, prettyPrint: true)
-      ..addApi(commonServer);
+    commonServerApi = CommonServerApi(commonServerImpl);
   }
 
   Future<dynamic> start([int gaePort = 8080]) async {
@@ -111,10 +102,8 @@
       await _processReadynessRequest(request);
     } else if (request.uri.path == _healthCheck) {
       await _processHealthRequest(request);
-    } else if (request.uri.path.startsWith(_API_V1_PREFIX)) {
-      await _processApiRequest(request);
-    } else if (request.uri.path.startsWith(PROTO_API_URL_PREFIX)) {
-      await shelf_io.handleRequest(request, commonServerProto.router.handler);
+    } else if (request.uri.path.startsWith(_API_PREFIX)) {
+      await shelf_io.handleRequest(request, commonServerApi.router.handler);
     } else {
       await _processDefaultRequest(request);
     }
@@ -173,32 +162,6 @@
     await request.response.close();
   }
 
-  Future _processApiRequest(io.HttpRequest request) async {
-    if (!discoveryEnabled) {
-      apiServer.enableDiscoveryApi();
-      discoveryEnabled = true;
-    }
-    // NOTE: We could read in the request body here and parse it similar to
-    // the _parseRequest method to determine content-type and dispatch to e.g.
-    // a plain text handler if we want to support that.
-    final apiRequest = rpc.HttpApiRequest.fromHttpRequest(request);
-
-    // Dartpad sends data as plain text, we need to promote this to
-    // application/json to ensure that the rpc library processes it correctly
-    try {
-      apiRequest.headers['content-type'] = 'application/json; charset=utf-8';
-      final apiResponse = await apiServer.handleHttpApiRequest(apiRequest);
-      await rpc.sendApiResponse(apiResponse, request.response);
-    } catch (e) {
-      // This should only happen in the case where there is a bug in the rpc
-      // package. Otherwise it always returns an HttpApiResponse.
-      _logger.warning('Failed with error: $e when trying to call '
-          'method at \'${request.uri.path}\'.');
-      request.response.statusCode = io.HttpStatus.internalServerError;
-      await request.response.close();
-    }
-  }
-
   Future _processDefaultRequest(io.HttpRequest request) async {
     request.response.statusCode = io.HttpStatus.notFound;
     await request.response.close();
diff --git a/lib/src/analysis_server.dart b/lib/src/analysis_server.dart
index 4b3401a..19ae44f 100644
--- a/lib/src/analysis_server.dart
+++ b/lib/src/analysis_server.dart
@@ -6,15 +6,16 @@
 library services.analysis_server;
 
 import 'dart:async';
+import 'dart:convert';
 import 'dart:io';
 
 import 'package:analysis_server_lib/analysis_server_lib.dart';
 import 'package:logging/logging.dart';
 import 'package:path/path.dart' as path;
 
-import 'api_classes.dart' as api;
 import 'common.dart';
 import 'flutter_web.dart';
+import 'protos/dart_services.pb.dart' as proto;
 import 'pub.dart';
 import 'scheduler.dart';
 
@@ -24,12 +25,12 @@
 /// to stdout.
 bool dumpServerMessages = false;
 
-final String _WARMUP_SRC_HTML =
+const String _WARMUP_SRC_HTML =
     "import 'dart:html'; main() { int b = 2;  b++;   b. }";
-final String _WARMUP_SRC = 'main() { int b = 2;  b++;   b. }';
+const String _WARMUP_SRC = 'main() { int b = 2;  b++;   b. }';
 
 // Use very long timeouts to ensure that the server has enough time to restart.
-final Duration _ANALYSIS_SERVER_TIMEOUT = Duration(seconds: 35);
+const Duration _ANALYSIS_SERVER_TIMEOUT = Duration(seconds: 35);
 
 class AnalysisServerWrapper {
   final String sdkPath;
@@ -111,15 +112,15 @@
     // Return when the analysis server exits. We introduce a delay so that when
     // we terminate the analysis server we can exit normally.
     return analysisServer.processCompleter.future.then((int code) {
-      return Future<int>.delayed(Duration(seconds: 1), () {
+      return Future<int>.delayed(const Duration(seconds: 1), () {
         return code;
       });
     });
   }
 
-  Future<api.CompleteResponse> complete(String src, int offset) async {
+  Future<proto.CompleteResponse> complete(String src, int offset) async {
     final sources = <String, String>{kMainDart: src};
-    final location = api.Location.from(kMainDart, offset);
+    final location = Location(kMainDart, offset);
 
     final results =
         await _completeImpl(sources, location.sourceName, location.offset);
@@ -144,37 +145,44 @@
       }
     });
 
-    return api.CompleteResponse(
-      results.replacementOffset,
-      results.replacementLength,
-      suggestions.map((CompletionSuggestion c) => c.toMap()).toList(),
-    );
+    return proto.CompleteResponse()
+      ..replacementOffset = results.replacementOffset
+      ..replacementLength = results.replacementLength
+      ..completions
+          .addAll(suggestions.map((CompletionSuggestion c) => proto.Completion()
+            ..completion.addAll(c.toMap().map((key, value) {
+              // TODO: Properly support Lists, Maps (this is a hack).
+              if (value is Map || value is List) {
+                value = json.encode(value);
+              }
+              return MapEntry(key.toString(), value.toString());
+            }))));
   }
 
-  Future<api.FixesResponse> getFixes(String src, int offset) {
+  Future<proto.FixesResponse> getFixes(String src, int offset) {
     return getFixesMulti(
       <String, String>{kMainDart: src},
-      api.Location.from(kMainDart, offset),
+      Location(kMainDart, offset),
     );
   }
 
-  Future<api.FixesResponse> getFixesMulti(
-      Map<String, String> sources, api.Location location) async {
+  Future<proto.FixesResponse> getFixesMulti(
+      Map<String, String> sources, Location location) async {
     final results =
         await _getFixesImpl(sources, location.sourceName, location.offset);
-    final responseFixes = results.fixes.map(_convertAnalysisErrorFix).toList();
-    return api.FixesResponse(responseFixes);
+    final responseFixes = results.fixes.map(_convertAnalysisErrorFix);
+    return proto.FixesResponse()..fixes.addAll(responseFixes);
   }
 
-  Future<api.AssistsResponse> getAssists(String src, int offset) async {
+  Future<proto.AssistsResponse> getAssists(String src, int offset) async {
     final sources = {kMainDart: src};
-    final sourceName = api.Location.from(kMainDart, offset).sourceName;
+    final sourceName = Location(kMainDart, offset).sourceName;
     final results = await _getAssistsImpl(sources, sourceName, offset);
     final fixes = _convertSourceChangesToCandidateFixes(results.assists);
-    return api.AssistsResponse(fixes);
+    return proto.AssistsResponse()..assists.addAll(fixes);
   }
 
-  Future<api.FormatResponse> format(String src, int offset) {
+  Future<proto.FormatResponse> format(String src, int offset) {
     return _formatImpl(src, offset).then((FormatResult editResult) {
       final edits = editResult.edits;
 
@@ -186,10 +194,14 @@
             edit.offset, edit.offset + edit.length, edit.replacement);
       }
 
-      return api.FormatResponse(src, editResult.selectionOffset);
+      return proto.FormatResponse()
+        ..newString = src
+        ..offset = editResult.selectionOffset;
     }).catchError((dynamic error) {
       _logger.fine('format error: $error');
-      return api.FormatResponse(src, offset);
+      return proto.FormatResponse()
+        ..newString = src
+        ..offset = offset;
     });
   }
 
@@ -232,13 +244,14 @@
     }, timeoutDuration: _ANALYSIS_SERVER_TIMEOUT));
   }
 
-  Future<api.AnalysisResults> analyze(String source) {
+  Future<proto.AnalysisResults> analyze(String source) {
     var sources = <String, String>{kMainDart: source};
 
     _logger
         .fine('analyzeMulti: Scheduler queue: ${serverScheduler.queueCount}');
 
-    return serverScheduler.schedule(ClosureTask<api.AnalysisResults>(() async {
+    return serverScheduler
+        .schedule(ClosureTask<proto.AnalysisResults>(() async {
       clearErrors();
 
       final analysisCompleter = getAnalysisCompleteCompleter();
@@ -248,15 +261,14 @@
 
       // Calculate the issues.
       final issues = getErrors().map((AnalysisError error) {
-        return api.AnalysisIssue.fromIssue(
-          error.severity.toLowerCase(),
-          error.location.startLine,
-          error.message,
-          charStart: error.location.offset,
-          charLength: error.location.length,
-          sourceName: path.basename(error.location.file),
-          hasFixes: error.hasFix,
-        );
+        return proto.AnalysisIssue()
+          ..kind = error.severity.toLowerCase()
+          ..line = error.location.startLine
+          ..message = error.message
+          ..sourceName = path.basename(error.location.file)
+          ..hasFixes = error.hasFix
+          ..charStart = error.location.offset
+          ..charLength = error.location.length;
       }).toList();
 
       issues.sort();
@@ -268,10 +280,9 @@
             .addAll(filterSafePackagesFromImports(getAllImportsFor(source)));
       }
 
-      return api.AnalysisResults(
-        issues,
-        packageImports.toList(),
-      );
+      return proto.AnalysisResults()
+        ..issues.addAll(issues)
+        ..packageImports.addAll(packageImports);
     }, timeoutDuration: _ANALYSIS_SERVER_TIMEOUT));
   }
 
@@ -289,7 +300,7 @@
       final analysisCompleter = getAnalysisCompleteCompleter();
       await _loadSources(sources);
       await analysisCompleter.future;
-      final length = 1;
+      const length = 1;
       final assists =
           await analysisServer.edit.getAssists(path, offset, length);
       await _unloadSources();
@@ -298,16 +309,16 @@
   }
 
   /// Convert between the Analysis Server type and the API protocol types.
-  static api.ProblemAndFixes _convertAnalysisErrorFix(
+  static proto.ProblemAndFixes _convertAnalysisErrorFix(
       AnalysisErrorFixes analysisFixes) {
     final problemMessage = analysisFixes.error.message;
     final problemOffset = analysisFixes.error.location.offset;
     final problemLength = analysisFixes.error.location.length;
 
-    final possibleFixes = <api.CandidateFix>[];
+    final possibleFixes = <proto.CandidateFix>[];
 
     for (final sourceChange in analysisFixes.fixes) {
-      final edits = <api.SourceEdit>[];
+      final edits = <proto.SourceEdit>[];
 
       // A fix that tries to modify other files is considered invalid.
 
@@ -321,23 +332,29 @@
         }
 
         for (final sourceEdit in sourceFileEdit.edits) {
-          edits.add(api.SourceEdit.fromChanges(
-              sourceEdit.offset, sourceEdit.length, sourceEdit.replacement));
+          edits.add(proto.SourceEdit()
+            ..offset = sourceEdit.offset
+            ..length = sourceEdit.length
+            ..replacement = sourceEdit.replacement);
         }
       }
       if (!invalidFix) {
-        final possibleFix =
-            api.CandidateFix.fromEdits(sourceChange.message, edits);
+        final possibleFix = proto.CandidateFix()
+          ..message = sourceChange.message
+          ..edits.addAll(edits);
         possibleFixes.add(possibleFix);
       }
     }
-    return api.ProblemAndFixes.fromList(
-        possibleFixes, problemMessage, problemOffset, problemLength);
+    return proto.ProblemAndFixes()
+      ..fixes.addAll(possibleFixes)
+      ..problemMessage = problemMessage
+      ..offset = problemOffset
+      ..length = problemLength;
   }
 
-  static List<api.CandidateFix> _convertSourceChangesToCandidateFixes(
+  static List<proto.CandidateFix> _convertSourceChangesToCandidateFixes(
       List<SourceChange> sourceChanges) {
-    final assists = <api.CandidateFix>[];
+    final assists = <proto.CandidateFix>[];
 
     for (final sourceChange in sourceChanges) {
       for (final sourceFileEdit in sourceChange.edits) {
@@ -345,17 +362,23 @@
           break;
         }
 
-        final apiSourceEdits = sourceFileEdit.edits.map((sourceEdit) {
-          return api.SourceEdit.fromChanges(
-              sourceEdit.offset, sourceEdit.length, sourceEdit.replacement);
-        }).toList();
+        final sourceEdits = sourceFileEdit.edits.map((sourceEdit) {
+          return proto.SourceEdit()
+            ..offset = sourceEdit.offset
+            ..length = sourceEdit.length
+            ..replacement = sourceEdit.replacement;
+        });
 
-        assists.add(api.CandidateFix.fromEdits(
-          sourceChange.message,
-          apiSourceEdits,
-          sourceChange.selection?.offset,
-          _convertLinkedEditGroups(sourceChange.linkedEditGroups),
-        ));
+        final candidateFix = proto.CandidateFix();
+        candidateFix.message = sourceChange.message;
+        candidateFix.edits.addAll(sourceEdits);
+        final selectionOffset = sourceChange.selection?.offset;
+        if (selectionOffset != null) {
+          candidateFix.selectionOffset = selectionOffset;
+        }
+        candidateFix.linkedEditGroups
+            .addAll(_convertLinkedEditGroups(sourceChange.linkedEditGroups));
+        assists.add(candidateFix);
       }
     }
 
@@ -364,17 +387,19 @@
 
   /// Convert a list of the analysis server's [LinkedEditGroup]s into the API's
   /// equivalent.
-  static List<api.LinkedEditGroup> _convertLinkedEditGroups(
-      List<LinkedEditGroup> groups) {
-    return groups?.map<api.LinkedEditGroup>((g) {
-      return api.LinkedEditGroup(
-        g.positions?.map((p) => p.offset)?.toList(),
-        g.length,
-        g.suggestions
-            ?.map((s) => api.LinkedEditSuggestion(s.value, s.kind))
-            ?.toList(),
-      );
-    })?.toList();
+  static Iterable<proto.LinkedEditGroup> _convertLinkedEditGroups(
+      Iterable<LinkedEditGroup> groups) {
+    return groups?.map<proto.LinkedEditGroup>((g) {
+          return proto.LinkedEditGroup()
+            ..positions.addAll(g.positions?.map((p) => p.offset)?.toList())
+            ..length = g.length
+            ..suggestions.addAll(g.suggestions
+                ?.map((s) => proto.LinkedEditSuggestion()
+                  ..value = s.value
+                  ..kind = s.kind)
+                ?.toList());
+        }) ??
+        [];
   }
 
   /// Cleanly shutdown the Analysis Server.
@@ -383,7 +408,7 @@
     // --pause-isolates-on-exit from working; fix.
     return analysisServer.server
         .shutdown()
-        .timeout(Duration(seconds: 1))
+        .timeout(const Duration(seconds: 1))
         .catchError((dynamic e) => null);
   }
 
@@ -451,7 +476,7 @@
       path.join(_sourceDirPath, sourceName);
 
   /// Warm up the analysis server to be ready for use.
-  Future<api.CompleteResponse> warmup({bool useHtml = false}) =>
+  Future<proto.CompleteResponse> warmup({bool useHtml = false}) =>
       complete(useHtml ? _WARMUP_SRC_HTML : _WARMUP_SRC, 10);
 
   final Set<String> _overlayPaths = <String>{};
@@ -561,3 +586,10 @@
     return errors;
   }
 }
+
+class Location {
+  final String sourceName;
+  final int offset;
+
+  const Location(this.sourceName, this.offset);
+}
diff --git a/lib/src/api_classes.dart b/lib/src/api_classes.dart
deleted file mode 100644
index 945a321..0000000
--- a/lib/src/api_classes.dart
+++ /dev/null
@@ -1,314 +0,0 @@
-// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-/// All classes exported over the RPC protocol.
-library services.api_classes;
-
-import 'dart:convert';
-
-import 'package:rpc/rpc.dart';
-
-class AnalysisResults {
-  final List<AnalysisIssue> issues;
-
-  @ApiProperty(description: 'The package imports parsed from the source.')
-  final List<String> packageImports;
-
-  AnalysisResults(this.issues, this.packageImports);
-}
-
-class AnalysisIssue implements Comparable<AnalysisIssue> {
-  final String kind;
-  final int line;
-  final String message;
-  final String sourceName;
-
-  final bool hasFixes;
-
-  final int charStart;
-  final int charLength;
-
-  AnalysisIssue.fromIssue(this.kind, this.line, this.message,
-      {this.charStart,
-      this.charLength,
-      this.sourceName,
-      this.hasFixes = false});
-
-  Map<String, dynamic> toMap() {
-    final m = <String, dynamic>{'kind': kind, 'line': line, 'message': message};
-    if (charStart != null) m['charStart'] = charStart;
-    if (charLength != null) m['charLength'] = charLength;
-    if (hasFixes != null) m['hasFixes'] = hasFixes;
-    if (sourceName != null) m['sourceName'] = sourceName;
-
-    return m;
-  }
-
-  @override
-  int compareTo(AnalysisIssue other) => line - other.line;
-
-  @override
-  String toString() => '$kind: $message [$line]';
-}
-
-class SourceRequest {
-  @ApiProperty(required: true, description: 'The Dart source.')
-  String source;
-
-  @ApiProperty(description: 'An optional offset into the source code.')
-  int offset;
-}
-
-class SourcesRequest {
-  @ApiProperty(required: true, description: 'Map of names to Sources.')
-  Map<String, String> sources;
-
-  @ApiProperty(description: 'An optional location in the source code.')
-  Location location;
-
-  @ApiProperty(description: 'Ignored: always treated as true.')
-  @deprecated
-  bool strongMode;
-}
-
-class Location {
-  String sourceName;
-  int offset;
-
-  Location();
-
-  Location.from(this.sourceName, this.offset);
-}
-
-class CompileRequest {
-  @ApiProperty(required: true, description: 'The Dart source.')
-  String source;
-
-  @ApiProperty(
-      description:
-          'Return the Dart to JS source map; optional (defaults to false).')
-  bool returnSourceMap;
-}
-
-class CompileResponse {
-  final String result;
-  final String sourceMap;
-
-  CompileResponse(this.result, [this.sourceMap]);
-}
-
-class CompileDDCRequest {
-  @ApiProperty(required: true, description: 'The Dart source.')
-  String source;
-}
-
-class CompileDDCResponse {
-  final String result;
-  final String modulesBaseUrl;
-
-  CompileDDCResponse(this.result, this.modulesBaseUrl);
-}
-
-class CounterRequest {
-  @ApiProperty(required: true)
-  String name;
-}
-
-class CounterResponse {
-  final int count;
-
-  CounterResponse(this.count);
-}
-
-class DocumentResponse {
-  final Map<String, String> info;
-
-  DocumentResponse(this.info);
-}
-
-class CompleteResponse {
-  @ApiProperty(
-      description: 'The offset of the start of the text to be replaced.')
-  final int replacementOffset;
-
-  @ApiProperty(description: 'The length of the text to be replaced.')
-  final int replacementLength;
-
-  final List<Map<String, String>> completions;
-
-  CompleteResponse(this.replacementOffset, this.replacementLength,
-      List<Map<dynamic, dynamic>> completions)
-      : completions = _convert(completions);
-
-  /// Convert any non-string values from the contained maps.
-  static List<Map<String, String>> _convert(List<Map<dynamic, dynamic>> list) {
-    return list.map<Map<String, String>>((Map<dynamic, dynamic> m) {
-      final newMap = <String, String>{};
-      for (final key in m.keys.cast<String>()) {
-        dynamic data = m[key];
-        // TODO: Properly support Lists, Maps (this is a hack).
-        if (data is Map || data is List) {
-          data = json.encode(data);
-        }
-        newMap[key.toString()] = '$data';
-      }
-      return newMap;
-    }).toList();
-  }
-}
-
-class FixesResponse {
-  final List<ProblemAndFixes> fixes;
-
-  FixesResponse(this.fixes);
-}
-
-/// Represents a problem detected during analysis, and a set of possible
-/// ways of resolving the problem.
-class ProblemAndFixes {
-  // TODO(lukechurch): consider consolidating this with [AnalysisIssue]
-  final List<CandidateFix> fixes;
-  final String problemMessage;
-  final int offset;
-  final int length;
-
-  ProblemAndFixes() : this.fromList(<CandidateFix>[]);
-
-  ProblemAndFixes.fromList(
-      [this.fixes, this.problemMessage, this.offset, this.length]);
-}
-
-class LinkedEditSuggestion {
-  @ApiProperty(
-      description: 'The value that could be used to replace all of the linked '
-          'edit regions.')
-  final String value;
-
-  @ApiProperty(description: 'The kind of value being proposed.')
-  final String kind;
-
-  LinkedEditSuggestion(this.value, this.kind);
-}
-
-class LinkedEditGroup {
-  @ApiProperty(
-      description: 'The positions of the regions that should be edited '
-          'simultaneously.')
-  final List<int> positions;
-
-  @ApiProperty(
-      description: 'The length of the regions that should be edited '
-          'simultaneously.')
-  final int length;
-
-  @ApiProperty(
-      description: 'Pre-computed suggestions for what every region might want '
-          'to be changed to.')
-  final List<LinkedEditSuggestion> suggestions;
-
-  LinkedEditGroup(this.positions, this.length, this.suggestions);
-}
-
-/// Represents a possible way of solving an Analysis Problem.
-class CandidateFix {
-  final String message;
-  final List<SourceEdit> edits;
-  final int selectionOffset;
-  final List<LinkedEditGroup> linkedEditGroups;
-
-  CandidateFix() : this.fromEdits();
-
-  CandidateFix.fromEdits([
-    this.message,
-    this.edits,
-    this.selectionOffset,
-    this.linkedEditGroups,
-  ]);
-}
-
-/// Represents a reformatting of the code.
-class FormatResponse {
-  @ApiProperty(description: 'The formatted source code.')
-  final String newString;
-
-  @ApiProperty(
-      description: 'The (optional) new offset of the cursor; can be `null`.')
-  final int offset;
-
-  FormatResponse(this.newString, [this.offset = 0]);
-}
-
-/// Represents a single edit-point change to a source file.
-class SourceEdit {
-  final int offset;
-  final int length;
-  final String replacement;
-
-  SourceEdit() : this.fromChanges();
-
-  SourceEdit.fromChanges([this.offset, this.length, this.replacement]);
-
-  String applyTo(String target) {
-    if (offset >= replacement.length) {
-      throw 'Offset beyond end of string';
-    } else if (offset + length >= replacement.length) {
-      throw 'Change beyond end of string';
-    }
-
-    final pre = '${target.substring(0, offset)}';
-    final post = '${target.substring(offset + length)}';
-    return '$pre$replacement$post';
-  }
-}
-
-/// The response from the `/assists` service call.
-class AssistsResponse {
-  final List<CandidateFix> assists;
-
-  AssistsResponse(this.assists);
-}
-
-/// The response from the `/version` service call.
-class VersionResponse {
-  @ApiProperty(
-      description: 'The Dart SDK version that DartServices is compatible with. '
-          'This will be a semver string.')
-  final String sdkVersion;
-
-  @ApiProperty(
-      description:
-          'The full Dart SDK version that DartServices is compatible with.')
-  final String sdkVersionFull;
-
-  @ApiProperty(
-      description: 'The Dart SDK version that the server is running on. This '
-          'will start with a semver string, and have a space and other build '
-          'details appended.')
-  final String runtimeVersion;
-
-  @ApiProperty(description: 'The App Engine version.')
-  final String appEngineVersion;
-
-  @ApiProperty(description: 'The dart-services backend version.')
-  final String servicesVersion;
-
-  @ApiProperty(description: 'The Flutter SDK version.')
-  final String flutterVersion;
-
-  @ApiProperty(description: "The Flutter SDK's Dart version.")
-  final String flutterDartVersion;
-
-  @ApiProperty(description: "The Flutter SDK's full Dart version.")
-  final String flutterDartVersionFull;
-
-  VersionResponse(
-      {this.sdkVersion,
-      this.sdkVersionFull,
-      this.runtimeVersion,
-      this.appEngineVersion,
-      this.servicesVersion,
-      this.flutterDartVersion,
-      this.flutterDartVersionFull,
-      this.flutterVersion});
-}
diff --git a/lib/src/common_server.dart b/lib/src/common_server.dart
deleted file mode 100644
index aa29900..0000000
--- a/lib/src/common_server.dart
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-library services.common_server;
-
-import 'dart:async';
-
-import 'package:rpc/rpc.dart';
-
-import 'api_classes.dart';
-import 'common_server_impl.dart' show BadRequest, CommonServerImpl;
-export 'common_server_impl.dart' show log, ServerContainer;
-
-@ApiClass(name: 'dartservices', version: 'v1')
-class CommonServer {
-  final CommonServerImpl _impl;
-
-  CommonServer(this._impl);
-
-  @ApiMethod(
-      method: 'POST',
-      path: 'analyze',
-      description:
-          'Analyze the given Dart source code and return any resulting '
-          'analysis errors or warnings.')
-  Future<AnalysisResults> analyze(SourceRequest request) =>
-      _convertBadRequest(() => _impl.analyze(request));
-
-  @ApiMethod(
-      method: 'POST',
-      path: 'compile',
-      description: 'Compile the given Dart source code and return the '
-          'resulting JavaScript; this uses the dart2js compiler.')
-  Future<CompileResponse> compile(CompileRequest request) =>
-      _convertBadRequest(() => _impl.compile(request));
-
-  @ApiMethod(
-      method: 'POST',
-      path: 'compileDDC',
-      description: 'Compile the given Dart source code and return the '
-          'resulting JavaScript; this uses the DDC compiler.')
-  Future<CompileDDCResponse> compileDDC(CompileRequest request) =>
-      _convertBadRequest(() => _impl.compileDDC(request));
-
-  @ApiMethod(
-      method: 'POST',
-      path: 'complete',
-      description:
-          'Get the valid code completion results for the given offset.')
-  Future<CompleteResponse> complete(SourceRequest request) =>
-      _convertBadRequest(() => _impl.complete(request));
-
-  @ApiMethod(
-      method: 'POST',
-      path: 'fixes',
-      description: 'Get any quick fixes for the given source code location.')
-  Future<FixesResponse> fixes(SourceRequest request) =>
-      _convertBadRequest(() => _impl.fixes(request));
-
-  @ApiMethod(
-      method: 'POST',
-      path: 'assists',
-      description: 'Get assists for the given source code location.')
-  Future<AssistsResponse> assists(SourceRequest request) =>
-      _convertBadRequest(() => _impl.assists(request));
-
-  @ApiMethod(
-      method: 'POST',
-      path: 'format',
-      description: 'Format the given Dart source code and return the results. '
-          'If an offset is supplied in the request, the new position for that '
-          'offset in the formatted code will be returned.')
-  Future<FormatResponse> format(SourceRequest request) =>
-      _convertBadRequest(() => _impl.format(request));
-
-  @ApiMethod(
-      method: 'POST',
-      path: 'document',
-      description: 'Return the relevant dartdoc information for the element at '
-          'the given offset.')
-  Future<DocumentResponse> document(SourceRequest request) =>
-      _convertBadRequest(() => _impl.document(request));
-
-  @ApiMethod(
-      method: 'GET',
-      path: 'version',
-      description: 'Return the current SDK version for DartServices.')
-  Future<VersionResponse> version() =>
-      _convertBadRequest(() => _impl.version());
-}
-
-Future<T> _convertBadRequest<T>(Future<T> Function() fun) async {
-  try {
-    return await fun();
-  } catch (e) {
-    if (e is BadRequest) {
-      throw BadRequestError(e.cause);
-    }
-    throw BadRequestError(e.toString());
-  }
-}
diff --git a/lib/src/common_server_api.dart b/lib/src/common_server_api.dart
new file mode 100644
index 0000000..2e118c4
--- /dev/null
+++ b/lib/src/common_server_api.dart
@@ -0,0 +1,176 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library services.common_server_api;
+
+import 'dart:async';
+import 'dart:convert';
+
+import 'package:meta/meta.dart';
+import 'package:protobuf/protobuf.dart';
+import 'package:shelf/shelf.dart';
+import 'package:shelf_router/shelf_router.dart';
+
+import 'common_server_impl.dart' show CommonServerImpl, BadRequest;
+import 'protos/dart_services.pb.dart' as proto;
+
+export 'common_server_impl.dart' show log, ServerContainer;
+
+part 'common_server_api.g.dart'; // generated with 'pub run build_runner build'
+
+const PROTOBUF_CONTENT_TYPE = 'application/x-protobuf';
+const JSON_CONTENT_TYPE = 'application/json; charset=utf-8';
+const PROTO_API_URL_PREFIX = '/api/dartservices/<apiVersion>';
+
+class CommonServerApi {
+  final CommonServerImpl _impl;
+
+  CommonServerApi(this._impl);
+
+  @Route.post('$PROTO_API_URL_PREFIX/analyze')
+  Future<Response> analyze(Request request, String apiVersion) =>
+      _processRequest(request,
+          decodeFromJSON: (json) =>
+              proto.SourceRequest.create()..mergeFromProto3Json(json),
+          decodeFromProto: (bytes) => proto.SourceRequest.fromBuffer(bytes),
+          transform: _impl.analyze);
+
+  @Route.post('$PROTO_API_URL_PREFIX/compile')
+  Future<Response> compile(Request request, String apiVersion) =>
+      _processRequest(request,
+          decodeFromJSON: (json) =>
+              proto.CompileRequest.create()..mergeFromProto3Json(json),
+          decodeFromProto: (bytes) => proto.CompileRequest.fromBuffer(bytes),
+          transform: _impl.compile);
+
+  @Route.post('$PROTO_API_URL_PREFIX/compileDDC')
+  Future<Response> compileDDC(Request request, String apiVersion) =>
+      _processRequest(request,
+          decodeFromJSON: (json) =>
+              proto.CompileDDCRequest.create()..mergeFromProto3Json(json),
+          decodeFromProto: (bytes) => proto.CompileDDCRequest.fromBuffer(bytes),
+          transform: _impl.compileDDC);
+
+  @Route.post('$PROTO_API_URL_PREFIX/complete')
+  Future<Response> complete(Request request, String apiVersion) =>
+      _processRequest(request,
+          decodeFromJSON: (json) =>
+              proto.SourceRequest.create()..mergeFromProto3Json(json),
+          decodeFromProto: (bytes) => proto.SourceRequest.fromBuffer(bytes),
+          transform: _impl.complete);
+
+  @Route.post('$PROTO_API_URL_PREFIX/fixes')
+  Future<Response> fixes(Request request, String apiVersion) =>
+      _processRequest(request,
+          decodeFromJSON: (json) =>
+              proto.SourceRequest.create()..mergeFromProto3Json(json),
+          decodeFromProto: (bytes) => proto.SourceRequest.fromBuffer(bytes),
+          transform: _impl.fixes);
+
+  @Route.post('$PROTO_API_URL_PREFIX/assists')
+  Future<Response> assists(Request request, String apiVersion) =>
+      _processRequest(request,
+          decodeFromJSON: (json) =>
+              proto.SourceRequest.create()..mergeFromProto3Json(json),
+          decodeFromProto: (bytes) => proto.SourceRequest.fromBuffer(bytes),
+          transform: _impl.assists);
+
+  @Route.post('$PROTO_API_URL_PREFIX/format')
+  Future<Response> format(Request request, String apiVersion) =>
+      _processRequest(request,
+          decodeFromJSON: (json) =>
+              proto.SourceRequest.create()..mergeFromProto3Json(json),
+          decodeFromProto: (bytes) => proto.SourceRequest.fromBuffer(bytes),
+          transform: _impl.format);
+
+  @Route.post('$PROTO_API_URL_PREFIX/document')
+  Future<Response> document(Request request, String apiVersion) =>
+      _processRequest(request,
+          decodeFromJSON: (json) =>
+              proto.SourceRequest.create()..mergeFromProto3Json(json),
+          decodeFromProto: (bytes) => proto.SourceRequest.fromBuffer(bytes),
+          transform: _impl.document);
+
+  @Route.post('$PROTO_API_URL_PREFIX/version')
+  Future<Response> versionPost(Request request, String apiVersion) =>
+      _processRequest(request,
+          decodeFromJSON: (json) =>
+              proto.VersionRequest.create()..mergeFromProto3Json(json),
+          decodeFromProto: (bytes) => proto.VersionRequest.fromBuffer(bytes),
+          transform: _impl.version);
+
+  @Route.get('$PROTO_API_URL_PREFIX/version')
+  Future<Response> versionGet(Request request, String apiVersion) =>
+      _processRequest(request,
+          decodeFromJSON: (json) =>
+              proto.VersionRequest.create()..mergeFromProto3Json(json),
+          decodeFromProto: (bytes) => proto.VersionRequest.fromBuffer(bytes),
+          transform: _impl.version);
+
+  Router get router => _$CommonServerApiRouter(this);
+
+  // We are serving requests that are arriving in both Protobuf binary encoding,
+  // and Protobuf JSON encoding. To handle this we need the ability to decode
+  // the requests and encode the responses. We also need to know how to do the
+  // work the request is requesting.
+
+  Future<Response> _processRequest<I, O extends GeneratedMessage>(
+    Request request, {
+    @required I Function(List<int> bytes) decodeFromProto,
+    @required I Function(Object json) decodeFromJSON,
+    @required Future<O> Function(I input) transform,
+  }) async {
+    if (request.mimeType == PROTOBUF_CONTENT_TYPE) {
+      // Dealing with binary Protobufs
+      final body = <int>[];
+      await for (final chunk in request.read()) {
+        body.addAll(chunk);
+      }
+      try {
+        final response = await transform(decodeFromProto(body));
+        return Response.ok(
+          response.writeToBuffer(),
+          headers: _PROTOBUF_HEADERS,
+        );
+      } on BadRequest catch (e) {
+        return Response(400,
+            headers: _PROTOBUF_HEADERS,
+            body: (proto.BadRequest.create()
+                  ..error = (proto.ErrorMessage.create()..message = e.cause))
+                .writeToBuffer());
+      }
+    } else {
+      // Dealing with JSON encoded Protobufs
+      final body = await request.readAsString();
+      try {
+        final response = await transform(
+            decodeFromJSON(body.isNotEmpty ? json.decode(body) : {}));
+        return Response.ok(
+          _jsonEncoder.convert(response.toProto3Json()),
+          encoding: utf8,
+          headers: _JSON_HEADERS,
+        );
+      } on BadRequest catch (e) {
+        return Response(400,
+            headers: _JSON_HEADERS,
+            encoding: utf8,
+            body: _jsonEncoder.convert((proto.BadRequest.create()
+                  ..error = (proto.ErrorMessage.create()..message = e.cause))
+                .toProto3Json()));
+      }
+    }
+  }
+
+  final JsonEncoder _jsonEncoder = const JsonEncoder.withIndent(' ');
+
+  static const _JSON_HEADERS = {
+    'Access-Control-Allow-Origin': '*',
+    'Content-Type': JSON_CONTENT_TYPE
+  };
+
+  static const _PROTOBUF_HEADERS = {
+    'Access-Control-Allow-Origin': '*',
+    'Content-Type': PROTOBUF_CONTENT_TYPE
+  };
+}
diff --git a/lib/src/common_server_api.g.dart b/lib/src/common_server_api.g.dart
new file mode 100644
index 0000000..f2e9900
--- /dev/null
+++ b/lib/src/common_server_api.g.dart
@@ -0,0 +1,30 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of services.common_server_api;
+
+// **************************************************************************
+// ShelfRouterGenerator
+// **************************************************************************
+
+Router _$CommonServerApiRouter(CommonServerApi service) {
+  final router = Router();
+  router.add(
+      'POST', r'/api/dartservices/<apiVersion>/analyze', service.analyze);
+  router.add(
+      'POST', r'/api/dartservices/<apiVersion>/compile', service.compile);
+  router.add(
+      'POST', r'/api/dartservices/<apiVersion>/compileDDC', service.compileDDC);
+  router.add(
+      'POST', r'/api/dartservices/<apiVersion>/complete', service.complete);
+  router.add('POST', r'/api/dartservices/<apiVersion>/fixes', service.fixes);
+  router.add(
+      'POST', r'/api/dartservices/<apiVersion>/assists', service.assists);
+  router.add('POST', r'/api/dartservices/<apiVersion>/format', service.format);
+  router.add(
+      'POST', r'/api/dartservices/<apiVersion>/document', service.document);
+  router.add(
+      'POST', r'/api/dartservices/<apiVersion>/version', service.versionPost);
+  router.add(
+      'GET', r'/api/dartservices/<apiVersion>/version', service.versionGet);
+  return router;
+}
diff --git a/lib/src/common_server_impl.dart b/lib/src/common_server_impl.dart
index 61b49e2..544c015 100644
--- a/lib/src/common_server_impl.dart
+++ b/lib/src/common_server_impl.dart
@@ -14,15 +14,15 @@
 
 import '../version.dart';
 import 'analysis_server.dart';
-import 'api_classes.dart';
 import 'common.dart';
 import 'compiler.dart';
 import 'flutter_web.dart';
+import 'protos/dart_services.pb.dart' as proto;
 import 'pub.dart';
 import 'sdk_manager.dart';
 import 'server_cache.dart';
 
-final Duration _standardExpiration = Duration(hours: 1);
+const Duration _standardExpiration = Duration(hours: 1);
 final Logger log = Logger('common_server');
 
 class BadRequest implements Exception {
@@ -118,62 +118,90 @@
       flutterAnalysisServer.shutdown(),
       compiler.dispose(),
       Future<dynamic>.sync(cache.shutdown)
-    ]).timeout(Duration(minutes: 1));
+    ]).timeout(const Duration(minutes: 1));
   }
 
-  Future<AnalysisResults> analyze(SourceRequest request) {
+  Future<proto.AnalysisResults> analyze(proto.SourceRequest request) {
+    if (!request.hasSource()) {
+      throw BadRequest('Missing parameter: \'source\'');
+    }
+
     return _analyze(request.source);
   }
 
-  Future<CompileResponse> compile(CompileRequest request) {
+  Future<proto.CompileResponse> compile(proto.CompileRequest request) {
+    if (!request.hasSource()) {
+      throw BadRequest('Missing parameter: \'source\'');
+    }
+
     return _compileDart2js(request.source,
         returnSourceMap: request.returnSourceMap ?? false);
   }
 
-  Future<CompileDDCResponse> compileDDC(CompileRequest request) {
+  Future<proto.CompileDDCResponse> compileDDC(proto.CompileDDCRequest request) {
+    if (!request.hasSource()) {
+      throw BadRequest('Missing parameter: \'source\'');
+    }
+
     return _compileDDC(request.source);
   }
 
-  Future<CompleteResponse> complete(SourceRequest request) {
-    if (request.offset == null) {
+  Future<proto.CompleteResponse> complete(proto.SourceRequest request) {
+    if (!request.hasSource()) {
+      throw BadRequest('Missing parameter: \'source\'');
+    }
+    if (!request.hasOffset()) {
       throw BadRequest('Missing parameter: \'offset\'');
     }
 
     return _complete(request.source, request.offset);
   }
 
-  Future<FixesResponse> fixes(SourceRequest request) {
-    if (request.offset == null) {
+  Future<proto.FixesResponse> fixes(proto.SourceRequest request) {
+    if (!request.hasSource()) {
+      throw BadRequest('Missing parameter: \'source\'');
+    }
+    if (!request.hasOffset()) {
       throw BadRequest('Missing parameter: \'offset\'');
     }
 
     return _fixes(request.source, request.offset);
   }
 
-  Future<AssistsResponse> assists(SourceRequest request) {
-    if (request.offset == null) {
+  Future<proto.AssistsResponse> assists(proto.SourceRequest request) {
+    if (!request.hasSource()) {
+      throw BadRequest('Missing parameter: \'source\'');
+    }
+    if (!request.hasOffset()) {
       throw BadRequest('Missing parameter: \'offset\'');
     }
 
     return _assists(request.source, request.offset);
   }
 
-  Future<FormatResponse> format(SourceRequest request) {
-    return _format(request.source, offset: request.offset);
-  }
-
-  Future<DocumentResponse> document(SourceRequest request) {
-    return _document(request.source, request.offset);
-  }
-
-  Future<VersionResponse> version() =>
-      Future<VersionResponse>.value(_version());
-
-  Future<AnalysisResults> _analyze(String source) async {
-    if (source == null) {
+  Future<proto.FormatResponse> format(proto.SourceRequest request) {
+    if (!request.hasSource()) {
       throw BadRequest('Missing parameter: \'source\'');
     }
 
+    return _format(request.source, offset: request.offset ?? 0);
+  }
+
+  Future<proto.DocumentResponse> document(proto.SourceRequest request) {
+    if (!request.hasSource()) {
+      throw BadRequest('Missing parameter: \'source\'');
+    }
+    if (!request.hasOffset()) {
+      throw BadRequest('Missing parameter: \'offset\'');
+    }
+
+    return _document(request.source, request.offset);
+  }
+
+  Future<proto.VersionResponse> version(proto.VersionRequest _) =>
+      Future<proto.VersionResponse>.value(_version());
+
+  Future<proto.AnalysisResults> _analyze(String source) async {
     await _checkPackageReferencesInitFlutterWeb(source);
 
     try {
@@ -191,14 +219,10 @@
     }
   }
 
-  Future<CompileResponse> _compileDart2js(
+  Future<proto.CompileResponse> _compileDart2js(
     String source, {
     bool returnSourceMap = false,
   }) async {
-    if (source == null) {
-      throw BadRequest('Missing parameter: \'source\'');
-    }
-
     await _checkPackageReferencesInitFlutterWeb(source);
 
     final sourceHash = _hashSource(source);
@@ -208,11 +232,10 @@
     final result = await checkCache(memCacheKey);
     if (result != null) {
       log.info('CACHE: Cache hit for compileDart2js');
-      final resultObj = JsonDecoder().convert(result);
-      return CompileResponse(
-        resultObj['compiledJS'] as String,
-        returnSourceMap ? resultObj['sourceMap'] as String : null,
-      );
+      final resultObj = const JsonDecoder().convert(result);
+      return proto.CompileResponse()
+        ..result = resultObj['compiledJS'] as String
+        ..sourceMap = returnSourceMap ? resultObj['sourceMap'] as String : null;
     }
 
     log.info('CACHE: MISS for compileDart2js');
@@ -229,13 +252,18 @@
             '${outputSize}kb of JavaScript in ${ms}ms using dart2js.');
         final sourceMap = returnSourceMap ? results.sourceMap : null;
 
-        final cachedResult = JsonEncoder().convert(<String, String>{
+        final cachedResult = const JsonEncoder().convert(<String, String>{
           'compiledJS': results.compiledJS,
           'sourceMap': sourceMap,
         });
         // Don't block on cache set.
         unawaited(setCache(memCacheKey, cachedResult));
-        return CompileResponse(results.compiledJS, sourceMap);
+        final compileResponse = proto.CompileResponse();
+        compileResponse.result = results.compiledJS;
+        if (sourceMap != null) {
+          compileResponse.sourceMap = sourceMap;
+        }
+        return compileResponse;
       } else {
         final problems = results.problems;
         final errors = problems.map(_printCompileProblem).join('\n');
@@ -249,11 +277,7 @@
     });
   }
 
-  Future<CompileDDCResponse> _compileDDC(String source) async {
-    if (source == null) {
-      throw BadRequest('Missing parameter: \'source\'');
-    }
-
+  Future<proto.CompileDDCResponse> _compileDDC(String source) async {
     await _checkPackageReferencesInitFlutterWeb(source);
 
     final sourceHash = _hashSource(source);
@@ -262,11 +286,10 @@
     final result = await checkCache(memCacheKey);
     if (result != null) {
       log.info('CACHE: Cache hit for compileDDC');
-      final resultObj = JsonDecoder().convert(result);
-      return CompileDDCResponse(
-        resultObj['compiledJS'] as String,
-        resultObj['modulesBaseUrl'] as String,
-      );
+      final resultObj = const JsonDecoder().convert(result);
+      return proto.CompileDDCResponse()
+        ..result = resultObj['compiledJS'] as String
+        ..modulesBaseUrl = resultObj['modulesBaseUrl'] as String;
     }
 
     log.info('CACHE: MISS for compileDDC');
@@ -280,13 +303,15 @@
         log.info('PERF: Compiled $lineCount lines of Dart into '
             '${outputSize}kb of JavaScript in ${ms}ms using DDC.');
 
-        final cachedResult = JsonEncoder().convert(<String, String>{
+        final cachedResult = const JsonEncoder().convert(<String, String>{
           'compiledJS': results.compiledJS,
           'modulesBaseUrl': results.modulesBaseUrl,
         });
         // Don't block on cache set.
         unawaited(setCache(memCacheKey, cachedResult));
-        return CompileDDCResponse(results.compiledJS, results.modulesBaseUrl);
+        return proto.CompileDDCResponse()
+          ..result = results.compiledJS
+          ..modulesBaseUrl = results.modulesBaseUrl;
       } else {
         final problems = results.problems;
         final errors = problems.map(_printCompileProblem).join('\n');
@@ -300,14 +325,7 @@
     });
   }
 
-  Future<DocumentResponse> _document(String source, int offset) async {
-    if (source == null) {
-      throw BadRequest('Missing parameter: \'source\'');
-    }
-    if (offset == null) {
-      throw BadRequest('Missing parameter: \'offset\'');
-    }
-
+  Future<proto.DocumentResponse> _document(String source, int offset) async {
     await _checkPackageReferencesInitFlutterWeb(source);
 
     final watch = Stopwatch()..start();
@@ -316,7 +334,7 @@
           await getCorrectAnalysisServer(source).dartdoc(source, offset);
       docInfo ??= <String, String>{};
       log.info('PERF: Computed dartdoc in ${watch.elapsedMilliseconds}ms.');
-      return DocumentResponse(docInfo);
+      return proto.DocumentResponse()..info.addAll(docInfo);
     } catch (e, st) {
       log.severe('Error during dartdoc', e, st);
       await restart();
@@ -324,24 +342,17 @@
     }
   }
 
-  VersionResponse _version() => VersionResponse(
-      sdkVersion: SdkManager.sdk.version,
-      sdkVersionFull: SdkManager.sdk.versionFull,
-      runtimeVersion: vmVersion,
-      servicesVersion: servicesVersion,
-      appEngineVersion: container.version,
-      flutterDartVersion: SdkManager.flutterSdk.version,
-      flutterDartVersionFull: SdkManager.flutterSdk.versionFull,
-      flutterVersion: SdkManager.flutterSdk.flutterVersion);
+  proto.VersionResponse _version() => proto.VersionResponse()
+    ..sdkVersion = SdkManager.sdk.version
+    ..sdkVersionFull = SdkManager.sdk.versionFull
+    ..runtimeVersion = vmVersion
+    ..servicesVersion = servicesVersion
+    ..appEngineVersion = container.version
+    ..flutterDartVersion = SdkManager.flutterSdk.version
+    ..flutterDartVersionFull = SdkManager.flutterSdk.versionFull
+    ..flutterVersion = SdkManager.flutterSdk.flutterVersion;
 
-  Future<CompleteResponse> _complete(String source, int offset) async {
-    if (source == null) {
-      throw BadRequest('Missing parameter: \'source\'');
-    }
-    if (offset == null) {
-      throw BadRequest('Missing parameter: \'offset\'');
-    }
-
+  Future<proto.CompleteResponse> _complete(String source, int offset) async {
     await _checkPackageReferencesInitFlutterWeb(source);
 
     final watch = Stopwatch()..start();
@@ -357,14 +368,7 @@
     }
   }
 
-  Future<FixesResponse> _fixes(String source, int offset) async {
-    if (source == null) {
-      throw BadRequest('Missing parameter: \'source\'');
-    }
-    if (offset == null) {
-      throw BadRequest('Missing parameter: \'offset\'');
-    }
-
+  Future<proto.FixesResponse> _fixes(String source, int offset) async {
     await _checkPackageReferencesInitFlutterWeb(source);
 
     final watch = Stopwatch()..start();
@@ -374,14 +378,7 @@
     return response;
   }
 
-  Future<AssistsResponse> _assists(String source, int offset) async {
-    if (source == null) {
-      throw BadRequest('Missing parameter: \'source\'');
-    }
-    if (offset == null) {
-      throw BadRequest('Missing parameter: \'offset\'');
-    }
-
+  Future<proto.AssistsResponse> _assists(String source, int offset) async {
     await _checkPackageReferencesInitFlutterWeb(source);
 
     final watch = Stopwatch()..start();
@@ -391,12 +388,7 @@
     return response;
   }
 
-  Future<FormatResponse> _format(String source, {int offset}) async {
-    if (source == null) {
-      throw BadRequest('Missing parameter: \'source\'');
-    }
-    offset ??= 0;
-
+  Future<proto.FormatResponse> _format(String source, {int offset}) async {
     final watch = Stopwatch()..start();
 
     final response =
diff --git a/lib/src/common_server_proto.dart b/lib/src/common_server_proto.dart
deleted file mode 100644
index a9443e5..0000000
--- a/lib/src/common_server_proto.dart
+++ /dev/null
@@ -1,365 +0,0 @@
-// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-library services.common_server_proto;
-
-import 'dart:async';
-import 'dart:convert';
-
-import 'package:meta/meta.dart';
-import 'package:protobuf/protobuf.dart';
-import 'package:shelf/shelf.dart';
-import 'package:shelf_router/shelf_router.dart';
-
-import 'api_classes.dart' as api;
-import 'common_server_impl.dart' show CommonServerImpl, BadRequest;
-import 'protos/dart_services.pb.dart' as proto;
-
-export 'common_server_impl.dart' show log, ServerContainer;
-
-part 'common_server_proto.g.dart'; // generated with 'pub run build_runner build'
-
-const PROTOBUF_CONTENT_TYPE = 'application/x-protobuf';
-const JSON_CONTENT_TYPE = 'application/json; charset=utf-8';
-const PROTO_API_URL_PREFIX = '/api/dartservices/v2';
-
-class CommonServerProto {
-  final CommonServerImpl _impl;
-
-  CommonServerProto(this._impl);
-
-  @Route.post('$PROTO_API_URL_PREFIX/analyze')
-  Future<Response> analyze(Request request) => _processRequest(request,
-      decodeFromJSON: (json) =>
-          proto.SourceRequest.create()..mergeFromProto3Json(json),
-      decodeFromProto: (bytes) => proto.SourceRequest.fromBuffer(bytes),
-      transform: _analyze);
-
-  Future<proto.AnalysisResults> _analyze(proto.SourceRequest request) async {
-    if (!request.hasSource()) {
-      throw BadRequest('Missing parameter: \'source\'');
-    }
-
-    final apiRequest = api.SourceRequest()
-      ..source = request.source
-      ..offset = request.offset;
-    final apiResponse = await _impl.analyze(apiRequest);
-
-    return proto.AnalysisResults()
-      ..packageImports.addAll(apiResponse.packageImports)
-      ..issues.addAll(
-        apiResponse.issues.map(
-          (issue) => proto.AnalysisIssue()
-            ..kind = issue.kind
-            ..line = issue.line
-            ..message = issue.message
-            ..sourceName = issue.sourceName
-            ..hasFixes = issue.hasFixes
-            ..charStart = issue.charStart
-            ..charLength = issue.charLength,
-        ),
-      );
-  }
-
-  @Route.post('$PROTO_API_URL_PREFIX/compile')
-  Future<Response> compile(Request request) => _processRequest(request,
-      decodeFromJSON: (json) =>
-          proto.CompileRequest.create()..mergeFromProto3Json(json),
-      decodeFromProto: (bytes) => proto.CompileRequest.fromBuffer(bytes),
-      transform: _compile);
-
-  Future<proto.CompileResponse> _compile(proto.CompileRequest request) async {
-    final apiRequest = api.CompileRequest()
-      ..source = request.source
-      ..returnSourceMap = request.returnSourceMap;
-    final apiResponse = await _impl.compile(apiRequest);
-    final response = proto.CompileResponse()..result = apiResponse.result;
-    if (apiResponse.sourceMap != null) {
-      response.sourceMap = apiResponse.sourceMap;
-    }
-    return response;
-  }
-
-  @Route.post('$PROTO_API_URL_PREFIX/compileDDC')
-  Future<Response> compileDDC(Request request) => _processRequest(request,
-      decodeFromJSON: (json) =>
-          proto.CompileRequest.create()..mergeFromProto3Json(json),
-      decodeFromProto: (bytes) => proto.CompileRequest.fromBuffer(bytes),
-      transform: _compileDDC);
-
-  Future<proto.CompileDDCResponse> _compileDDC(
-      proto.CompileRequest request) async {
-    final apiRequest = api.CompileRequest()
-      ..source = request.source
-      ..returnSourceMap = request.returnSourceMap;
-    final apiResponse = await _impl.compileDDC(apiRequest);
-
-    return proto.CompileDDCResponse()
-      ..result = apiResponse.result
-      ..modulesBaseUrl = apiResponse.modulesBaseUrl;
-  }
-
-  @Route.post('$PROTO_API_URL_PREFIX/complete')
-  Future<Response> complete(Request request) => _processRequest(request,
-      decodeFromJSON: (json) =>
-          proto.SourceRequest.create()..mergeFromProto3Json(json),
-      decodeFromProto: (bytes) => proto.SourceRequest.fromBuffer(bytes),
-      transform: _complete);
-
-  Future<proto.CompleteResponse> _complete(proto.SourceRequest request) async {
-    if (!request.hasSource()) {
-      throw BadRequest('Missing parameter: \'source\'');
-    }
-    if (!request.hasOffset()) {
-      throw BadRequest('Missing parameter: \'offset\'');
-    }
-
-    final apiRequest = api.SourceRequest()
-      ..offset = request.offset
-      ..source = request.source;
-    final apiResponse = await _impl.complete(apiRequest);
-
-    return proto.CompleteResponse()
-      ..replacementOffset = apiResponse.replacementOffset
-      ..replacementLength = apiResponse.replacementLength
-      ..completions.addAll(
-        apiResponse.completions.map(
-          (completion) => proto.Completion()..completion.addAll(completion),
-        ),
-      );
-  }
-
-  @Route.post('$PROTO_API_URL_PREFIX/fixes')
-  Future<Response> fixes(Request request) => _processRequest(request,
-      decodeFromJSON: (json) =>
-          proto.SourceRequest.create()..mergeFromProto3Json(json),
-      decodeFromProto: (bytes) => proto.SourceRequest.fromBuffer(bytes),
-      transform: _fixes);
-
-  Future<proto.FixesResponse> _fixes(proto.SourceRequest request) async {
-    if (!request.hasSource()) {
-      throw BadRequest('Missing parameter: \'source\'');
-    }
-    if (!request.hasOffset()) {
-      throw BadRequest('Missing parameter: \'offset\'');
-    }
-
-    final apiRequest = api.SourceRequest()
-      ..offset = request.offset
-      ..source = request.source;
-    final apiResponse = await _impl.fixes(apiRequest);
-
-    return proto.FixesResponse()
-      ..fixes.addAll(
-        apiResponse.fixes.map(
-          (apiFix) => proto.ProblemAndFixes()
-            ..problemMessage = apiFix.problemMessage
-            ..offset = apiFix.offset
-            ..length = apiFix.length
-            ..fixes.addAll(
-              apiFix.fixes.map(_transformCandidateFix),
-            ),
-        ),
-      );
-  }
-
-  @Route.post('$PROTO_API_URL_PREFIX/assists')
-  Future<Response> assists(Request request) => _processRequest(request,
-      decodeFromJSON: (json) =>
-          proto.SourceRequest.create()..mergeFromProto3Json(json),
-      decodeFromProto: (bytes) => proto.SourceRequest.fromBuffer(bytes),
-      transform: _assists);
-
-  Future<proto.AssistsResponse> _assists(proto.SourceRequest request) async {
-    if (!request.hasSource()) {
-      throw BadRequest('Missing parameter: \'source\'');
-    }
-    if (!request.hasOffset()) {
-      throw BadRequest('Missing parameter: \'offset\'');
-    }
-
-    final apiRequest = api.SourceRequest()
-      ..offset = request.offset
-      ..source = request.source;
-    final apiResponse = await _impl.assists(apiRequest);
-
-    return proto.AssistsResponse()
-      ..assists.addAll(
-        apiResponse.assists.map(_transformCandidateFix),
-      );
-  }
-
-  @Route.post('$PROTO_API_URL_PREFIX/format')
-  Future<Response> format(Request request) => _processRequest(request,
-      decodeFromJSON: (json) =>
-          proto.SourceRequest.create()..mergeFromProto3Json(json),
-      decodeFromProto: (bytes) => proto.SourceRequest.fromBuffer(bytes),
-      transform: _format);
-
-  Future<proto.FormatResponse> _format(proto.SourceRequest request) async {
-    if (!request.hasSource()) {
-      throw BadRequest('Missing parameter: \'source\'');
-    }
-
-    final apiRequest = api.SourceRequest()
-      ..offset = request.offset
-      ..source = request.source;
-    final apiResponse = await _impl.format(apiRequest);
-
-    return proto.FormatResponse()
-      ..newString = apiResponse.newString
-      ..offset = apiResponse.offset;
-  }
-
-  @Route.post('$PROTO_API_URL_PREFIX/document')
-  Future<Response> document(Request request) => _processRequest(request,
-      decodeFromJSON: (json) =>
-          proto.SourceRequest.create()..mergeFromProto3Json(json),
-      decodeFromProto: (bytes) => proto.SourceRequest.fromBuffer(bytes),
-      transform: _document);
-
-  Future<proto.DocumentResponse> _document(proto.SourceRequest request) async {
-    if (!request.hasSource()) {
-      throw BadRequest('Missing parameter: \'source\'');
-    }
-    if (!request.hasOffset()) {
-      throw BadRequest('Missing parameter: \'offset\'');
-    }
-
-    final apiRequest = api.SourceRequest()
-      ..offset = request.offset
-      ..source = request.source;
-    final apiResponse = await _impl.document(apiRequest);
-
-    return proto.DocumentResponse()..info.addAll(apiResponse.info);
-  }
-
-  @Route.post('$PROTO_API_URL_PREFIX/version')
-  Future<Response> versionPost(Request request) => _processRequest(request,
-      decodeFromJSON: (json) =>
-          proto.VersionRequest.create()..mergeFromProto3Json(json),
-      decodeFromProto: (bytes) => proto.VersionRequest.fromBuffer(bytes),
-      transform: _version);
-
-  @Route.get('$PROTO_API_URL_PREFIX/version')
-  Future<Response> versionGet(Request request) => _processRequest(request,
-      decodeFromJSON: (json) =>
-          proto.VersionRequest.create()..mergeFromProto3Json(json),
-      decodeFromProto: (bytes) => proto.VersionRequest.fromBuffer(bytes),
-      transform: _version);
-
-  Future<proto.VersionResponse> _version(proto.VersionRequest request) async {
-    final apiResponse = await _impl.version();
-
-    return proto.VersionResponse()
-      ..sdkVersion = apiResponse.sdkVersion
-      ..sdkVersionFull = apiResponse.sdkVersionFull
-      ..runtimeVersion = apiResponse.runtimeVersion
-      ..appEngineVersion = apiResponse.appEngineVersion
-      ..servicesVersion = apiResponse.servicesVersion
-      ..flutterDartVersion = apiResponse.flutterDartVersion
-      ..flutterDartVersionFull = apiResponse.flutterDartVersionFull
-      ..flutterVersion = apiResponse.flutterVersion;
-  }
-
-  proto.CandidateFix _transformCandidateFix(api.CandidateFix candidateFix) {
-    final result = proto.CandidateFix()..message = candidateFix.message;
-    if (candidateFix.edits != null) {
-      result.edits.addAll(
-        candidateFix.edits.map(
-          (edit) => proto.SourceEdit()
-            ..offset = edit.offset
-            ..length = edit.length
-            ..replacement = edit.replacement,
-        ),
-      );
-    }
-    if (candidateFix.linkedEditGroups != null) {
-      result.linkedEditGroups.addAll(
-        candidateFix.linkedEditGroups.map(
-          (group) => proto.LinkedEditGroup()
-            ..positions.addAll(group.positions)
-            ..length = group.length
-            ..suggestions.addAll(
-              group.suggestions.map(
-                (suggestion) => proto.LinkedEditSuggestion()
-                  ..value = suggestion.value
-                  ..kind = suggestion.kind,
-              ),
-            ),
-        ),
-      );
-    }
-    if (candidateFix.selectionOffset != null) {
-      result.selectionOffset = candidateFix.selectionOffset;
-    }
-    return result;
-  }
-
-  Router get router => _$CommonServerProtoRouter(this);
-
-  // We are serving requests that are arriving in both Protobuf binary encoding,
-  // and Protobuf JSON encoding. To handle this we need the ability to decode
-  // the requests and encode the responses. We also need to know how to do the
-  // work the request is requesting.
-
-  Future<Response> _processRequest<I, O extends GeneratedMessage>(
-    Request request, {
-    @required I Function(List<int> bytes) decodeFromProto,
-    @required I Function(Object json) decodeFromJSON,
-    @required Future<O> Function(I input) transform,
-  }) async {
-    if (request.mimeType == PROTOBUF_CONTENT_TYPE) {
-      // Dealing with binary Protobufs
-      final body = <int>[];
-      await for (final chunk in request.read()) {
-        body.addAll(chunk);
-      }
-      try {
-        final response = await transform(decodeFromProto(body));
-        return Response.ok(
-          response.writeToBuffer(),
-          headers: _PROTOBUF_HEADERS,
-        );
-      } on BadRequest catch (e) {
-        return Response(400,
-            headers: _PROTOBUF_HEADERS,
-            body: (proto.BadRequest.create()
-                  ..error = (proto.ErrorMessage.create()..message = e.cause))
-                .writeToBuffer());
-      }
-    } else {
-      // Dealing with JSON encoded Protobufs
-      final body = await request.readAsString();
-      try {
-        final response = await transform(
-            decodeFromJSON(body.isNotEmpty ? json.decode(body) : {}));
-        return Response.ok(
-          _jsonEncoder.convert(response.toProto3Json()),
-          encoding: utf8,
-          headers: _JSON_HEADERS,
-        );
-      } on BadRequest catch (e) {
-        return Response(400,
-            headers: _JSON_HEADERS,
-            encoding: utf8,
-            body: _jsonEncoder.convert((proto.BadRequest.create()
-                  ..error = (proto.ErrorMessage.create()..message = e.cause))
-                .toProto3Json()));
-      }
-    }
-  }
-
-  final JsonEncoder _jsonEncoder = JsonEncoder.withIndent(' ');
-
-  static const _JSON_HEADERS = {
-    'Access-Control-Allow-Origin': '*',
-    'Content-Type': JSON_CONTENT_TYPE
-  };
-
-  static const _PROTOBUF_HEADERS = {
-    'Access-Control-Allow-Origin': '*',
-    'Content-Type': PROTOBUF_CONTENT_TYPE
-  };
-}
diff --git a/lib/src/common_server_proto.g.dart b/lib/src/common_server_proto.g.dart
deleted file mode 100644
index 64e5fef..0000000
--- a/lib/src/common_server_proto.g.dart
+++ /dev/null
@@ -1,22 +0,0 @@
-// GENERATED CODE - DO NOT MODIFY BY HAND
-
-part of services.common_server_proto;
-
-// **************************************************************************
-// ShelfRouterGenerator
-// **************************************************************************
-
-Router _$CommonServerProtoRouter(CommonServerProto service) {
-  final router = Router();
-  router.add('POST', r'/api/dartservices/v2/analyze', service.analyze);
-  router.add('POST', r'/api/dartservices/v2/compile', service.compile);
-  router.add('POST', r'/api/dartservices/v2/compileDDC', service.compileDDC);
-  router.add('POST', r'/api/dartservices/v2/complete', service.complete);
-  router.add('POST', r'/api/dartservices/v2/fixes', service.fixes);
-  router.add('POST', r'/api/dartservices/v2/assists', service.assists);
-  router.add('POST', r'/api/dartservices/v2/format', service.format);
-  router.add('POST', r'/api/dartservices/v2/document', service.document);
-  router.add('POST', r'/api/dartservices/v2/version', service.versionPost);
-  router.add('GET', r'/api/dartservices/v2/version', service.versionGet);
-  return router;
-}
diff --git a/lib/src/protos/dart_services.pb.dart b/lib/src/protos/dart_services.pb.dart
index 63fc84f..d53e751 100644
--- a/lib/src/protos/dart_services.pb.dart
+++ b/lib/src/protos/dart_services.pb.dart
@@ -64,6 +64,48 @@
   void clearReturnSourceMap() => clearField(2);
 }
 
+class CompileDDCRequest extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo('CompileDDCRequest',
+      package: const $pb.PackageName('dart_services.api'),
+      createEmptyInstance: create)
+    ..aOS(1, 'source')
+    ..hasRequiredFields = false;
+
+  CompileDDCRequest._() : super();
+  factory CompileDDCRequest() => create();
+  factory CompileDDCRequest.fromBuffer($core.List<$core.int> i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromBuffer(i, r);
+  factory CompileDDCRequest.fromJson($core.String i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromJson(i, r);
+  CompileDDCRequest clone() => CompileDDCRequest()..mergeFromMessage(this);
+  CompileDDCRequest copyWith(void Function(CompileDDCRequest) updates) =>
+      super.copyWith((message) => updates(message as CompileDDCRequest));
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static CompileDDCRequest create() => CompileDDCRequest._();
+  CompileDDCRequest createEmptyInstance() => create();
+  static $pb.PbList<CompileDDCRequest> createRepeated() =>
+      $pb.PbList<CompileDDCRequest>();
+  @$core.pragma('dart2js:noInline')
+  static CompileDDCRequest getDefault() => _defaultInstance ??=
+      $pb.GeneratedMessage.$_defaultFor<CompileDDCRequest>(create);
+  static CompileDDCRequest _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.String get source => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set source($core.String v) {
+    $_setString(0, v);
+  }
+
+  @$pb.TagNumber(1)
+  $core.bool hasSource() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearSource() => clearField(1);
+}
+
 class SourceRequest extends $pb.GeneratedMessage {
   static final $pb.BuilderInfo _i = $pb.BuilderInfo('SourceRequest',
       package: const $pb.PackageName('dart_services.api'),
diff --git a/lib/src/protos/dart_services.pbjson.dart b/lib/src/protos/dart_services.pbjson.dart
index aa7d708..0834843 100644
--- a/lib/src/protos/dart_services.pbjson.dart
+++ b/lib/src/protos/dart_services.pbjson.dart
@@ -13,6 +13,13 @@
   ],
 };
 
+const CompileDDCRequest$json = {
+  '1': 'CompileDDCRequest',
+  '2': [
+    {'1': 'source', '3': 1, '4': 1, '5': 9, '10': 'source'},
+  ],
+};
+
 const SourceRequest$json = {
   '1': 'SourceRequest',
   '2': [
diff --git a/lib/src/server_cache.dart b/lib/src/server_cache.dart
index 3bddaf4..538c71a 100644
--- a/lib/src/server_cache.dart
+++ b/lib/src/server_cache.dart
@@ -11,7 +11,7 @@
 import 'package:pedantic/pedantic.dart';
 import 'package:quiver/cache.dart';
 
-import 'common_server.dart' show log;
+import 'common_server_impl.dart' show log;
 import 'sdk_manager.dart';
 
 abstract class ServerCache {
@@ -121,7 +121,7 @@
             _reconnect();
           });
         })
-        .timeout(Duration(milliseconds: _connectionRetryMaxMs))
+        .timeout(const Duration(milliseconds: _connectionRetryMaxMs))
         .catchError((_) {
           log.severe(
               '$_logPrefix: Unable to connect to redis server, reconnecting in ${nextRetryMs}ms ...');
diff --git a/lib/src/summarize.dart b/lib/src/summarize.dart
index 5a1833d..8062120 100644
--- a/lib/src/summarize.dart
+++ b/lib/src/summarize.dart
@@ -6,7 +6,7 @@
 
 import 'package:crypto/crypto.dart';
 
-import 'api_classes.dart';
+import 'protos/dart_services.pb.dart' as proto;
 
 /// Instances of this class take string input of dart code as well as an
 /// analysis result, and output a text description ofthe code's size, packages,
@@ -15,7 +15,7 @@
   final String dart;
   final String html;
   final String css;
-  final AnalysisResults analysis;
+  final proto.AnalysisResults analysis;
 
   _SummarizeToken storage;
   int _randomizer;
@@ -276,15 +276,15 @@
 
   List<String> packageImports;
 
-  List<AnalysisIssue> errors;
+  List<proto.AnalysisIssue> errors;
 
-  _SummarizeToken(String input, {AnalysisResults analysis}) {
+  _SummarizeToken(String input, {proto.AnalysisResults analysis}) {
     linesCode = _linesOfCode(input);
     if (analysis != null) {
-      errorPresent =
-          analysis.issues.any((AnalysisIssue issue) => issue.kind == 'error');
-      warningPresent =
-          analysis.issues.any((AnalysisIssue issue) => issue.kind == 'warning');
+      errorPresent = analysis.issues
+          .any((proto.AnalysisIssue issue) => issue.kind == 'error');
+      warningPresent = analysis.issues
+          .any((proto.AnalysisIssue issue) => issue.kind == 'warning');
       packageCount = analysis.packageImports.length;
       packageImports = analysis.packageImports;
       errors = analysis.issues;
diff --git a/protos/dart_services.proto b/protos/dart_services.proto
index 06f664a..aa20424 100644
--- a/protos/dart_services.proto
+++ b/protos/dart_services.proto
@@ -14,6 +14,11 @@
   bool returnSourceMap = 2;
 }
 
+message CompileDDCRequest {
+  // The Dart source.
+  string source = 1;
+}
+
 message SourceRequest {
   // The Dart source.
   string source = 1;
diff --git a/pubspec.lock b/pubspec.lock
index 046fac5..1c54f7d 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -2,7 +2,7 @@
 # See https://dart.dev/tools/pub/glossary#lockfile
 packages:
   _discoveryapis_commons:
-    dependency: "direct main"
+    dependency: transitive
     description:
       name: _discoveryapis_commons
       url: "https://pub.dartlang.org"
@@ -14,7 +14,7 @@
       name: _fe_analyzer_shared
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.1.0"
+    version: "2.2.0"
   analysis_server_lib:
     dependency: "direct main"
     description:
@@ -28,7 +28,7 @@
       name: analyzer
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.39.6"
+    version: "0.39.7"
   appengine:
     dependency: "direct main"
     description:
@@ -91,7 +91,7 @@
       name: build_resolvers
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.3.4"
+    version: "1.3.6"
   build_runner:
     dependency: "direct dev"
     description:
@@ -204,13 +204,6 @@
       url: "https://pub.dartlang.org"
     source: hosted
     version: "0.4.0"
-  discoveryapis_generator:
-    dependency: "direct dev"
-    description:
-      name: discoveryapis_generator
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.9.11"
   fixnum:
     dependency: transitive
     description:
@@ -422,7 +415,7 @@
       name: path
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.6.4"
+    version: "1.7.0"
   pedantic:
     dependency: "direct dev"
     description:
@@ -465,13 +458,6 @@
       url: "https://pub.dartlang.org"
     source: hosted
     version: "2.1.3"
-  rpc:
-    dependency: "direct main"
-    description:
-      name: rpc
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.6.2"
   shelf:
     dependency: "direct main"
     description:
@@ -626,20 +612,6 @@
       url: "https://pub.dartlang.org"
     source: hosted
     version: "1.1.6"
-  uri:
-    dependency: transitive
-    description:
-      name: uri
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.11.3+1"
-  utf:
-    dependency: transitive
-    description:
-      name: utf
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.9.0+5"
   uuid:
     dependency: "direct main"
     description:
@@ -653,7 +625,7 @@
       name: vm_service
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "4.0.0"
+    version: "4.0.1"
   watcher:
     dependency: transitive
     description:
diff --git a/pubspec.yaml b/pubspec.yaml
index 69e4fd8..94456d9 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -6,7 +6,6 @@
   sdk: ^2.7.2
 
 dependencies:
-  _discoveryapis_commons: 0.1.9
   analyzer: ^0.39.0
   analysis_server_lib: ^0.1.4
   appengine: ^0.10.3
@@ -20,7 +19,6 @@
   path: ^1.6.2
   protobuf: ^1.0.1
   quiver: ^2.0.3
-  rpc: ^0.6.2
   shelf: ^0.7.5
   shelf_router: ^0.7.0+1
   uuid: ^2.0.0
@@ -31,7 +29,6 @@
   build_runner: ^1.3.1
   codemirror: ^0.5.6
   coverage: ^0.13.0
-  discoveryapis_generator: ^0.9.11
   grinder: ^0.8.0
   # TODO(brettmorgan): return to mock_request pub package after it is
   # published with https://github.com/thosakwe/mock_request/pull/3
diff --git a/test/all.dart b/test/all.dart
index 59fd367..4fb2e80 100644
--- a/test/all.dart
+++ b/test/all.dart
@@ -5,12 +5,10 @@
 library services.all_test;
 
 import 'analysis_server_test.dart' as analysis_server_test;
-import 'api_classes_test.dart' as api_classes_test;
 import 'bench_test.dart' as bench_test;
-import 'common_server_api_v1_test.dart' as common_server_api_v1_test;
-import 'common_server_api_v2_protobuf_test.dart'
-    as common_server_api_v2_protobuf_test;
-import 'common_server_api_v2_test.dart' as common_server_api_v2_test;
+import 'common_server_api_protobuf_test.dart'
+    as common_server_api_protobuf_test;
+import 'common_server_api_test.dart' as common_server_api_test;
 import 'common_test.dart' as common_test;
 import 'compiler_test.dart' as compiler_test;
 import 'flutter_web_test.dart' as flutter_web_test;
@@ -22,11 +20,9 @@
 
 void main() async {
   analysis_server_test.defineTests();
-  api_classes_test.defineTests();
   bench_test.defineTests();
-  common_server_api_v1_test.defineTests();
-  common_server_api_v2_test.defineTests();
-  common_server_api_v2_protobuf_test.defineTests();
+  common_server_api_test.defineTests();
+  common_server_api_protobuf_test.defineTests();
   common_test.defineTests();
   compiler_test.defineTests();
   flutter_web_test.defineTests();
diff --git a/test/analysis_server_test.dart b/test/analysis_server_test.dart
index f290954..bd0dd7b 100644
--- a/test/analysis_server_test.dart
+++ b/test/analysis_server_test.dart
@@ -5,7 +5,7 @@
 library services.analyzer_server_test;
 
 import 'package:dart_services/src/analysis_server.dart';
-import 'package:dart_services/src/api_classes.dart';
+import 'package:dart_services/src/protos/dart_services.pb.dart' as proto;
 import 'package:dart_services/src/common.dart';
 import 'package:dart_services/src/flutter_web.dart';
 import 'package:dart_services/src/sdk_manager.dart';
@@ -75,9 +75,7 @@
 
     test('simple_completion', () {
       // Just after i.
-      return analysisServer
-          .complete(completionCode, 32)
-          .then((CompleteResponse results) {
+      return analysisServer.complete(completionCode, 32).then((results) {
         expect(results.replacementLength, 0);
         expect(results.replacementOffset, 32);
         expect(completionsContains(results, 'abs'), true);
@@ -87,12 +85,10 @@
 
     test('repro #126 - completions polluted on second request', () {
       // https://github.com/dart-lang/dart-services/issues/126
-      return analysisServer
-          .complete(completionFilterCode, 17)
-          .then((CompleteResponse results) {
+      return analysisServer.complete(completionFilterCode, 17).then((results) {
         return analysisServer
             .complete(completionFilterCode, 17)
-            .then((CompleteResponse results) {
+            .then((results) {
           expect(results.replacementLength, 2);
           expect(results.replacementOffset, 16);
           expect(completionsContains(results, 'print'), true);
@@ -104,11 +100,9 @@
     test('import_test', () {
       final testCode = "import '/'; main() { int a = 0; a. }";
 
-      return analysisServer
-          .complete(testCode, 9)
-          .then((CompleteResponse results) {
-        expect(results.completions.every((Map<String, String> completion) {
-          return completion['completion'].startsWith('dart:');
+      return analysisServer.complete(testCode, 9).then((results) {
+        expect(results.completions.every((completion) {
+          return completion.completion['completion'].startsWith('dart:');
         }), true);
       });
     });
@@ -116,9 +110,7 @@
     test('import_and_other_test', () {
       final testCode = "import '/'; main() { int a = 0; a. }";
 
-      return analysisServer
-          .complete(testCode, 34)
-          .then((CompleteResponse results) {
+      return analysisServer.complete(testCode, 34).then((results) {
         expect(completionsContains(results, 'abs'), true);
       });
     });
@@ -216,5 +208,6 @@
   });
 }
 
-bool completionsContains(CompleteResponse response, String completion) =>
-    response.completions.any((map) => map['completion'] == completion);
+bool completionsContains(proto.CompleteResponse response, String expected) =>
+    response.completions
+        .any((completion) => completion.completion['completion'] == expected);
diff --git a/test/api_classes_test.dart b/test/api_classes_test.dart
deleted file mode 100644
index b947376..0000000
--- a/test/api_classes_test.dart
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-library services.api_classes_test;
-
-import 'package:dart_services/src/api_classes.dart';
-import 'package:test/test.dart';
-
-void main() => defineTests();
-
-void defineTests() {
-  group('AnalysisIssue', () {
-    test('toMap', () {
-      final issue =
-          AnalysisIssue.fromIssue('error', 1, 'not found', charStart: 123);
-      final m = issue.toMap();
-      expect(m['kind'], 'error');
-      expect(m['line'], 1);
-      expect(m['message'], isNotNull);
-      expect(m['charStart'], isNotNull);
-      expect(m['charLength'], isNull);
-    });
-
-    test('toString', () {
-      final issue = AnalysisIssue.fromIssue('error', 1, 'not found');
-      expect(issue.toString(), isNotNull);
-    });
-  });
-}
diff --git a/test/common_server_api_v2_protobuf_test.dart b/test/common_server_api_protobuf_test.dart
similarity index 96%
rename from test/common_server_api_v2_protobuf_test.dart
rename to test/common_server_api_protobuf_test.dart
index b24f0c8..c4aa222 100644
--- a/test/common_server_api_v2_protobuf_test.dart
+++ b/test/common_server_api_protobuf_test.dart
@@ -2,15 +2,14 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-library services.common_server_api_v2_protobuf_test;
+library services.common_server_api_protobuf_test;
 
 import 'dart:async';
 import 'dart:convert';
 
 import 'package:dart_services/src/common.dart';
-import 'package:dart_services/src/common_server.dart';
 import 'package:dart_services/src/common_server_impl.dart';
-import 'package:dart_services/src/common_server_proto.dart';
+import 'package:dart_services/src/common_server_api.dart';
 import 'package:dart_services/src/flutter_web.dart';
 import 'package:dart_services/src/sdk_manager.dart';
 import 'package:dart_services/src/server_cache.dart';
@@ -57,7 +56,7 @@
 void main() => defineTests();
 
 void defineTests() {
-  CommonServerProto commonServerProto;
+  CommonServerApi commonServerApi;
   CommonServerImpl commonServerImpl;
   FlutterWebManager flutterWebManager;
 
@@ -68,25 +67,25 @@
     String path,
     GeneratedMessage message,
   ) async {
-    assert(commonServerProto != null);
+    assert(commonServerApi != null);
     final uri = Uri.parse('/api/$path');
     final request = MockHttpRequest('POST', uri);
     request.headers.add('content-type', JSON_CONTENT_TYPE);
     request.add(utf8.encode(json.encode(message.toProto3Json())));
     await request.close();
-    await shelf_io.handleRequest(request, commonServerProto.router.handler);
+    await shelf_io.handleRequest(request, commonServerApi.router.handler);
     return request.response;
   }
 
   Future<MockHttpResponse> _sendGetRequest(
     String path,
   ) async {
-    assert(commonServerProto != null);
+    assert(commonServerApi != null);
     final uri = Uri.parse('/api/$path');
     final request = MockHttpRequest('POST', uri);
     request.headers.add('content-type', JSON_CONTENT_TYPE);
     await request.close();
-    await shelf_io.handleRequest(request, commonServerProto.router.handler);
+    await shelf_io.handleRequest(request, commonServerApi.router.handler);
     return request.response;
   }
 
@@ -97,7 +96,7 @@
       flutterWebManager = FlutterWebManager(SdkManager.flutterSdk);
       commonServerImpl =
           CommonServerImpl(sdkPath, flutterWebManager, container, cache);
-      commonServerProto = CommonServerProto(commonServerImpl);
+      commonServerApi = CommonServerApi(commonServerImpl);
       await commonServerImpl.init();
 
       // Some piece of initialization doesn't always happen fast enough for this
diff --git a/test/common_server_api_test.dart b/test/common_server_api_test.dart
new file mode 100644
index 0000000..08e9839
--- /dev/null
+++ b/test/common_server_api_test.dart
@@ -0,0 +1,466 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library services.common_server_api_test;
+
+import 'dart:async';
+import 'dart:convert';
+
+import 'package:dart_services/src/common.dart';
+import 'package:dart_services/src/common_server_impl.dart';
+import 'package:dart_services/src/common_server_api.dart';
+import 'package:dart_services/src/flutter_web.dart';
+import 'package:dart_services/src/sdk_manager.dart';
+import 'package:dart_services/src/server_cache.dart';
+import 'package:logging/logging.dart';
+import 'package:mock_request/mock_request.dart';
+import 'package:shelf/shelf_io.dart' as shelf_io;
+import 'package:test/test.dart';
+
+const versions = ['v1', 'v2'];
+
+const quickFixesCode = r'''
+import 'dart:async';
+void main() {
+  int i = 0;
+}
+''';
+
+const preFormattedCode = r'''
+void main()
+{
+int i = 0;
+}
+''';
+
+const postFormattedCode = r'''
+void main() {
+  int i = 0;
+}
+''';
+
+const formatBadCode = r'''
+void main()
+{
+  print('foo')
+}
+''';
+
+const assistCode = r'''
+main() {
+  int v = 0;
+}
+''';
+
+void main() => defineTests();
+
+void defineTests() {
+  CommonServerApi commonServerApi;
+  CommonServerImpl commonServerImpl;
+  FlutterWebManager flutterWebManager;
+
+  MockContainer container;
+  MockCache cache;
+
+  Future<MockHttpResponse> _sendPostRequest(
+    String path,
+    dynamic jsonData,
+  ) async {
+    assert(commonServerApi != null);
+    final uri = Uri.parse('/api/$path');
+    final request = MockHttpRequest('POST', uri);
+    request.headers.add('content-type', JSON_CONTENT_TYPE);
+    request.add(utf8.encode(json.encode(jsonData)));
+    await request.close();
+    await shelf_io.handleRequest(request, commonServerApi.router.handler);
+    return request.response;
+  }
+
+  Future<MockHttpResponse> _sendGetRequest(
+    String path,
+  ) async {
+    assert(commonServerApi != null);
+    final uri = Uri.parse('/api/$path');
+    final request = MockHttpRequest('POST', uri);
+    request.headers.add('content-type', JSON_CONTENT_TYPE);
+    await request.close();
+    await shelf_io.handleRequest(request, commonServerApi.router.handler);
+    return request.response;
+  }
+
+  group('CommonServerProto JSON', () {
+    setUpAll(() async {
+      container = MockContainer();
+      cache = MockCache();
+      flutterWebManager = FlutterWebManager(SdkManager.flutterSdk);
+      commonServerImpl =
+          CommonServerImpl(sdkPath, flutterWebManager, container, cache);
+      commonServerApi = CommonServerApi(commonServerImpl);
+      await commonServerImpl.init();
+
+      // Some piece of initialization doesn't always happen fast enough for this
+      // request to work in time for the test. So try it here until the server
+      // returns something valid.
+      // TODO(jcollins-g): determine which piece of initialization isn't
+      // happening and deal with that in warmup/init.
+      {
+        var decodedJson = {};
+        final jsonData = {'source': sampleCodeError};
+        while (decodedJson.isEmpty) {
+          final response =
+              await _sendPostRequest('dartservices/v2/analyze', jsonData);
+          expect(response.statusCode, 200);
+          expect(response.headers['content-type'],
+              ['application/json; charset=utf-8']);
+          final data = await response.transform(utf8.decoder).join();
+          decodedJson = json.decode(data) as Map<dynamic, dynamic>;
+        }
+      }
+    });
+
+    tearDownAll(() async {
+      await commonServerImpl.shutdown();
+    });
+
+    setUp(() {
+      log.onRecord.listen((LogRecord rec) {
+        print('${rec.level.name}: ${rec.time}: ${rec.message}');
+      });
+    });
+
+    tearDown(log.clearListeners);
+
+    test('analyze Dart', () async {
+      for (final version in versions) {
+        final jsonData = {'source': sampleCode};
+        final response =
+            await _sendPostRequest('dartservices/$version/analyze', jsonData);
+        expect(response.statusCode, 200);
+        final data = await response.transform(utf8.decoder).join();
+        expect(json.decode(data), {});
+      }
+    });
+
+    test('analyze Flutter', () async {
+      for (final version in versions) {
+        final jsonData = {'source': sampleCodeFlutter};
+        final response =
+            await _sendPostRequest('dartservices/$version/analyze', jsonData);
+        expect(response.statusCode, 200);
+        final data = await response.transform(utf8.decoder).join();
+        expect(json.decode(data), {
+          'packageImports': ['flutter']
+        });
+      }
+    });
+
+    test('analyze errors', () async {
+      for (final version in versions) {
+        final jsonData = {'source': sampleCodeError};
+        final response =
+            await _sendPostRequest('dartservices/$version/analyze', jsonData);
+        expect(response.statusCode, 200);
+        expect(response.headers['content-type'],
+            ['application/json; charset=utf-8']);
+        final data = await response.transform(utf8.decoder).join();
+        final expectedJson = {
+          'issues': [
+            {
+              'kind': 'error',
+              'line': 2,
+              'sourceName': 'main.dart',
+              'message': "Expected to find ';'.",
+              'hasFixes': true,
+              'charStart': 29,
+              'charLength': 1
+            }
+          ]
+        };
+        expect(json.decode(data), expectedJson);
+      }
+    });
+
+    test('analyze negative-test noSource', () async {
+      for (final version in versions) {
+        final jsonData = {};
+        final response =
+            await _sendPostRequest('dartservices/$version/analyze', jsonData);
+        expect(response.statusCode, 400);
+      }
+    });
+
+    test('compile', () async {
+      for (final version in versions) {
+        final jsonData = {'source': sampleCode};
+        final response =
+            await _sendPostRequest('dartservices/$version/compile', jsonData);
+        expect(response.statusCode, 200);
+        final data = await response.transform(utf8.decoder).join();
+        expect(json.decode(data), isNotEmpty);
+      }
+    });
+
+    test('compile error', () async {
+      for (final version in versions) {
+        final jsonData = {'source': sampleCodeError};
+        final response =
+            await _sendPostRequest('dartservices/$version/compile', jsonData);
+        expect(response.statusCode, 400);
+        final data = json.decode(await response.transform(utf8.decoder).join());
+        expect(data, isNotEmpty);
+        expect(data['error']['message'], contains('Error: Expected'));
+      }
+    });
+
+    test('compile negative-test noSource', () async {
+      for (final version in versions) {
+        final jsonData = {};
+        final response =
+            await _sendPostRequest('dartservices/$version/compile', jsonData);
+        expect(response.statusCode, 400);
+      }
+    });
+
+    test('compileDDC', () async {
+      for (final version in versions) {
+        final jsonData = {'source': sampleCode};
+        final response = await _sendPostRequest(
+            'dartservices/$version/compileDDC', jsonData);
+        expect(response.statusCode, 200);
+        final data = await response.transform(utf8.decoder).join();
+        expect(json.decode(data), isNotEmpty);
+      }
+    });
+
+    test('complete', () async {
+      for (final version in versions) {
+        final jsonData = {'source': 'void main() {print("foo");}', 'offset': 1};
+        final response =
+            await _sendPostRequest('dartservices/$version/complete', jsonData);
+        expect(response.statusCode, 200);
+        final data = json.decode(await response.transform(utf8.decoder).join());
+        expect(data, isNotEmpty);
+      }
+    });
+
+    test('complete no data', () async {
+      for (final version in versions) {
+        final response =
+            await _sendPostRequest('dartservices/$version/complete', {});
+        expect(response.statusCode, 400);
+      }
+    });
+
+    test('complete param missing', () async {
+      for (final version in versions) {
+        final jsonData = {'offset': 1};
+        final response =
+            await _sendPostRequest('dartservices/$version/complete', jsonData);
+        expect(response.statusCode, 400);
+      }
+    });
+
+    test('complete param missing 2', () async {
+      for (final version in versions) {
+        final jsonData = {'source': 'void main() {print("foo");}'};
+        final response =
+            await _sendPostRequest('dartservices/$version/complete', jsonData);
+        expect(response.statusCode, 400);
+        final data = json.decode(await response.transform(utf8.decoder).join());
+        expect(data['error']['message'], 'Missing parameter: \'offset\'');
+      }
+    });
+
+    test('document', () async {
+      for (final version in versions) {
+        final jsonData = {
+          'source': 'void main() {print("foo");}',
+          'offset': 17
+        };
+        final response =
+            await _sendPostRequest('dartservices/$version/document', jsonData);
+        expect(response.statusCode, 200);
+        final data = json.decode(await response.transform(utf8.decoder).join());
+        expect(data, isNotEmpty);
+      }
+    });
+
+    test('document little data', () async {
+      for (final version in versions) {
+        final jsonData = {'source': 'void main() {print("foo");}', 'offset': 2};
+        final response =
+            await _sendPostRequest('dartservices/$version/document', jsonData);
+        expect(response.statusCode, 200);
+        final data = json.decode(await response.transform(utf8.decoder).join());
+        expect(data, {
+          'info': {},
+        });
+      }
+    });
+
+    test('document no data', () async {
+      for (final version in versions) {
+        final jsonData = {
+          'source': 'void main() {print("foo");}',
+          'offset': 12
+        };
+        final response =
+            await _sendPostRequest('dartservices/$version/document', jsonData);
+        expect(response.statusCode, 200);
+        final data = json.decode(await response.transform(utf8.decoder).join());
+        expect(data, {'info': {}});
+      }
+    });
+
+    test('document negative-test noSource', () async {
+      for (final version in versions) {
+        final jsonData = {'offset': 12};
+        final response =
+            await _sendPostRequest('dartservices/$version/document', jsonData);
+        expect(response.statusCode, 400);
+      }
+    });
+
+    test('document negative-test noOffset', () async {
+      for (final version in versions) {
+        final jsonData = {'source': 'void main() {print("foo");}'};
+        final response =
+            await _sendPostRequest('dartservices/$version/document', jsonData);
+        expect(response.statusCode, 400);
+      }
+    });
+
+    test('format', () async {
+      for (final version in versions) {
+        final jsonData = {'source': preFormattedCode};
+        final response =
+            await _sendPostRequest('dartservices/$version/format', jsonData);
+        expect(response.statusCode, 200);
+        final data = json.decode(await response.transform(utf8.decoder).join());
+        expect(data['newString'], postFormattedCode);
+      }
+    });
+
+    test('format bad code', () async {
+      for (final version in versions) {
+        final jsonData = {'source': formatBadCode};
+        final response =
+            await _sendPostRequest('dartservices/$version/format', jsonData);
+        expect(response.statusCode, 200);
+        final data = json.decode(await response.transform(utf8.decoder).join());
+        expect(data['newString'], formatBadCode);
+      }
+    });
+
+    test('format position', () async {
+      for (final version in versions) {
+        final jsonData = {'source': preFormattedCode, 'offset': 21};
+        final response =
+            await _sendPostRequest('dartservices/$version/format', jsonData);
+        expect(response.statusCode, 200);
+        final data = json.decode(await response.transform(utf8.decoder).join());
+        expect(data['newString'], postFormattedCode);
+        expect(data['offset'], 24);
+      }
+    });
+
+    test('fix', () async {
+      for (final version in versions) {
+        final jsonData = {'source': quickFixesCode, 'offset': 10};
+        final response =
+            await _sendPostRequest('dartservices/$version/fixes', jsonData);
+        expect(response.statusCode, 200);
+        final data = json.decode(await response.transform(utf8.decoder).join());
+        final fixes = data['fixes'];
+        expect(fixes.length, 1);
+        final problemAndFix = fixes[0];
+        expect(problemAndFix['problemMessage'], isNotNull);
+      }
+    });
+
+    test('fixes completeness', () async {
+      for (final version in versions) {
+        final jsonData = {
+          'source': '''
+void main() {
+  for (int i = 0; i < 4; i++) {
+    print('hello \$i')
+  }
+}
+''',
+          'offset': 67,
+        };
+        final response =
+            await _sendPostRequest('dartservices/$version/fixes', jsonData);
+        expect(response.statusCode, 200);
+        final data = json.decode(await response.transform(utf8.decoder).join());
+        expect(data, {
+          'fixes': [
+            {
+              'fixes': [
+                {
+                  'message': "Insert ';'",
+                  'edits': [
+                    {'offset': 67, 'length': 0, 'replacement': ';'}
+                  ]
+                }
+              ],
+              'problemMessage': "Expected to find ';'.",
+              'offset': 66,
+              'length': 1
+            }
+          ]
+        });
+      }
+    });
+
+    test('assist', () async {
+      for (final version in versions) {
+        final jsonData = {'source': assistCode, 'offset': 15};
+        final response =
+            await _sendPostRequest('dartservices/$version/assists', jsonData);
+        expect(response.statusCode, 200);
+
+        final data = json.decode(await response.transform(utf8.decoder).join());
+        final assists = data['assists'] as List;
+        expect(assists, hasLength(2));
+        expect(assists.first['edits'], isNotNull);
+        expect(assists.first['edits'], hasLength(1));
+        expect(assists.where((m) {
+          final map = m as Map<String, dynamic>;
+          return map['message'] == 'Remove type annotation';
+        }), isNotEmpty);
+      }
+    });
+
+    test('version', () async {
+      for (final version in versions) {
+        final response = await _sendGetRequest('dartservices/$version/version');
+        expect(response.statusCode, 200);
+        final data = json.decode(await response.transform(utf8.decoder).join());
+        expect(data['sdkVersion'], isNotNull);
+        expect(data['runtimeVersion'], isNotNull);
+      }
+    });
+  });
+}
+
+class MockContainer implements ServerContainer {
+  @override
+  String get version => vmVersion;
+}
+
+class MockCache implements ServerCache {
+  @override
+  Future<String> get(String key) => Future.value(null);
+
+  @override
+  Future set(String key, String value, {Duration expiration}) => Future.value();
+
+  @override
+  Future remove(String key) => Future.value();
+
+  @override
+  Future<void> shutdown() => Future.value();
+}
diff --git a/test/common_server_api_v1_test.dart b/test/common_server_api_v1_test.dart
deleted file mode 100644
index f7fdad2..0000000
--- a/test/common_server_api_v1_test.dart
+++ /dev/null
@@ -1,408 +0,0 @@
-// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-library services.common_server_api_v1_test;
-
-import 'dart:async';
-import 'dart:convert';
-
-import 'package:dart_services/src/common.dart';
-import 'package:dart_services/src/common_server.dart';
-import 'package:dart_services/src/common_server_impl.dart';
-import 'package:dart_services/src/flutter_web.dart';
-import 'package:dart_services/src/sdk_manager.dart';
-import 'package:dart_services/src/server_cache.dart';
-import 'package:logging/logging.dart';
-import 'package:rpc/rpc.dart';
-import 'package:test/test.dart';
-
-const quickFixesCode = r'''
-import 'dart:async';
-void main() {
-  int i = 0;
-}
-''';
-
-const preFormattedCode = r'''
-void main()
-{
-int i = 0;
-}
-''';
-
-const postFormattedCode = r'''
-void main() {
-  int i = 0;
-}
-''';
-
-const formatBadCode = r'''
-void main()
-{
-  print('foo')
-}
-''';
-
-const assistCode = r'''
-main() {
-  int v = 0;
-}
-''';
-
-void main() => defineTests();
-
-void defineTests() {
-  CommonServer server;
-  CommonServerImpl commonServerImpl;
-  ApiServer apiServer;
-  FlutterWebManager flutterWebManager;
-
-  MockContainer container;
-  MockCache cache;
-
-  Future<HttpApiResponse> _sendPostRequest(String path, jsonData) {
-    assert(apiServer != null);
-    final uri = Uri.parse('/api/$path');
-    final body = Stream.fromIterable([utf8.encode(json.encode(jsonData))]);
-    final request = HttpApiRequest(
-        'POST', uri, {'content-type': 'application/json; charset=utf-8'}, body);
-    return apiServer.handleHttpApiRequest(request);
-  }
-
-  Future<HttpApiResponse> _sendGetRequest(String path, [String queryParams]) {
-    assert(apiServer != null);
-    final uri = Uri.parse(
-        queryParams == null ? '/api/$path' : '/api/$path?$queryParams');
-    final body = Stream<List<int>>.fromIterable([]);
-    final request = HttpApiRequest(
-        'GET', uri, {'content-type': 'application/json; charset=utf-8'}, body);
-    return apiServer.handleHttpApiRequest(request);
-  }
-
-  group('CommonServer', () {
-    setUpAll(() async {
-      container = MockContainer();
-      cache = MockCache();
-      flutterWebManager = FlutterWebManager(SdkManager.flutterSdk);
-      commonServerImpl =
-          CommonServerImpl(sdkPath, flutterWebManager, container, cache);
-      server = CommonServer(commonServerImpl);
-      await commonServerImpl.init();
-
-      apiServer = ApiServer(apiPrefix: '/api', prettyPrint: true);
-      apiServer.addApi(server);
-
-      // Some piece of initialization doesn't always happen fast enough for this
-      // request to work in time for the test. So try it here until the server
-      // returns something valid.
-      // TODO(jcollins-g): determine which piece of initialization isn't
-      // happening and deal with that in warmup/init.
-      {
-        var decodedJson = {};
-        final jsonData = {'source': sampleCodeError};
-        while (decodedJson.isEmpty) {
-          final response =
-              await _sendPostRequest('dartservices/v1/analyze', jsonData);
-          expect(response.status, 200);
-          expect(response.headers['content-type'],
-              'application/json; charset=utf-8');
-          final data = await response.body.first;
-          decodedJson = json.decode(utf8.decode(data)) as Map<dynamic, dynamic>;
-        }
-      }
-    });
-
-    tearDownAll(() async {
-      await commonServerImpl.shutdown();
-    });
-
-    setUp(() {
-      log.onRecord.listen((LogRecord rec) {
-        print('${rec.level.name}: ${rec.time}: ${rec.message}');
-      });
-    });
-
-    tearDown(log.clearListeners);
-
-    test('analyze Dart', () async {
-      final jsonData = {'source': sampleCode};
-      final response =
-          await _sendPostRequest('dartservices/v1/analyze', jsonData);
-      expect(response.status, 200);
-      final data = await response.body.first;
-      expect(
-          json.decode(utf8.decode(data)), {'issues': [], 'packageImports': []});
-    });
-
-    test('analyze Flutter', () async {
-      final jsonData = {'source': sampleCodeFlutter};
-      final response =
-          await _sendPostRequest('dartservices/v1/analyze', jsonData);
-      expect(response.status, 200);
-      final data = await response.body.first;
-      expect(json.decode(utf8.decode(data)), {
-        'issues': [],
-        'packageImports': ['flutter']
-      });
-    });
-
-    test('analyze errors', () async {
-      final jsonData = {'source': sampleCodeError};
-      final response =
-          await _sendPostRequest('dartservices/v1/analyze', jsonData);
-      expect(response.status, 200);
-      expect(
-          response.headers['content-type'], 'application/json; charset=utf-8');
-      final data = await response.body.first;
-      final expectedJson = {
-        'issues': [
-          {
-            'kind': 'error',
-            'line': 2,
-            'sourceName': 'main.dart',
-            'message': "Expected to find ';'.",
-            'hasFixes': true,
-            'charStart': 29,
-            'charLength': 1
-          }
-        ],
-        'packageImports': []
-      };
-      expect(json.decode(utf8.decode(data)), expectedJson);
-    });
-
-    test('analyze negative-test noSource', () async {
-      final jsonData = {};
-      final response =
-          await _sendPostRequest('dartservices/v1/analyze', jsonData);
-      expect(response.status, 400);
-    });
-
-    test('compile', () async {
-      final jsonData = {'source': sampleCode};
-      final response =
-          await _sendPostRequest('dartservices/v1/compile', jsonData);
-      expect(response.status, 200);
-      final data = await response.body.first;
-      expect(json.decode(utf8.decode(data)), isNotEmpty);
-    });
-
-    test('compile error', () async {
-      final jsonData = {'source': sampleCodeError};
-      final response =
-          await _sendPostRequest('dartservices/v1/compile', jsonData);
-      expect(response.status, 400);
-      final data = json.decode(utf8.decode(await response.body.first));
-      expect(data, isNotEmpty);
-      expect(data['error']['message'], contains('Error: Expected'));
-    });
-
-    test('compile negative-test noSource', () async {
-      final jsonData = {};
-      final response =
-          await _sendPostRequest('dartservices/v1/compile', jsonData);
-      expect(response.status, 400);
-    });
-
-    test('compileDDC', () async {
-      final jsonData = {'source': sampleCode};
-      final response =
-          await _sendPostRequest('dartservices/v1/compileDDC', jsonData);
-      expect(response.status, 200);
-      final data = await response.body.first;
-      expect(json.decode(utf8.decode(data)), isNotEmpty);
-    });
-
-    test('complete', () async {
-      final jsonData = {'source': 'void main() {print("foo");}', 'offset': 1};
-      final response =
-          await _sendPostRequest('dartservices/v1/complete', jsonData);
-      expect(response.status, 200);
-      final data = json.decode(utf8.decode(await response.body.first));
-      expect(data, isNotEmpty);
-    });
-
-    test('complete no data', () async {
-      final response = await _sendPostRequest('dartservices/v1/complete', {});
-      expect(response.status, 400);
-    });
-
-    test('complete param missing', () async {
-      final jsonData = {'offset': 1};
-      final response =
-          await _sendPostRequest('dartservices/v1/complete', jsonData);
-      expect(response.status, 400);
-    });
-
-    test('complete param missing 2', () async {
-      final jsonData = {'source': 'void main() {print("foo");}'};
-      final response =
-          await _sendPostRequest('dartservices/v1/complete', jsonData);
-      expect(response.status, 400);
-      final data = json.decode(utf8.decode(await response.body.first));
-      expect(data['error']['message'], 'Missing parameter: \'offset\'');
-    });
-
-    test('document', () async {
-      final jsonData = {'source': 'void main() {print("foo");}', 'offset': 17};
-      final response =
-          await _sendPostRequest('dartservices/v1/document', jsonData);
-      expect(response.status, 200);
-      final data = json.decode(utf8.decode(await response.body.first));
-      expect(data, isNotEmpty);
-    });
-
-    test('document little data', () async {
-      final jsonData = {'source': 'void main() {print("foo");}', 'offset': 2};
-      final response =
-          await _sendPostRequest('dartservices/v1/document', jsonData);
-      expect(response.status, 200);
-      final data = json.decode(utf8.decode(await response.body.first));
-      expect(data, {
-        'info': {},
-      });
-    });
-
-    test('document no data', () async {
-      final jsonData = {'source': 'void main() {print("foo");}', 'offset': 12};
-      final response =
-          await _sendPostRequest('dartservices/v1/document', jsonData);
-      expect(response.status, 200);
-      final data = json.decode(utf8.decode(await response.body.first));
-      expect(data, {'info': {}});
-    });
-
-    test('document negative-test noSource', () async {
-      final jsonData = {'offset': 12};
-      final response =
-          await _sendPostRequest('dartservices/v1/document', jsonData);
-      expect(response.status, 400);
-    });
-
-    test('document negative-test noOffset', () async {
-      final jsonData = {'source': 'void main() {print("foo");}'};
-      final response =
-          await _sendPostRequest('dartservices/v1/document', jsonData);
-      expect(response.status, 400);
-    });
-
-    test('format', () async {
-      final jsonData = {'source': preFormattedCode};
-      final response =
-          await _sendPostRequest('dartservices/v1/format', jsonData);
-      expect(response.status, 200);
-      final data = json.decode(utf8.decode(await response.body.first));
-      expect(data['newString'], postFormattedCode);
-    });
-
-    test('format bad code', () async {
-      final jsonData = {'source': formatBadCode};
-      final response =
-          await _sendPostRequest('dartservices/v1/format', jsonData);
-      expect(response.status, 200);
-      final data = json.decode(utf8.decode(await response.body.first));
-      expect(data['newString'], formatBadCode);
-    });
-
-    test('format position', () async {
-      final jsonData = {'source': preFormattedCode, 'offset': 21};
-      final response =
-          await _sendPostRequest('dartservices/v1/format', jsonData);
-      expect(response.status, 200);
-      final data = json.decode(utf8.decode(await response.body.first));
-      expect(data['newString'], postFormattedCode);
-      expect(data['offset'], 24);
-    });
-
-    test('fix', () async {
-      final jsonData = {'source': quickFixesCode, 'offset': 10};
-      final response =
-          await _sendPostRequest('dartservices/v1/fixes', jsonData);
-      expect(response.status, 200);
-      final data = json.decode(utf8.decode(await response.body.first));
-      final fixes = data['fixes'];
-      expect(fixes.length, 1);
-      final problemAndFix = fixes[0];
-      expect(problemAndFix['problemMessage'], isNotNull);
-    });
-
-    test('fixes completeness', () async {
-      final jsonData = {
-        'source': '''
-void main() {
-  for (int i = 0; i < 4; i++) {
-    print('hello \$i')
-  }
-}
-''',
-        'offset': 67,
-      };
-      final response =
-          await _sendPostRequest('dartservices/v1/fixes', jsonData);
-      expect(response.status, 200);
-      final data = json.decode(utf8.decode(await response.body.first));
-      expect(data, {
-        'fixes': [
-          {
-            'fixes': [
-              {
-                'message': "Insert ';'",
-                'edits': [
-                  {'offset': 67, 'length': 0, 'replacement': ';'}
-                ]
-              }
-            ],
-            'problemMessage': "Expected to find ';'.",
-            'offset': 66,
-            'length': 1
-          }
-        ]
-      });
-    });
-
-    test('assist', () async {
-      final jsonData = {'source': assistCode, 'offset': 15};
-      final response =
-          await _sendPostRequest('dartservices/v1/assists', jsonData);
-      expect(response.status, 200);
-
-      final data = json.decode(utf8.decode(await response.body.first));
-      final assists = data['assists'] as List;
-      expect(assists, hasLength(2));
-      expect(assists.first['edits'], isNotNull);
-      expect(assists.first['edits'], hasLength(1));
-      expect(assists.where((m) {
-        final map = m as Map<String, dynamic>;
-        return map['message'] == 'Remove type annotation';
-      }), isNotEmpty);
-    });
-
-    test('version', () async {
-      final response = await _sendGetRequest('dartservices/v1/version');
-      expect(response.status, 200);
-      final data = json.decode(utf8.decode(await response.body.first));
-      expect(data['sdkVersion'], isNotNull);
-      expect(data['runtimeVersion'], isNotNull);
-    });
-  });
-}
-
-class MockContainer implements ServerContainer {
-  @override
-  String get version => vmVersion;
-}
-
-class MockCache implements ServerCache {
-  @override
-  Future<String> get(String key) => Future.value(null);
-
-  @override
-  Future set(String key, String value, {Duration expiration}) => Future.value();
-
-  @override
-  Future remove(String key) => Future.value();
-
-  @override
-  Future<void> shutdown() => Future.value();
-}
diff --git a/test/common_server_api_v2_test.dart b/test/common_server_api_v2_test.dart
deleted file mode 100644
index b29e16e..0000000
--- a/test/common_server_api_v2_test.dart
+++ /dev/null
@@ -1,410 +0,0 @@
-// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-library services.common_server_api_v2_test;
-
-import 'dart:async';
-import 'dart:convert';
-
-import 'package:dart_services/src/common.dart';
-import 'package:dart_services/src/common_server.dart';
-import 'package:dart_services/src/common_server_impl.dart';
-import 'package:dart_services/src/common_server_proto.dart';
-import 'package:dart_services/src/flutter_web.dart';
-import 'package:dart_services/src/sdk_manager.dart';
-import 'package:dart_services/src/server_cache.dart';
-import 'package:logging/logging.dart';
-import 'package:mock_request/mock_request.dart';
-import 'package:shelf/shelf_io.dart' as shelf_io;
-import 'package:test/test.dart';
-
-const quickFixesCode = r'''
-import 'dart:async';
-void main() {
-  int i = 0;
-}
-''';
-
-const preFormattedCode = r'''
-void main()
-{
-int i = 0;
-}
-''';
-
-const postFormattedCode = r'''
-void main() {
-  int i = 0;
-}
-''';
-
-const formatBadCode = r'''
-void main()
-{
-  print('foo')
-}
-''';
-
-const assistCode = r'''
-main() {
-  int v = 0;
-}
-''';
-
-void main() => defineTests();
-
-void defineTests() {
-  CommonServerProto commonServerProto;
-  CommonServerImpl commonServerImpl;
-  FlutterWebManager flutterWebManager;
-
-  MockContainer container;
-  MockCache cache;
-
-  Future<MockHttpResponse> _sendPostRequest(
-    String path,
-    dynamic jsonData,
-  ) async {
-    assert(commonServerProto != null);
-    final uri = Uri.parse('/api/$path');
-    final request = MockHttpRequest('POST', uri);
-    request.headers.add('content-type', JSON_CONTENT_TYPE);
-    request.add(utf8.encode(json.encode(jsonData)));
-    await request.close();
-    await shelf_io.handleRequest(request, commonServerProto.router.handler);
-    return request.response;
-  }
-
-  Future<MockHttpResponse> _sendGetRequest(
-    String path,
-  ) async {
-    assert(commonServerProto != null);
-    final uri = Uri.parse('/api/$path');
-    final request = MockHttpRequest('POST', uri);
-    request.headers.add('content-type', JSON_CONTENT_TYPE);
-    await request.close();
-    await shelf_io.handleRequest(request, commonServerProto.router.handler);
-    return request.response;
-  }
-
-  group('CommonServerProto JSON', () {
-    setUpAll(() async {
-      container = MockContainer();
-      cache = MockCache();
-      flutterWebManager = FlutterWebManager(SdkManager.flutterSdk);
-      commonServerImpl =
-          CommonServerImpl(sdkPath, flutterWebManager, container, cache);
-      commonServerProto = CommonServerProto(commonServerImpl);
-      await commonServerImpl.init();
-
-      // Some piece of initialization doesn't always happen fast enough for this
-      // request to work in time for the test. So try it here until the server
-      // returns something valid.
-      // TODO(jcollins-g): determine which piece of initialization isn't
-      // happening and deal with that in warmup/init.
-      {
-        var decodedJson = {};
-        final jsonData = {'source': sampleCodeError};
-        while (decodedJson.isEmpty) {
-          final response =
-              await _sendPostRequest('dartservices/v2/analyze', jsonData);
-          expect(response.statusCode, 200);
-          expect(response.headers['content-type'],
-              ['application/json; charset=utf-8']);
-          final data = await response.transform(utf8.decoder).join();
-          decodedJson = json.decode(data) as Map<dynamic, dynamic>;
-        }
-      }
-    });
-
-    tearDownAll(() async {
-      await commonServerImpl.shutdown();
-    });
-
-    setUp(() {
-      log.onRecord.listen((LogRecord rec) {
-        print('${rec.level.name}: ${rec.time}: ${rec.message}');
-      });
-    });
-
-    tearDown(log.clearListeners);
-
-    test('analyze Dart', () async {
-      final jsonData = {'source': sampleCode};
-      final response =
-          await _sendPostRequest('dartservices/v2/analyze', jsonData);
-      expect(response.statusCode, 200);
-      final data = await response.transform(utf8.decoder).join();
-      expect(json.decode(data), {});
-    });
-
-    test('analyze Flutter', () async {
-      final jsonData = {'source': sampleCodeFlutter};
-      final response =
-          await _sendPostRequest('dartservices/v2/analyze', jsonData);
-      expect(response.statusCode, 200);
-      final data = await response.transform(utf8.decoder).join();
-      expect(json.decode(data), {
-        'packageImports': ['flutter']
-      });
-    });
-
-    test('analyze errors', () async {
-      final jsonData = {'source': sampleCodeError};
-      final response =
-          await _sendPostRequest('dartservices/v2/analyze', jsonData);
-      expect(response.statusCode, 200);
-      expect(response.headers['content-type'],
-          ['application/json; charset=utf-8']);
-      final data = await response.transform(utf8.decoder).join();
-      final expectedJson = {
-        'issues': [
-          {
-            'kind': 'error',
-            'line': 2,
-            'sourceName': 'main.dart',
-            'message': "Expected to find ';'.",
-            'hasFixes': true,
-            'charStart': 29,
-            'charLength': 1
-          }
-        ]
-      };
-      expect(json.decode(data), expectedJson);
-    });
-
-    test('analyze negative-test noSource', () async {
-      final jsonData = {};
-      final response =
-          await _sendPostRequest('dartservices/v2/analyze', jsonData);
-      expect(response.statusCode, 400);
-    });
-
-    test('compile', () async {
-      final jsonData = {'source': sampleCode};
-      final response =
-          await _sendPostRequest('dartservices/v2/compile', jsonData);
-      expect(response.statusCode, 200);
-      final data = await response.transform(utf8.decoder).join();
-      expect(json.decode(data), isNotEmpty);
-    });
-
-    test('compile error', () async {
-      final jsonData = {'source': sampleCodeError};
-      final response =
-          await _sendPostRequest('dartservices/v2/compile', jsonData);
-      expect(response.statusCode, 400);
-      final data = json.decode(await response.transform(utf8.decoder).join());
-      expect(data, isNotEmpty);
-      expect(data['error']['message'], contains('Error: Expected'));
-    });
-
-    test('compile negative-test noSource', () async {
-      final jsonData = {};
-      final response =
-          await _sendPostRequest('dartservices/v2/compile', jsonData);
-      expect(response.statusCode, 400);
-    });
-
-    test('compileDDC', () async {
-      final jsonData = {'source': sampleCode};
-      final response =
-          await _sendPostRequest('dartservices/v2/compileDDC', jsonData);
-      expect(response.statusCode, 200);
-      final data = await response.transform(utf8.decoder).join();
-      expect(json.decode(data), isNotEmpty);
-    });
-
-    test('complete', () async {
-      final jsonData = {'source': 'void main() {print("foo");}', 'offset': 1};
-      final response =
-          await _sendPostRequest('dartservices/v2/complete', jsonData);
-      expect(response.statusCode, 200);
-      final data = json.decode(await response.transform(utf8.decoder).join());
-      expect(data, isNotEmpty);
-    });
-
-    test('complete no data', () async {
-      final response = await _sendPostRequest('dartservices/v2/complete', {});
-      expect(response.statusCode, 400);
-    });
-
-    test('complete param missing', () async {
-      final jsonData = {'offset': 1};
-      final response =
-          await _sendPostRequest('dartservices/v2/complete', jsonData);
-      expect(response.statusCode, 400);
-    });
-
-    test('complete param missing 2', () async {
-      final jsonData = {'source': 'void main() {print("foo");}'};
-      final response =
-          await _sendPostRequest('dartservices/v2/complete', jsonData);
-      expect(response.statusCode, 400);
-      final data = json.decode(await response.transform(utf8.decoder).join());
-      expect(data['error']['message'], 'Missing parameter: \'offset\'');
-    });
-
-    test('document', () async {
-      final jsonData = {'source': 'void main() {print("foo");}', 'offset': 17};
-      final response =
-          await _sendPostRequest('dartservices/v2/document', jsonData);
-      expect(response.statusCode, 200);
-      final data = json.decode(await response.transform(utf8.decoder).join());
-      expect(data, isNotEmpty);
-    });
-
-    test('document little data', () async {
-      final jsonData = {'source': 'void main() {print("foo");}', 'offset': 2};
-      final response =
-          await _sendPostRequest('dartservices/v2/document', jsonData);
-      expect(response.statusCode, 200);
-      final data = json.decode(await response.transform(utf8.decoder).join());
-      expect(data, {
-        'info': {},
-      });
-    });
-
-    test('document no data', () async {
-      final jsonData = {'source': 'void main() {print("foo");}', 'offset': 12};
-      final response =
-          await _sendPostRequest('dartservices/v2/document', jsonData);
-      expect(response.statusCode, 200);
-      final data = json.decode(await response.transform(utf8.decoder).join());
-      expect(data, {'info': {}});
-    });
-
-    test('document negative-test noSource', () async {
-      final jsonData = {'offset': 12};
-      final response =
-          await _sendPostRequest('dartservices/v2/document', jsonData);
-      expect(response.statusCode, 400);
-    });
-
-    test('document negative-test noOffset', () async {
-      final jsonData = {'source': 'void main() {print("foo");}'};
-      final response =
-          await _sendPostRequest('dartservices/v2/document', jsonData);
-      expect(response.statusCode, 400);
-    });
-
-    test('format', () async {
-      final jsonData = {'source': preFormattedCode};
-      final response =
-          await _sendPostRequest('dartservices/v2/format', jsonData);
-      expect(response.statusCode, 200);
-      final data = json.decode(await response.transform(utf8.decoder).join());
-      expect(data['newString'], postFormattedCode);
-    });
-
-    test('format bad code', () async {
-      final jsonData = {'source': formatBadCode};
-      final response =
-          await _sendPostRequest('dartservices/v2/format', jsonData);
-      expect(response.statusCode, 200);
-      final data = json.decode(await response.transform(utf8.decoder).join());
-      expect(data['newString'], formatBadCode);
-    });
-
-    test('format position', () async {
-      final jsonData = {'source': preFormattedCode, 'offset': 21};
-      final response =
-          await _sendPostRequest('dartservices/v2/format', jsonData);
-      expect(response.statusCode, 200);
-      final data = json.decode(await response.transform(utf8.decoder).join());
-      expect(data['newString'], postFormattedCode);
-      expect(data['offset'], 24);
-    });
-
-    test('fix', () async {
-      final jsonData = {'source': quickFixesCode, 'offset': 10};
-      final response =
-          await _sendPostRequest('dartservices/v2/fixes', jsonData);
-      expect(response.statusCode, 200);
-      final data = json.decode(await response.transform(utf8.decoder).join());
-      final fixes = data['fixes'];
-      expect(fixes.length, 1);
-      final problemAndFix = fixes[0];
-      expect(problemAndFix['problemMessage'], isNotNull);
-    });
-
-    test('fixes completeness', () async {
-      final jsonData = {
-        'source': '''
-void main() {
-  for (int i = 0; i < 4; i++) {
-    print('hello \$i')
-  }
-}
-''',
-        'offset': 67,
-      };
-      final response =
-          await _sendPostRequest('dartservices/v2/fixes', jsonData);
-      expect(response.statusCode, 200);
-      final data = json.decode(await response.transform(utf8.decoder).join());
-      expect(data, {
-        'fixes': [
-          {
-            'fixes': [
-              {
-                'message': "Insert ';'",
-                'edits': [
-                  {'offset': 67, 'length': 0, 'replacement': ';'}
-                ]
-              }
-            ],
-            'problemMessage': "Expected to find ';'.",
-            'offset': 66,
-            'length': 1
-          }
-        ]
-      });
-    });
-
-    test('assist', () async {
-      final jsonData = {'source': assistCode, 'offset': 15};
-      final response =
-          await _sendPostRequest('dartservices/v2/assists', jsonData);
-      expect(response.statusCode, 200);
-
-      final data = json.decode(await response.transform(utf8.decoder).join());
-      final assists = data['assists'] as List;
-      expect(assists, hasLength(2));
-      expect(assists.first['edits'], isNotNull);
-      expect(assists.first['edits'], hasLength(1));
-      expect(assists.where((m) {
-        final map = m as Map<String, dynamic>;
-        return map['message'] == 'Remove type annotation';
-      }), isNotEmpty);
-    });
-
-    test('version', () async {
-      final response = await _sendGetRequest('dartservices/v2/version');
-      expect(response.statusCode, 200);
-      final data = json.decode(await response.transform(utf8.decoder).join());
-      expect(data['sdkVersion'], isNotNull);
-      expect(data['runtimeVersion'], isNotNull);
-    });
-  });
-}
-
-class MockContainer implements ServerContainer {
-  @override
-  String get version => vmVersion;
-}
-
-class MockCache implements ServerCache {
-  @override
-  Future<String> get(String key) => Future.value(null);
-
-  @override
-  Future set(String key, String value, {Duration expiration}) => Future.value();
-
-  @override
-  Future remove(String key) => Future.value();
-
-  @override
-  Future<void> shutdown() => Future.value();
-}
diff --git a/tool/fuzz_driver.dart b/tool/fuzz_driver.dart
index a766202..8a14f2a 100644
--- a/tool/fuzz_driver.dart
+++ b/tool/fuzz_driver.dart
@@ -13,15 +13,13 @@
 import 'dart:math';
 
 import 'package:dart_services/src/analysis_server.dart' as analysis_server;
-import 'package:dart_services/src/api_classes.dart';
 import 'package:dart_services/src/common.dart';
-import 'package:dart_services/src/common_server.dart';
 import 'package:dart_services/src/common_server_impl.dart';
 import 'package:dart_services/src/compiler.dart' as comp;
 import 'package:dart_services/src/flutter_web.dart';
 import 'package:dart_services/src/sdk_manager.dart';
 import 'package:dart_services/src/server_cache.dart';
-import 'package:rpc/rpc.dart';
+import 'package:dart_services/src/protos/dart_services.pb.dart' as proto;
 
 bool _SERVER_BASED_CALL = false;
 bool _VERBOSE = false;
@@ -29,9 +27,7 @@
 bool _DUMP_PERF = false;
 bool _DUMP_DELTA = false;
 
-CommonServer server;
 CommonServerImpl commonServerImpl;
-ApiServer apiServer;
 MockContainer container;
 MockCache cache;
 analysis_server.AnalysisServerWrapper analysisServer;
@@ -133,11 +129,8 @@
   cache = MockCache();
   commonServerImpl =
       CommonServerImpl(sdkPath, flutterWebManager, container, cache);
-  server = CommonServer(commonServerImpl);
   await commonServerImpl.init();
 
-  apiServer = ApiServer(apiPrefix: '/api', prettyPrint: true)..addApi(server);
-
   analysisServer =
       analysis_server.AnalysisServerWrapper(sdkPath, flutterWebManager);
   await analysisServer.init();
@@ -244,10 +237,10 @@
 
   lastOffset = null;
   if (_SERVER_BASED_CALL) {
-    final request = SourceRequest();
+    final request = proto.SourceRequest();
     request.source = src;
-    await withTimeOut(server.analyze(request));
-    await withTimeOut(server.analyze(request));
+    await withTimeOut(commonServerImpl.analyze(request));
+    await withTimeOut(commonServerImpl.analyze(request));
   } else {
     await withTimeOut(analysisServer.analyze(src));
     await withTimeOut(analysisServer.analyze(src));
@@ -263,9 +256,9 @@
 
   lastOffset = null;
   if (_SERVER_BASED_CALL) {
-    final request = CompileRequest();
+    final request = proto.CompileRequest();
     request.source = src;
-    await withTimeOut(server.compile(request));
+    await withTimeOut(commonServerImpl.compile(request));
   } else {
     await withTimeOut(compiler.compile(src));
   }
@@ -284,10 +277,10 @@
     if (i % 1000 == 0 && i > 0) print('INC: $i docs completed');
     lastOffset = i;
     if (_SERVER_BASED_CALL) {
-      final request = SourceRequest();
+      final request = proto.SourceRequest();
       request.source = src;
       request.offset = i;
-      log(await withTimeOut(server.document(request)));
+      log(await withTimeOut(commonServerImpl.document(request)));
     } else {
       log(await withTimeOut(analysisServer.dartdoc(src, i)));
     }
@@ -306,10 +299,10 @@
     if (i % 1000 == 0 && i > 0) print('INC: $i completes');
     lastOffset = i;
     if (_SERVER_BASED_CALL) {
-      final request = SourceRequest()
+      final request = proto.SourceRequest()
         ..source = src
         ..offset = i;
-      await withTimeOut(server.complete(request));
+      await withTimeOut(commonServerImpl.complete(request));
     } else {
       await withTimeOut(wrapper.complete(src, i));
     }
@@ -328,10 +321,10 @@
     if (i % 1000 == 0 && i > 0) print('INC: $i fixes');
     lastOffset = i;
     if (_SERVER_BASED_CALL) {
-      final request = SourceRequest();
+      final request = proto.SourceRequest();
       request.source = src;
       request.offset = i;
-      await withTimeOut(server.fixes(request));
+      await withTimeOut(commonServerImpl.fixes(request));
     } else {
       await withTimeOut(wrapper.getFixes(src, i));
     }
@@ -345,10 +338,10 @@
   final sw = Stopwatch()..start();
   final i = 0;
   lastOffset = i;
-  final request = SourceRequest();
+  final request = proto.SourceRequest();
   request.source = src;
   request.offset = i;
-  log(await withTimeOut(server.format(request)));
+  log(await withTimeOut(commonServerImpl.format(request)));
   return sw.elapsedMilliseconds;
 }
 
diff --git a/tool/grind.dart b/tool/grind.dart
index 5b1926b..f91ac5f 100644
--- a/tool/grind.dart
+++ b/tool/grind.dart
@@ -234,40 +234,17 @@
   log('warning: fuzz testing is a noop, see #301');
 }
 
-@Task('Update discovery files and run all checks prior to deployment')
-@Depends(setupFlutterSubmodule, updateDockerVersion, generateProtos, discovery,
-    analyze, test, fuzz, validateStorageArtifacts)
+@Task('Update generated files and run all checks prior to deployment')
+@Depends(setupFlutterSubmodule, updateDockerVersion, generateProtos, analyze,
+    test, fuzz, validateStorageArtifacts)
 void deploy() {
   log('Run: gcloud app deploy --project=dart-services --no-promote');
 }
 
 @Task()
-@Depends(generateProtos, discovery, analyze, fuzz, buildStorageArtifacts)
+@Depends(generateProtos, analyze, fuzz, buildStorageArtifacts)
 void buildbot() => null;
 
-@Task('Generate the discovery doc and Dart library from the annotated API')
-void discovery() {
-  final result = Process.runSync(
-      Platform.executable, ['bin/server_dev.dart', '--discovery']);
-
-  if (result.exitCode != 0) {
-    throw 'Error generating the discovery document\n${result.stderr}';
-  }
-
-  final discoveryFile = File('doc/generated/dartservices.json');
-  discoveryFile.parent.createSync();
-  log('writing ${discoveryFile.path}');
-  discoveryFile.writeAsStringSync('${result.stdout.trim()}\n');
-
-  // Generate the Dart library from the json discovery file.
-  Pub.global.activate('discoveryapis_generator');
-  Pub.global.run('discoveryapis_generator:generate', arguments: [
-    'files',
-    '--input-dir=doc/generated',
-    '--output-dir=doc/generated'
-  ]);
-}
-
 @Task('Generate Protobuf classes')
 void generateProtos() async {
   await runWithLogging(