diff --git a/packages/fdb-debugger/src/index.ts b/packages/fdb-debugger/src/index.ts
index ee576676..ccf40525 100644
--- a/packages/fdb-debugger/src/index.ts
+++ b/packages/fdb-debugger/src/index.ts
@@ -449,16 +449,55 @@ export class RemoteHost extends EventEmitter {
     FDBTypes.AppDebugEvalResult,
   );
 
+  private sendButtonInput = this.bindMethod(
+    'input.button',
+    FDBTypes.ButtonInput,
+    t.null,
+  );
+
+  private sendTouchInput = this.bindMethod(
+    'input.touch',
+    FDBTypes.TouchInput,
+    t.null,
+  );
+
   hasEvalSupport() {
     return this.hasCapability('appHost.debug.app.evalToString.supported') &&
       this.info.capabilities.appHost!.debug!.app!.evalToString!.supported &&
       !FBOS3_EVAL_QUIRK.test(this.info.device);
   }
 
+  hasTouchInputSupport() {
+    return this.hasCapability('appHost.input.touch') &&
+      this.info.capabilities.appHost!.input!.touch!;
+  }
+
+  hasButtonInputSupport() {
+    return this.hasCapability('appHost.input.buttons') &&
+      this.info.capabilities.appHost!.input!.buttons! &&
+      this.info.capabilities.appHost!.input!.buttons!.length > 0;
+  }
+
+  buttons() {
+    if (!this.hasButtonInputSupport) return [];
+    return this.info.capabilities.appHost!.input!.buttons!;
+  }
+
   eval(cmd: string) {
     return this.sendEvalCmd({ cmd });
   }
 
+  simulateButtonPress(button: FDBTypes.Button) {
+    return this.sendButtonInput({ button });
+  }
+
+  simulateTouch(location: FDBTypes.Point, state: FDBTypes.TouchState) {
+    return this.sendTouchInput({
+      location,
+      state,
+    });
+  }
+
   supportsPartialAppInstall() {
     return this.hasCapability('appHost.install.partialBundle') &&
       this.info.capabilities.appHost!.install!.partialBundle!;
diff --git a/packages/fdb-protocol/src/FDBTypes/Initialize.ts b/packages/fdb-protocol/src/FDBTypes/Initialize.ts
index 5c644b25..4b3f17b9 100644
--- a/packages/fdb-protocol/src/FDBTypes/Initialize.ts
+++ b/packages/fdb-protocol/src/FDBTypes/Initialize.ts
@@ -5,6 +5,7 @@ import { IOCapabilities } from './BulkData';
 import { ConsoleDebuggerCapabilities } from './Console';
 import { EvalToStringCapability } from './Eval';
 import { HeapSnapshotCapability } from './HeapSnapshot';
+import { InputCapabilities } from './Input';
 import { LaunchCapabilities } from './Launch';
 import { ProtocolCapabilities } from './Meta';
 import { ScreenshotCapabilities } from './Screenshot';
@@ -84,6 +85,7 @@ export const ApplicationHostCapabilities = t.partial(
     launch: LaunchCapabilities,
     screenshot: ScreenshotCapabilities,
     debug: DebugCapabilities,
+    input: InputCapabilities,
   },
   'ApplicationHostCapabilities',
 );
