We launched CoWork, a mobile app testing solution, on PH a while back and got a lot of useful feedback. Some of the most interesting comments came from developers who wanted to test their mobile apps before the merge, not after.
So we built QApilot MCP, that lets Claude, Cursor or Codex drive a real Android device. It launches Saturday.
QApilot
Hey Product Hunt!
I am Charan. Thrilled to bring QApilot MCP to you. → https://qapilot.io/mcp/guide
Android app testing inside your AI client. You describe what needs to hold in plain English, and it runs on a device or emulator connected to your machine.
The specifics:
Android only. iOS is next.
Native Android apps. Hybrid and WebView aren't supported yet.
Local device or emulator.
Tested configs for Claude Desktop, Cursor and Codex.
You'll need Node, Java, the Android SDK, and Appium running locally.
Why an MCP?
Coding agents made mobile code cheap to write. They did nothing for verifying it, so the bottleneck just moved. A mobile lead put it well: "by the time your report reaches me, I've merged three more PRs." Our testing wasn't wrong. It was happening too late, in a tool he never opened. MCP is how we moved it into his editor.
What it does
You tell your agent what needs to hold: "verify checkout still works after this refactor." It lays out the steps; QApilot finds the actual element behind "Checkout" on a real Android screen, waits for it to settle, retries when something moves, and says so honestly when it can't.
You get a verdict, a live browser preview, and files on disk: YAML, JSON steps, Gherkin on pass. Accept a run and it becomes a test case you can replay by ID, in batch, or from Excel. It caches what it learns per app, so it gets faster the more you use it.
The part we care most about
It runs on your machine. Your app, your screenshots and your runs don't get uploaded. Only your account and the test cases you explicitly save go to QApilot. Most tools here force every run through their cloud behind a tunnel, and for a lot of teams that's an immediate no. We didn't want to build that.
If you build Android apps with a coding agent, I'd like to know what you still check by hand before every merge. That's what shapes where this goes next.
Happy to answer anything in the comments.
@charan_tej_kammara Congratulations on the launch today! While I'm not a developer, I can see how anything that fixes bottlenecks would be a valuable tool.
Who would you say is the Ideal User for your product - a solo mobile developer doing their own QA, or a team that already has Appium and a pipeline running? Just curious how you're marketing/positioning QAPilot.
QApilot
@anna_ludwinowski - Thanks Anna. It's the second, which surprised us a bit.
Setup needs Java, the Android SDK and Appium. A solo dev without that hits friction before seeing value; teams with a pipeline already have all of it. Not to say that its not for solo dev but the latter is more likely to find value immediately.
We sell the wider platform to QA teams. This is for the developer on that team who wants a check before the PR. Less a replacement for the pipeline, more something in front of it.
@charan_tej_kammara Congrats on the launch!
We'll def try it out. Any plans for an iOS version?
QApilot
@rodion_revin Thank you, Rodion. Looking forward to hearing your thoughts once you try it out. We have an active slack channel too. Join here to reach out to directly to us while you are trying out QApilot MCP on your coding agent.
QApilot
Hey Product Hunt,
I'm Surendranath. I work on the QApilot MCP, mostly on the parts that decide whether a decision is trustworthy.
Charan covered why we built this. I'll tell you where the work actually went, because "agent taps on cart“ is the easy 20% and it's already free in a few places.
Three things are hard, and we’ve naturally decided to jump on them:
Resolving an element from a fuzzy name. Your agent says "tap Checkout." On a real Android screen that could be a label inside a nested container, a button with no content description, or one of three things containing that string. Getting from intent to the correct node, repeatedly, on someone else's app, is unglamorous and it is most of what we do.
Knowing when the screen has finished changing. The naive version is a sleep, which is why so much mobile automation is flaky. Acting one frame too early is the single biggest source of false failures, and a test that lies to you is worse than no test, because you learn to ignore it.
Retrying without laundering a real failure. Retry logic that hides genuine breakage is how you ship a bug with a green check. So we retry on movement and re-render, and when we can't do the step we say so instead of guessing.
One design decision worth flagging: On a brand new app your first session is a cold start, and your agent explores like anyone else's would. It earns the speedup over a few runs.
And it runs locally against the device already connected to your machine. That was an engineering preference before it was a privacy one: no tunnel, no mandatory upload step, no waiting on a queue in someone's cloud.
Limits: native Android only right now, so hybrid and WebView are out. iOS is next.
And deciding what should hold before a merge is still your judgment, not ours.
What I want from you: tell me where it gives you a wrong verdict, specifically a false pass. A false failure annoys you, but a false pass is the thing that gets shipped. Those reports go to the top of my list.
I'll be in the comments and available in the Slack channel - https://join.slack.com/t/qapilotmcp/shared_invite/zt-4a0dag17k-XxOoyGlYrT55NrVOdHnQ1A
PicWish
@jsurendranathreddy u mentioned: instead of using naive sleeps, wait for the screen to settle. what happens if there's an infinite loading spinner that never actually stops rendering?
QApilot
@mohsinproduct - Good question, we hash the view hierarchy, not pixels. That is, a spinner animating doesn't change the tree, so it settles fine. A spinner with updating text would keep changing, but the wait is capped either way. So it proceeds and the assertion fails on the content that never arrived. Which is the failure you want.
It is great to see am app dedicated to android apps. There are so many that focus on ios it is refreshing to see android getting attention. Although I am not a mobile app developer (yet) I am an android user so kobe seeing this.
I also like the ease of use being able to use plain language.
One question, does it work with other coding agents (in particular I use opencode so would be interested in an opencode config).
Good luck with the launch.
QApilot
@dmoniz22 Thanks. opencode should work in principle. It's a standard stdio MCP server, so any MCP client can run it. We just haven't tested a config. If that's something you'd like to try, please do join our Slack channel we'll figure it out together, then I'll add it to the docs.
@charan_tej_kammara sounds fantastic. I have joined the slack channel. I will try to add it to opencode a bit later today and let you know how it goes.
QApilot
@dmoniz22 - wonderful, looking forward to see your findings. Should you run into any issues during installation or test runs, my team and I on slack are happy to troubleshoot.
Observer
Hey PH! Vidushee here. I am one of the makers of the QApilot MCP.
I wanted to add a few things that make this different from just pointing an agent at a phone, along with some honest caveats.
Your agent decides what to do; QApilot does the part that's hard on mobile - finding the real element behind "Checkout" in a live UI tree, and waiting for the screen to actually settle instead of guessing a sleep.
It caches what it learns per app from passing runs, so it gets faster the more you use it. And it all runs on your machine; your app is never uploaded.
Setup is the rough edge today. You need Node, Java, the Android SDK and Appium running before anything happens, and that's the next thing we're cutting down after iOS.
If there's something you'd want ahead of either, say so here - much easier to reorder this week than in three months.
Thanks!
Retro Y2K Theme
curious about the "plain language" part. i sometimes mess up what to say when trying to get the right retro look for a site, so if this can interpret some nonsense and still spit out a working test, i'd give it a shot. but i might forget to open the emulator. 😂
QApilot
@cepeshrc - Haha, messy input is mostly fine. It reads the actual screen, so "the button that takes you to checkout" works even if you don't know what it's called. Vague about what to verify is where it struggles more than vague wording. As long as the intent is communicated, you are set.
And it'll tell you straight away if there's no device connected, so the emulator thing sorts itself out. 😄
@QApilot This looks very useful, when will it be available for iOS?
QApilot
@codeandsea thank you! iOS is next up - no date I'd commit to yet, but it's by far the most asked-for thing today. I will be sure to ping you once its out.
i can't imagine writing a test in plain language and having it actually get the idea right. would prob be like my reddit replies, where i wing it and hope for the best. but if it nails it on the first try, then maybe i'd stop winging it so much.
QApilot
@therayess - Winging it works better than you'd think. It reads the screen first, so it's matching your words against what's actually there. And when it can't tell what you meant, it stops and says so rather than guessing.
So roughly: better than a Reddit reply, worse than a spec. Somewhere in the middle is fine :)