Appduct lets a terminal, a test runner, or an AI agent call functions inside your running iOS, Android, React Native, Flutter or web app. Appduct calls these functions tools. Only the tools you register are reachable.
- E2E tests skip the setup. Instead of tapping through login, onboarding and an empty cart, a test calls
log_inorseed_cartand starts at the screen it's testing. - Agents can drive your app. Over MCP or the CLI, an agent can flip a feature flag, open a screen, or read some state through your tools.
- No extra debug screens. Register tools for test setup instead of adding menus or buttons to your app.
- Production builds leave it out by default. You can opt in for internal builds such as TestFlight; see Build variants.
- Register tools in your app. Give each function a name, a description and a handler. Schemas tell callers what inputs and outputs to expect.
- Connect the running app. A connection link pairs it with Appduct on your computer.
- Call the tools. Use the CLI, a test runner or an agent over MCP.
You need Node 20 or newer for the CLI and a development build of your app. Check your SDK's setup guide below for platform requirements.
npm install -g appductFollow the setup guide for your platform to install the SDK and configure the connection:
| Platform | SDK and setup |
|---|---|
| iOS | AppductCore, with Swift Package Manager or CocoaPods |
| Android | Native Android SDK, from Maven Central |
| React Native | @appduct/react-native, for Expo development builds or bare apps |
| Flutter | appduct, from pub.dev |
| Web | @appduct/web, for any framework or plain JavaScript |
The same tool can be registered on each platform:
iOS (Swift)
import AppductCore
try Appduct.shared.register(
name: "seed_cart",
description: "Fill the cart with test items.",
inputSchema: ["type": "object", "properties": ["items": ["type": "number"]], "required": ["items"]]
) { args in
["added": (args["items"] as? NSNumber)?.intValue ?? 0]
}Android (Kotlin)
import com.callstack.appduct.Appduct
import org.json.JSONObject
Appduct.register(
name = "seed_cart",
description = "Fill the cart with test items.",
inputSchema = JSONObject("""{"type":"object","properties":{"items":{"type":"number"}},"required":["items"]}"""),
) { args ->
JSONObject().put("added", args.optInt("items"))
}React Native
import { useAppductTool } from "@appduct/react-native";
import { z } from "zod";
export function AppductTools() {
useAppductTool({
name: "seed_cart",
description: "Fill the cart with test items.",
inputSchema: z.object({ items: z.number() }),
outputSchema: z.object({ added: z.number() }),
handler: async ({ items }) => ({ added: items }),
});
return null;
}Mount <AppductTools /> near your app's root.
Flutter (Dart)
Register after Appduct.ensureInitialized() in your app startup code.
import 'package:appduct/appduct.dart';
Appduct.instance.registerTool(
'seed_cart',
description: 'Fill the cart with test items.',
inputSchema: {
'type': 'object',
'properties': {'items': {'type': 'number'}},
'required': ['items'],
},
handler: (args, context) => {'added': (args['items'] as num).toInt()},
);Web
import { registerTool } from "@appduct/web";
registerTool({
name: "seed_cart",
description: "Fill the cart with test items.",
inputSchema: {
type: "object",
properties: { items: { type: "number" } },
required: ["items"],
},
handler: async ({ items }) => ({ added: items }),
});With your app running, choose one connection method. Run the command from your app's project directory, using the scheme configured in its setup guide:
| Target | Command |
|---|---|
| iOS Simulator | appduct sessions link --open ios-sim |
| Android emulator or USB device | appduct sessions link --open android --app-id com.example.shop |
| Physical iOS or Android device | appduct sessions link --qr |
| Web page | appduct sessions link --open web http://localhost:5173/ |
For Android, replace com.example.shop with your installed build's app id. For a physical device, scan the QR code on the same local network as your computer. For Web, open the URL the CLI prints. See Connect a device for other options.
Once connected, list the tools and call the one you registered:
appduct tools ls
appduct tools call seed_cart --input '{"items":3}'The example returns {"added":3}. Replace its handler with your app's cart setup.
Try a complete example before adding Appduct to your own app. Each playground registers the same five demo tools and the playground_ping event:
- iOS: SwiftUI
- Android: Jetpack Compose
- React Native: Expo
- Flutter: Android, iOS and macOS
- Web: React and Vite
- Use the CLI: connect, find tools and watch events
- Connect an agent: MCP configuration and the agent skill
- Call tools from tests: the
appduct/clientAPI - Write tools: schemas, long-running tools and events
- Choose build variants: which builds include Appduct
- Configure security: trust, policy and key rotation
appduct is an open source project and will always remain free to use. If you think it's cool, please star it 🌟. Callstack is a group of React and React Native geeks, contact us at hello@callstack.com if you need any help with these or just want to say hi!
Like the project? ⚛️ Join the team who does amazing stuff for clients and drives React Native Open Source! 🔥