diff --git a/packages/fdb-protocol/src/FDBTypes/Input.ts b/packages/fdb-protocol/src/FDBTypes/Input.ts
new file mode 100644
index 00000000..96619057
--- /dev/null
+++ b/packages/fdb-protocol/src/FDBTypes/Input.ts
@@ -0,0 +1,66 @@
+import * as t from 'io-ts';
+
+import { Point } from './Structures';
+
+// Runtime types are variables which are used like types, which is
+// reflected in their PascalCase naming scheme.
+/* tslint:disable:variable-name */
+
+export const Button = t.union(
+  [t.literal('up'), t.literal('down'), t.literal('back')],
+  'Button',
+);
+export type Button = t.TypeOf<typeof Button>;
+
+export const ButtonInput = t.interface(
+  {
+    /**
+     * Which button is being pressed.
+     */
+    button: Button,
+  },
+  'ButtonInput',
+);
+export type ButtonInput = t.TypeOf<typeof ButtonInput>;
+
+export const TouchState = t.union(
+  [t.literal('up'), t.literal('down'), t.literal('move')],
+  'TouchState',
+);
+export type TouchState = t.TypeOf<typeof TouchState>;
+
+export const TouchInput = t.interface(
+  {
+    /**
+     * Status of simulated touch.
+     * 'move' must only be sent in the period between a 'down' input and its corresponding 'up'.
+     */
+    state: TouchState,
+
+    /**
+     * Location of touch event.
+     */
+    location: Point,
+  },
+  'TouchInput',
+);
+export type TouchInput = t.TypeOf<typeof TouchInput>;
+
+/**
+ * Capabilities specific to inputs.
+ */
+export const InputCapabilities = t.partial(
+  {
+    /**
+     * The Host supports sending simulated button presses.
+     */
+    buttons: t.array(Button),
+
+    /**
+     * The Host supports sending simulated touch screen presses.
+     */
+    touch: t.boolean,
+  },
+  'InputCapabilities',
+);
+export type InputCapabilities = t.TypeOf<typeof InputCapabilities>;
diff --git a/packages/fdb-protocol/src/FDBTypes/Structures.ts b/packages/fdb-protocol/src/FDBTypes/Structures.ts
index 41711b69..cde7336e 100644
--- a/packages/fdb-protocol/src/FDBTypes/Structures.ts
+++ b/packages/fdb-protocol/src/FDBTypes/Structures.ts
@@ -182,3 +182,15 @@ export const ComponentBundleKind = t.union([
   t.literal('companion'),
 ]);
 export type ComponentBundleKind = t.TypeOf<typeof ComponentBundleKind>;
+
+/**
+ * Describes a point on the simulated device's screen, relative to the top-left corner at (0,0).
+ */
+export const Point = t.interface(
+  {
+    x: NonNegativeInteger,
+    y: NonNegativeInteger,
+  },
+  'Point',
+);
+export type Point = t.TypeOf<typeof Point>;
diff --git a/packages/fdb-protocol/src/FDBTypes/index.ts b/packages/fdb-protocol/src/FDBTypes/index.ts
index bc3d998d..2d5391b7 100644
--- a/packages/fdb-protocol/src/FDBTypes/index.ts
+++ b/packages/fdb-protocol/src/FDBTypes/index.ts
@@ -6,6 +6,7 @@ export * from './ContentsList';
 export * from './Eval';
 export * from './HeapSnapshot';
 export * from './Initialize';
+export * from './Input';
 export * from './Launch';
 export * from './Meta';
 export * from './Screenshot';
diff --git a/packages/sdk-cli/src/cli.ts b/packages/sdk-cli/src/cli.ts
index 0d00ee7f..43fcba93 100644
--- a/packages/sdk-cli/src/cli.ts
+++ b/packages/sdk-cli/src/cli.ts
@@ -20,6 +20,7 @@ import buildAndInstall from './commands/buildAndInstall';
 import connect from  './commands/connect';
 import heapSnapshot from './commands/heapSnapshot';
 import hosts from './commands/hosts';
+import input from './commands/input';
 import install from './commands/install';
 import logout from  './commands/logout';
 import mockHost from './commands/mockHost';
@@ -38,6 +39,7 @@ cli.use(build);
 cli.use(buildAndInstall({ hostConnections, appContext }));
 cli.use(connect({ hostConnections }));
 cli.use(heapSnapshot({ hostConnections }));
+cli.use(input({ hostConnections }));
 cli.use(install({ hostConnections, appContext }));
 cli.use(screenshot({ hostConnections }));
 cli.use(setAppPackage({ appContext }));
