DEV Community

jackymenCZ (jackymenCZ)
jackymenCZ (jackymenCZ)

Posted on

MCP Shouldn't Require a Desktop: Designing a One-Tap Mobile MCP App

Thanks for support..
Release: "Sentinel-IR-MCP v0.2.2 on GitLab" (https://gitlab.com/jakubrojicek11/sentinel-ir-mcp/-/tree/v0.2.2?ref_type=tags)

Package: "sentinel-ir-mcp@0.2.2 on npm" (https://www.npmjs.com/package/sentinel-ir-mcp/v/0.2.2)

Somewhere between “I found a useful MCP tool” and “I can use it from my phone,” a simple idea can turn into a side quest through runtimes, JSON files, credentials, server URLs and settings screens.
I want to explore a simpler path: a mobile-first app that makes connecting to useful tools feel understandable, safe and close to one tap. Not by hiding the permissions, but by removing the setup archaeology.
This article has three levels: 30 seconds for the scroll crowd, 10 minutes for curious humans, and a deeper technical tour for developers.
Project status, before the marketing goblin gets near the keyboard: the Mobile MCP 0.1.0 source draft exists, and nine focused unit tests passed in the authoring environment. A DNS failure blocked npm dependency installation, so a normal dependency-backed build has not been verified. Google OAuth, a real Drive search, PostgreSQL integration and a deployment have not been tested live. The source draft is also not yet a general MCP client or MCP server. This is an engineering direction with an initial codebase, not a launched product.
The 30-second version
MCP, the Model Context Protocol, gives AI applications a common way to discover and use tools. But connecting tools can still feel like assembling furniture with instructions written for someone who already owns a screwdriver factory.
Mobile MCP is an attempt to make that connection mobile-first. Open an app on your phone, choose a connector, authorize it with the provider, understand what it can access, and use it without starting from a terminal and a configuration file.
The first planned slice uses Google Drive to search file names and show basic metadata. It is designed not to read document contents or modify files. The code draft exists, but the full build and live flow still need verification. It does not yet expose or consume the MCP protocol end-to-end.
The principle is simple: one tap should remove setup friction, not remove user consent.
The 10-minute guide for non-experts

  1. First, imagine a toolbox Imagine you buy a new cordless drill. You do not want to build the electric motor, manufacture the battery, solder a charger and write a driver before drilling one hole. You want to choose the tool, connect it safely, and get on with your day. AI tools have a similar problem. An AI assistant becomes more useful when it can work with things outside the chat: files, calendars, repositories, databases, issue trackers or other services. But the connection between an AI app and those tools needs a common interface, permissions and somewhere for the tool to run. That is where MCP enters the picture.
  2. What MCP is, without the acronym fog MCP stands for Model Context Protocol. Think of it as a standard connection language between an AI application and external tools or data sources. The AI model is not MCP. Google Drive is not MCP. A button labelled “Connect” is not MCP either. A useful mental model is: The AI application or client asks what tools are available and calls them. The MCP server exposes tools or other capabilities through the protocol. The underlying service does the actual work, such as searching files or looking up an issue. MCP helps standardize the conversation between the client and the server. It does not automatically make a tool secure, reliable or useful. A standard plug does not make every appliance safe to leave running overnight. The official project documentation is here: modelcontextprotocol.io.
  3. So why focus on phones? A lot of developer tooling assumes that the user is sitting at a computer. That makes sense for many development workflows. It is less comfortable when you are using a phone as your main device. Depending on the tool and client, connecting an MCP server may involve installing a runtime, creating a JSON configuration, entering an endpoint, managing credentials, restarting an application, and then figuring out why nothing appears in the tool list. A developer may consider that a five-minute setup. For someone who does not live in terminals, it can be a locked door with a very technical doormat. And mobile makes the friction more visible: typing long paths on a touchscreen is not exactly a premium user experience. Copying a JSON block from one app to another is possible, but so is eating soup with a fork. The problem is not that MCP is inherently desktop-only. Remote MCP services can be accessed over HTTP by compatible clients. The problem we want to tackle is the product experience around connecting, authorizing and using tools from a phone.
  4. The idea: a mobile-first connection desk The aim is to make the first steps feel like this: Open the app from a phone browser or install it as a PWA where supported. Choose a connector, for example Google Drive. Sign in with the provider and review the requested permissions. See a clear list of what that connector can and cannot do. Use the capability through a simple interface, without manually editing configuration files. Disconnect it when it is no longer needed. That is the experience we are aiming for, not a claim that every step is already live. For the first vertical slice, I chose Google Drive metadata search. It is concrete and easy to check: search for a file you know exists, see its name and type, and open the real Drive link. The first slice is not supposed to read your documents, rewrite them, share them or delete them. Why start small? Because “connect everything with one click” sounds wonderful until the first click grants a tool more access than it needs. A tiny, verifiable capability is a better foundation than a giant dashboard full of green connection badges.
  5. What the current source draft contains The project has a mobile-oriented React and TypeScript interface, a Node.js/Fastify API, PostgreSQL session and OAuth-account storage, a Google OAuth flow, a Drive metadata-search connector, security helpers, deployment configuration and test instructions. The current code is designed around a single configured owner account for a private test, not public multi-user onboarding. That keeps the first authorization boundary narrow while the flow is being checked. There are also important limits: It is not yet a general MCP client that can connect to arbitrary MCP servers. It is not yet an MCP server that exposes the connector through the MCP protocol. There is no AI model or chat-based tool selection in this first slice. No paid hosting has been provisioned and nothing has been deployed. Google authorization and real Drive requests have not been verified live. Why call it Mobile MCP, then? Because the intended product is a mobile-first way to connect and use MCP-compatible capabilities. The current connector hub is the first foundation to validate, not the final protocol surface.
  6. A phone-first interface does not mean a phone-only backend The mobile app should be the simple front door. It does not need to hold every secret or do every job itself. The planned boundary looks like this: Phone browser / installable PWA | HTTPS request | Authenticated API | Connector registry | Google Drive API The phone gets a usable interface. The server handles provider credentials and provider calls. Later, a protocol adapter can expose approved capabilities to compatible MCP clients. This separation matters. A mobile browser is not a secure place to paste a long-lived Google client secret, and a user should not have to understand refresh-token rotation to search for a project plan.
  7. “One click” cannot mean “trust us with everything” There is a difference between removing friction and removing control. For a real product, users should be able to see which account is connected, what the connector can access, whether it can write anything, and how to disconnect it. The server should enforce these limits even if an AI model asks for something outside them. The source draft includes several security mechanisms: an OAuth state check, verified Google identity, an owner-account allowlist, HTTP-only browser sessions, hashed session identifiers in PostgreSQL, token encryption at rest, request-origin checks, rate limits, API no-cache headers and basic log redaction. Those are design and implementation choices visible in the source. They are not a third-party security audit, and they do not prove the deployed system is safe. They still need normal integration tests and live checks. There is also an important permission decision ahead. The current broad Drive metadata-search design requests drive.metadata.readonly. Google classifies that scope as restricted. A public release must account for Google's applicable verification and data-handling requirements. A narrower drive.file flow combined with the Google Picker can give users more per-file control, but it does not provide the same unrestricted search across all Drive metadata. The convenient product requirement and the least-privilege permission model need to be considered together, not after launch. Google's scope guidance: Choose Google Drive API scopes.
  8. What would count as real progress? Not a screenshot. Not a successful-looking mock result. Not “the code seems fine.” The first meaningful acceptance test should be boring in the best possible way: Open the actual HTTPS app on a phone. Connect a real Google test account through OAuth. Search for a known file in that account. Confirm the real file name, metadata and Drive link. Try an unknown name and get an honest empty result. Deny or cancel authorization and confirm the app does not pretend to be connected. Disconnect and confirm protected search calls stop working. Before that test, the dependency installation, lockfile, type checking, full build and PostgreSQL integration test must be completed. The authoring environment hit EAI_AGAIN while resolving the npm registry, so those dependency-backed checks are still blocked. Nine focused unit tests did pass, but they are only one part of the evidence.
  9. What is the potential payoff? If the product works, the value is not “MCP, but with a smaller screen.” It is a different entry point. For non-developers, it could make useful tool connections less intimidating. For developers, it could provide a simpler way to configure approved connectors, test permissions and use remote tools while away from a workstation. For teams, a curated catalogue could make the allowed capabilities easier to discover and explain. That does not automatically make it faster, cheaper or safer than every existing MCP setup. Those are claims to measure after there is a working product. The first hypothesis is narrower: a mobile-first setup flow can remove unnecessary configuration work without hiding important permissions. That is worth testing. The 20-minute developer guide This section goes from the product idea to the source-level design. It is intentionally specific about what exists today and what remains future work.
  10. Start by separating three roles “MCP app” can refer to several different things, so let us avoid building one acronym-shaped ball of confusion. A. Mobile control surface The React PWA is the phone-friendly interface. It handles user interaction, account status, connector selection and search results. B. Connector backend The Fastify server handles sessions, OAuth credentials and provider API calls. The current first-party connector uses the Google Drive API directly. C. MCP protocol integration, planned A future MCP client capability would connect to remote MCP servers and discover/call their tools. A future MCP server capability would expose selected first-party tools, such as a permitted Drive search, to other MCP-compatible applications. These are separate capabilities. The current 0.1.0 source draft does not yet implement a general-purpose client or server. That distinction is architectural, not cosmetic. A React button calling /api/drive/search is an ordinary application API call. It does not magically become an MCP tool because the repository name contains “MCP.”
  11. Current stack and data flow The source draft uses: React, TypeScript and Vite for the responsive PWA. Node.js and Fastify for HTTP routes. PostgreSQL for sessions and OAuth-account records. Google's OAuth client and Drive API libraries. AES-256-GCM for encrypting stored OAuth tokens. Unit tests plus opt-in PostgreSQL and live-health tests. Current request flow: Browser / PWA | | POST /api/drive/search | HTTP-only session cookie v Fastify route | validate origin and input | resolve authenticated owner v DriveClient | load encrypted credentials | decrypt server-side | refresh if needed v Google Drive API | files.list(q, fields, pageSize) v Metadata only: id, name, mimeType, modifiedTime, webViewLink There is no LLM call in that path. The user query is validated and used to build a Drive search expression. The server asks Google for limited metadata and returns it to the interface.
  12. Input validation before the provider call The current helper checks the query length, rejects control characters and escapes backslashes and quotes before building the Drive expression. A simplified excerpt from src/connectors/google-drive/search-files.ts: export function escapeDriveQueryLiteral(input: string): string { return input.replace(/\/g, '\\').replace(/'/g, "\'"); }

export function buildDriveNameQuery(searchTerm: string): string {
const trimmed = searchTerm.trim();

if (trimmed.length < 2 || trimmed.length > 120) {
throw new Error('Search term must be between 2 and 120 characters.');
}

if (/[\u0000-\u001F\u007F]/.test(trimmed)) {
throw new Error('Search term cannot contain control characters.');
}

return name contains '${escapeDriveQueryLiteral(trimmed)}' and trashed = false;
}
The point is not that string escaping solves every security concern. It is that input length, control characters and query quoting should be handled in one narrow function and tested independently, rather than scattered through route handlers.
The current Drive request uses a limited field selection and a capped result count:
const result = await drive.files.list({
q: query,
pageSize: 25,
orderBy: 'modifiedTime desc',
fields: 'nextPageToken,files(id,name,mimeType,modifiedTime,webViewLink)',
spaces: 'drive'
});
That is a deliberate boundary: metadata, not document content. A webViewLink is a link to Drive, not a grant of extra access. Google still enforces the user's file permissions and the OAuth scope.

  1. Protect the route, not just the button Frontend controls are convenience, not authorization. Someone can call an HTTP endpoint without using the UI, so the route itself checks the request origin, validates the body, and requires the configured owner account. A shortened excerpt of the current route's shape: app.post('/api/drive/search', { config: { rateLimit: { max: 30, timeWindow: '1 minute' } } }, async (request, reply) => { if (!requestHasAllowedOrigin(request)) { return reply.code(403).send({ error: 'ORIGIN_NOT_ALLOWED' }); }

const email = await requireOwner(request, reply);
if (!email) return reply;

const parsed = searchSchema.safeParse(request.body);
if (!parsed.success) {
return reply.code(400).send({ error: 'INVALID_QUERY' });
}

const result = await driveClient.searchByName(email, parsed.data.q);
return reply.code(200).send(result);
});
This excerpt omits the error mapping found in the full route. The implementation uses a narrow, generic error response for unexpected failures instead of returning raw provider details to the client.
The same principle will matter even more when MCP tools are added. A model should not be able to choose an arbitrary account ID and access another person's connector. The server should resolve the identity from the authenticated session, validate every tool argument and enforce permissions independently of the model's instructions.

  1. OAuth state, sessions and token storage The authorization-code flow is server-side. The callback validates a short-lived OAuth state value against the session, verifies the returned Google identity and checks that the verified email matches the configured owner allowlist. After a successful login, the session identifier is rotated to reduce session-fixation risk. The browser session cookie is HTTP-only and uses Secure in production. PostgreSQL stores a SHA-256 hash of the opaque session identifier instead of the raw identifier. OAuth access and refresh tokens are encrypted before they are stored. The token-encryption helper uses authenticated encryption: const ALGORITHM = 'aes-256-gcm'; const IV_LENGTH = 12; const TAG_LENGTH = 16; Each encryption call generates a fresh random IV and stores the IV, authentication tag and ciphertext in a versioned envelope. Decryption verifies the authentication tag, so tampering causes failure rather than returning modified token bytes. The encryption key belongs in the server environment, not in the Vite frontend, Git history, the database alongside the ciphertext, or logs. Encrypting a token at rest helps protect database contents, but it does not protect a compromised application process that can read the key. Key access, backup handling, rotation and incident response remain operational concerns. The API also applies no-store headers to /api/ responses. Authentication is not a license to let private search results sit in a shared cache.
  2. The Google scope is a product decision The current source requests: export const DRIVE_METADATA_SCOPE = 'https://www.googleapis.com/auth/drive.metadata.readonly'; This scope is read-only for metadata, but Google classifies it as a restricted Drive scope. That has consequences for a public release. Depending on the app's usage and how restricted-scope data is stored or transmitted, Google verification and a security assessment may be required. This should be checked against Google's current requirements before inviting public users. An alternative is to use the narrower drive.file scope with Google Picker, letting the user select files they want to authorize for the app. That reduces the scope of access, but it does not provide identical behavior to searching metadata across all of a user's Drive. The product needs to decide what matters more for the first release: broad name search or a narrower per-file consent experience. Do not silently request a broader scope because it is easier to code. Make the trade-off visible, document it, and test it with actual Google Cloud configuration. Reference: Google Drive API scope guidance.
  3. Where the MCP protocol should enter The existing connector service should not be entangled with one protocol. A clean next step is to treat provider operations as capabilities and add a separate protocol adapter around only those operations that should be exposed. Conceptually: +----------------------+ Phone PWA ------> | App API | ------> Google Drive +----------------------+ ^ | +----------------------+ MCP client -----> | MCP adapter (future) | ------> approved capability +----------------------+ There are two possible product features here: Use remote MCP servers from the mobile app. The app becomes an MCP client. It needs server discovery/configuration, transport handling, authorization, a tool catalogue and a safe way to invoke selected tools. Expose Mobile MCP connectors to other AI clients. The backend becomes an MCP server. It needs protocol endpoints, tool discovery, schemas, per-user authorization and execution policies. They should not be implemented as one vague feature called “MCP support.” They have different trust boundaries and acceptance tests. For remote server access, the official TypeScript SDK documents Streamable HTTP as the recommended transport for remote MCP servers, while stdio is primarily suited to local process integrations. A phone cannot always spawn a local server process as a desktop client can, so a remote transport is a natural direction for a hosted mobile experience. Official references: MCP TypeScript SDK server guide and MCP specification.
  4. Design the connector interface before the protocol adapter The existing registry is intentionally small. It describes a connector and its capabilities rather than making the UI responsible for provider-specific logic. As additional connectors arrive, keep the provider operation separate from how it is called. A future capability could be described conceptually like this: interface ToolCapability { name: string; description: string; inputSchema: unknown; requiredPermission: string; execute(input: Input, actor: AuthenticatedActor): Promise; } This is a design sketch, not an interface that currently ships in the source draft. Its purpose is to make a few rules explicit: Each capability has a stable name and input schema. The authenticated actor is supplied by the backend, not invented by the model. Required permissions are declared and checked before execution. The output type is narrow and documented. The transport adapter does not become the source of authorization truth. A future MCP adapter can map approved capabilities to MCP tools. A mobile UI can call a first-party API. Both can reuse the same policy-checked service without copying Google API logic into two routes.
  5. The safety boundary for future MCP tools MCP tools are code execution entry points, even when the tool itself only calls a web API. Before allowing arbitrary server configuration or broader actions, the system needs to handle at least these concerns: Per-user isolation: a user must never inherit another user's connector credentials or data. Least privilege: a tool gets only the provider scope and operation it needs. Argument validation: tool inputs are untrusted, even when generated by a model. No arbitrary URL fetches by default: accepting arbitrary MCP URLs can create server-side request forgery and network-boundary problems. No shell execution in the connector path: do not turn tool configuration into an arbitrary command runner. Visible side effects: write, share and delete operations should be separate capabilities with clear confirmation and authorization rules. Time and size limits: cap result counts, request duration and response size. Redacted observability: record enough structured events to troubleshoot without logging tokens or private file contents. Revocation: disconnecting a provider should make subsequent calls fail and should remove or invalidate stored credentials according to the product's retention policy. MCP tool descriptions and annotations can help a client understand a tool, but they are not an access-control system. Authorization must be enforced by the server. For the first version, I would not allow users to paste an arbitrary remote MCP endpoint and immediately grant it network access. Start with a curated connector list, known transport behavior and explicit account boundaries. Extensibility can come after we have evidence that the first connector behaves correctly.
  6. PWA is a delivery choice, not a magic trick A Progressive Web App is attractive because one responsive web app can run in a mobile browser and can be installed from supported browsers over HTTPS. It reduces the need to maintain separate native apps just to test the first flow. But a PWA is not identical to a native app. Browser capabilities, background execution, notifications and installation behavior vary. The service worker should cache static interface assets only. Authentication callbacks, API responses, tokens and private search results must not be placed in a shared offline cache. This MVP is not an offline Drive search engine. It needs a network connection to call Google Drive, and a server connection for the hosted API. The interface should say so rather than inventing a spinner that works as a substitute for connectivity.
  7. Test gates: what is known and what is not The checks completed while preparing the source archive were: JSON configuration files parsed. YAML deployment and CI files parsed. TypeScript and TSX files passed a source-syntax transpilation check. This was not a full semantic typecheck. Nine focused unit tests passed for token encryption and tamper rejection, Drive query escaping and limits, log redaction, and same-origin comparison. The checks that remain blocked or unperformed are just as important: npm install could not reach the npm registry because the environment returned EAI_AGAIN during DNS resolution. No package-lock.json was generated. It must be created on a network-enabled machine and reviewed, not fabricated. A normal dependency-backed typecheck and full production build have not passed. PostgreSQL integration has not been run against a live test database. Google OAuth, refresh-token behavior, real Drive search, disconnect behavior, phone installation and deployment have not been tested live. No Render service was provisioned. No production user data was accessed. That is the evidence boundary for this source draft.
  8. The first release should pass a boring acceptance checklist Before adding an AI model, a marketplace or generic remote-server support, the first vertical slice should pass these gates: Install dependencies and generate a committed lockfile. Pass dependency-backed type checking, all unit and integration tests, and the production build. Run the database migration on a disposable test database. Configure a real Google Cloud OAuth client and exact HTTPS redirect URI. From the target phone, complete real sign-in and search for a file that is known to exist. Verify the returned metadata and Drive link against the actual file. Check cancelled consent, invalid state, revoked credentials, unknown results, logout, disconnect and unauthorized requests. Revisit the Google scope and verification requirements before a public beta. Only after those gates pass, add and test a real MCP protocol adapter. That order may look less exciting than a dashboard full of connectors. It is also less likely to produce a dashboard full of connectors that do not work.
  9. What I want to learn from this experiment The main hypothesis is not that one mobile interface magically makes every integration simple. It is that we can move complexity out of the user's path without moving their permissions out of view. For developers, that means a clean service boundary, reusable connector capabilities, explicit schemas and testable authorization rules. For everyone else, it means they should not need to learn what a refresh token is just to find a document they already have permission to see. The current draft is an early step, not proof that the whole idea has succeeded. The build still needs dependency-backed verification, the Google flow needs a real end-to-end test, and the MCP protocol layer still has to be implemented. Those are concrete tasks, not footnotes to hide under a launch graphic. If this works, the phone is not a tiny desktop pretending to be comfortable. It becomes a first-class way to connect useful tools, review permissions and use approved capabilities. And if the first thing we simplify is setup, not security, we might finally make the connection feel like a button instead of a weekend project.

https://github.com/mobile-next/mobile-mcp

Question for the community: what would you want as the first genuinely useful one-tap connector on mobile: Google Drive, GitHub, Notion, a calendar, or something else? And where would you draw the line between convenience and access control?

Top comments (0)