There is no WordPress-provided mobile SDK for calling abilities or MCP tools, and this lesson doesn’t pretend one exists. What it covers instead is accurate, general mobile development practice: how a native or cross-platform app makes an authenticated HTTPS request, and, more importantly, where that authentication credential actually lives on the device so it isn’t sitting in a plaintext preference file, decompiled binary, or autofill-shared keychain entry for anyone who gets hold of the phone. The WordPress side of this is unchanged from every previous lesson: an Application Password, sent as Basic Auth, resolved to a real WordPress user.
The authentication comparison from Lesson 2, and a mobile project (React Native, Flutter, or native iOS/Android, the pattern here is deliberately framework-agnostic).
Step 1: The threat model actually changes on mobile
A Next.js server’s environment variables live on infrastructure you control, behind
your own network boundary. A mobile app ships to a device you don’t control at all, one
that can be lost, stolen, jailbroken, or have its storage inspected by another app in
some circumstances. Neither WordPress nor the Abilities API changes here, the same
Application Password, sent the same way, resolves to the same WP_User. What changes is
that “where do I put this credential” is now a genuinely harder, device-level security
question, not a configuration convenience.
An Application Password baked directly into app source code, even as a “just for now” placeholder, ships inside the compiled binary and is recoverable by anyone willing to decompile or inspect network traffic from the app. Every credential needs to be provisioned per-install or per-user, never embedded as a constant.
Step 2: Secure on-device storage, the real platform mechanisms
Both major mobile platforms provide a hardware-backed secure storage area specifically for this kind of secret, distinct from ordinary app preferences or local storage:
None of these are WordPress-specific, they’re the same mechanisms any mobile app uses to store an API key or session token for any backend. That’s the correct mental model here: your WordPress Application Password is just a credential like any other, and it deserves the same storage discipline as one.
// File: auth/credentials.ts (React Native, using react-native-keychain)
import * as Keychain from 'react-native-keychain';
export async function saveWordPressCredential(username: string, appPassword: string) {
await Keychain.setGenericPassword(username, appPassword, {
service: 'wordpress-app-password',
});
}
export async function getWordPressCredential() {
const result = await Keychain.getGenericPassword({ service: 'wordpress-app-password' });
return result ? { username: result.username, appPassword: result.password } : null;
}
AsyncStorage in React Native, or plain SharedPreferences on Android, store data
unencrypted on most devices. They’re fine for UI preferences, not for an authentication
credential. If you see an Application Password going into either of those, that’s a
real vulnerability, move it into Keychain/Keystore-backed storage instead.
Step 3: A real fetch pattern, no invented SDK required
Calling an ability from the device is the same HTTP request as every previous lesson, just made with whatever standard HTTP client your mobile stack already uses:
// File: api/abilities.ts (React Native, fetch)
import { getWordPressCredential } from '../auth/credentials';
export async function getLatestPostTitle(baseUrl: string) {
const cred = await getWordPressCredential();
if (!cred) {
throw new Error('No WordPress credential stored on this device');
}
const auth = btoa(`${cred.username}:${cred.appPassword}`);
const res = await fetch(`${baseUrl}/wp-json/mcp/v1/my-plugin/get-latest-post-title`, {
method: 'POST',
headers: {
Authorization: `Basic ${auth}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
});
if (!res.ok) {
throw new Error(`Ability call failed with status ${res.status}`);
}
return res.json() as Promise<{ title: string }>;
}
Nothing here is a WordPress mobile SDK, it’s a standard fetch call with a credential
pulled from secure storage. That’s the accurate, honest shape of a WordPress mobile
integration today: general HTTP client code, plus WordPress’s existing REST and
Application Password surface.
Step 4: Why per-user accounts matter even more here
Course 4 recommended dedicated, least-privilege WordPress accounts for service integrations. On mobile, this matters more, not less: if each installed copy of your app authenticates as the same single shared Application Password, one compromised device compromises every user’s access simultaneously. Where practical, provision credentials per end user (each with their own WordPress account and Application Password, or their own JWT session if you made that deliberate tradeoff in Lesson 2) rather than baking one shared service credential into every install of a consumer app.
If your abilities allow anything beyond read-only, publishing, editing, deleting, a
single shared credential embedded in a widely distributed app is a materially worse
risk than the same credential used only by your own server. Scope mobile-facing
abilities’ permission_callback tightly, and prefer per-user credentials whenever the
app has any concept of individual user accounts at all.
Test it: confirm the credential survives an app restart, but only in secure storage
Save a credential, force-quit the app, relaunch it, and confirm the stored credential is still retrievable through your Keychain/Keystore wrapper, not through a plain preferences read:
// Quick manual check in a debug screen
const cred = await getWordPressCredential();
console.log('Retrieved:', cred ? 'yes' : 'no');
Then confirm you cannot find the raw Application Password string anywhere in the app’s plain preferences file or local storage directory, only inside the platform’s secure storage.
Recap
A mobile app calling WordPress abilities uses the exact same Application Password and Basic Auth mechanism as every other client in this course, there is no separate WordPress mobile SDK, and none is needed. What mobile changes is the storage question: the credential belongs in the iOS Keychain or Android Keystore, accessed through a wrapper like react-native-keychain or Expo SecureStore, never in plain preferences or hardcoded in source. For anything beyond read-only access, prefer per-user credentials over one shared secret baked into every install. The next lesson moves to a different constrained environment, edge and serverless functions, and where the same secret belongs there.
Resources & further reading
- react-native-keychain, GitHub
- Expo SecureStore, docs.expo.dev
- Application Passwords integration guide, make.wordpress.org