diff --git a/packages/sdk-cli/src/commands/input.ts b/packages/sdk-cli/src/commands/input.ts
new file mode 100644
index 00000000..70e6448a
--- /dev/null
+++ b/packages/sdk-cli/src/commands/input.ts
@@ -0,0 +1,94 @@
+import { FDBTypes } from '@fitbit/fdb-protocol';
+import { isRight } from 'fp-ts/lib/Either';
+import vorpal from 'vorpal';
+
+import HostConnections from '../models/HostConnections';
+
+function isSupportedButton(
+  supportedButtons: FDBTypes.Button[],
+  button: string,
+): button is FDBTypes.Button {
+  return supportedButtons.includes(button as FDBTypes.Button);
+}
+
+function isValidTouchState(state: string): state is FDBTypes.TouchState {
+  return isRight(FDBTypes.TouchState.decode(state));
+}
+
+const wait = (durationMs: number) =>
+  new Promise(resolve => setTimeout(resolve, durationMs));
+
+export default function input(stores: { hostConnections: HostConnections }) {
+  return (cli: vorpal) => {
+    cli
+      .command('input button <button>', 'Simulate a button press on device')
+      .hidden()
+      .action(async (args: vorpal.Args & { button?: string }) => {
+        const { appHost } = stores.hostConnections;
+        if (!appHost) {
+          cli.activeCommand.log('Not connected to a device');
+          return false;
+        }
+
+        if (!appHost.host.hasButtonInputSupport()) {
+          cli.activeCommand.log(
+            'Connected device does not support simulated button presses',
+          );
+          return false;
+        }
+
+        cli.activeCommand.log(args.button);
+        if (!isSupportedButton(appHost.host.buttons(), args.button!)) {
+          cli.activeCommand.log(
+            `Connected device does not support requested button type. Supported buttons: ${appHost.host
+              .buttons()
+              .join(', ')}`,
+          );
+          return false;
+        }
+
+        return appHost.host.simulateButtonPress(args.button);
+      });
+
+    cli
+      .command(
+        'input touch <state> <x> <y>',
+        'Simualate a touch event on device',
+      )
+      .hidden()
+      .action(
+        async (
+          args: vorpal.Args & { state?: string; x?: number; y?: number },
+        ) => {
+          const { appHost } = stores.hostConnections;
+          if (!appHost) {
+            cli.activeCommand.log('Not connected to a device');
+            return false;
+          }
+
+          if (!appHost.host.hasTouchInputSupport()) {
+            cli.activeCommand.log(
+              'Connected device does not support simulated touch events',
+            );
+            return false;
+          }
+
+          if (args.state === 'tap') {
+            await appHost.host.simulateTouch({ x: args.x!, y: args.y! }, 'down');
+            await wait(250);
+            await appHost.host.simulateTouch({ x: args.x!, y: args.y! }, 'up');
+          } else {
+            if (!isValidTouchState(args.state!)) {
+              cli.activeCommand.log('Touch state provided was not valid');
+              return false;
+            }
+
+            return appHost.host.simulateTouch(
+              { x: args.x!, y: args.y! },
+              args.state,
+            );
+          }
+        },
+      );
+  };
+}
diff --git a/yarn.lock b/yarn.lock
index 87242dbc..f96677ba 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1198,6 +1198,13 @@
   resolved "https://registry.yarnpkg.com/@types/base64-js/-/base64-js-1.2.5.tgz#582b2476169a6cba460a214d476c744441d873d5"
   integrity sha1-WCskdhaabLpGCiFNR2x0REHYc9U=
 
+"@types/cbor@^2.0.0":
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/@types/cbor/-/cbor-2.0.0.tgz#c627afc2ee22f23f2337fecb34628a4f97c6afbb"
+  integrity sha1-xievwu4i8j8jN/7LNGKKT5fGr7s=
+  dependencies:
+    "@types/node" "*"
+
 "@types/cbor@^5.0.0":
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/@types/cbor/-/cbor-5.0.0.tgz#b48dcc5f75191399ed6ea7cef862ef38a0945fa0"
@@ -1399,6 +1406,13 @@
   dependencies:
     "@types/yargs-parser" "*"
 
+"@types/yargs@^13.0.3":
+  version "13.0.5"
+  resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.5.tgz#18121bfd39dc12f280cee58f92c5b21d32041908"
+  integrity sha512-CF/+sxTO7FOwbIRL4wMv0ZYLCRfMid2HQpzDRyViH7kSpfoAFiMdGqKIxb1PxWfjtQXQhnQuD33lvRHNwr809Q==
+  dependencies:
+    "@types/yargs-parser" "*"
+
 "@zkochan/cmd-shim@^3.1.0":
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/@zkochan/cmd-shim/-/cmd-shim-3.1.0.tgz#2ab8ed81f5bb5452a85f25758eb9b8681982fd2e"
@@ -1529,6 +1543,14 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1:
   dependencies:
     color-convert "^1.9.0"
 
+ansi-styles@^4.0.0:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359"
+  integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==
+  dependencies:
+    "@types/color-name" "^1.1.1"
+    color-convert "^2.0.1"
+
 ansi-styles@^4.1.0:
   version "4.2.0"
   resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.0.tgz#5681f0dcf7ae5880a7841d8831c4724ed9cc0172"
@@ -2187,6 +2209,15 @@ cliui@^5.0.0:
     strip-ansi "^5.2.0"
     wrap-ansi "^5.1.0"
 
+cliui@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1"
+  integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==
+  dependencies:
+    string-width "^4.2.0"
+    strip-ansi "^6.0.0"
+    wrap-ansi "^6.2.0"
+
 clone-deep@^4.0.1:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387"
@@ -3130,6 +3161,14 @@ find-up@^3.0.0:
   dependencies:
     locate-path "^3.0.0"
 
+find-up@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
+  integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
+  dependencies:
+    locate-path "^5.0.0"
+    path-exists "^4.0.0"
+
 flush-write-stream@^1.0.0:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8"
@@ -4745,6 +4784,13 @@ locate-path@^3.0.0:
     p-locate "^3.0.0"
     path-exists "^3.0.0"
 
+locate-path@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
+  integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
+  dependencies:
+    p-locate "^4.1.0"
+
 lodash._reinterpolate@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
@@ -5641,6 +5687,13 @@ p-limit@^2.0.0:
   dependencies:
     p-try "^2.0.0"
 
+p-limit@^2.2.0:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e"
+  integrity sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==
+  dependencies:
+    p-try "^2.0.0"
+
 p-locate@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
@@ -5655,6 +5708,13 @@ p-locate@^3.0.0:
   dependencies:
     p-limit "^2.0.0"
 
+p-locate@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
+  integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
+  dependencies:
+    p-limit "^2.2.0"
+
 p-map-series@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-1.0.0.tgz#bf98fe575705658a9e1351befb85ae4c1f07bdca"
@@ -5790,6 +5850,11 @@ path-exists@^3.0.0:
   resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
   integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=
 
+path-exists@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
+  integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
+
 path-is-absolute@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
@@ -6819,7 +6884,7 @@ string-width@^3.0.0, string-width@^3.1.0:
     is-fullwidth-code-point "^2.0.0"
     strip-ansi "^5.1.0"
 
-string-width@^4.0.0, string-width@^4.1.0:
+string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0:
   version "4.2.0"
   resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5"
   integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==
@@ -7640,6 +7705,15 @@ wrap-ansi@^5.1.0:
     string-width "^3.0.0"
     strip-ansi "^5.0.0"
 
+wrap-ansi@^6.2.0:
+  version "6.2.0"
+  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
+  integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
+  dependencies:
+    ansi-styles "^4.0.0"
+    string-width "^4.1.0"
+    strip-ansi "^6.0.0"
+
 wrappy@1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
@@ -7769,6 +7843,14 @@ yargs-parser@^15.0.0:
     camelcase "^5.0.0"
     decamelize "^1.2.0"
 
+yargs-parser@^16.1.0:
+  version "16.1.0"
+  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-16.1.0.tgz#73747d53ae187e7b8dbe333f95714c76ea00ecf1"
+  integrity sha512-H/V41UNZQPkUMIT5h5hiwg4QKIY1RPvoBV4XcjUbRM8Bk2oKqqyZ0DIEbTFZB0XjbtSPG8SAa/0DxCQmiRgzKg==
+  dependencies:
+    camelcase "^5.0.0"
+    decamelize "^1.2.0"
+
 yargs@^13.3.0:
   version "13.3.0"
   resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83"
@@ -7801,3 +7883,20 @@ yargs@^14.2.2:
     which-module "^2.0.0"
     y18n "^4.0.0"
     yargs-parser "^15.0.0"
+
+yargs@^15.0.2:
+  version "15.1.0"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.1.0.tgz#e111381f5830e863a89550bd4b136bb6a5f37219"
+  integrity sha512-T39FNN1b6hCW4SOIk1XyTOWxtXdcen0t+XYrysQmChzSipvhBO8Bj0nK1ozAasdk24dNWuMZvr4k24nz+8HHLg==
+  dependencies:
+    cliui "^6.0.0"
+    decamelize "^1.2.0"
+    find-up "^4.1.0"
+    get-caller-file "^2.0.1"
+    require-directory "^2.1.1"
+    require-main-filename "^2.0.0"
+    set-blocking "^2.0.0"
+    string-width "^4.2.0"
+    which-module "^2.0.0"
+    y18n "^4.0.0"
+    yargs-parser "^16.1.0"