Skip to main content
Product

Turn Your App Into an MCP Server (No Extra Code)

Every app you build on GenMB serves its own MCP endpoint. Annotate a backend function, flip one switch, and Claude or Cursor can call it as a typed tool. Here is the whole setup.

Ambuj Agrawal

Ambuj Agrawal

Founder

7 min read

Your app has a front door for people: a URL, a UI, buttons. Since release 1.7.1 it also has a front door for AI agents. Every app built on GenMB serves its own MCP (Model Context Protocol) endpoint, and the tools on that endpoint are the backend functions you choose to publish.

Nothing is exposed until you say so. There is no separate API to build, no OpenAPI file to maintain, and no server to run. This post walks the whole path: annotate, expose, connect, and (optionally) list publicly.

What becomes a tool

A GenMB app's backend code lives in functions/, one file per handler: functions/track-order.ts or functions/track_order.py. Those functions already power your UI. Exposing one as an MCP tool changes nothing about how it runs.

Two things turn a function into a tool:

  1. An annotation block at the top of the file. One @description line, and one @param line per input. That text is the tool description an agent reads, and the @param lines are parsed into the JSON Schema it fills in.
  2. The "Expose as MCP tool" switch in the app's Functions panel. Every function is off by default, and the switch refuses a function whose annotation is missing or does not parse.

The accepted @param types are the four JSON Schema primitives (string, number, integer, boolean) and typed arrays written as string[], number[], integer[] and boolean[]. Bare object and array are rejected on purpose: they produced a schema with no items or properties, which strict function-calling bridges reject, and they reject the whole tool list, so one bad parameter used to take every other tool on the app down with it. Wrap nested input as a string and parse it inside the handler.

Wrap an input name in square brackets to make it optional: // @param {string} [note].

A worked example: an order-tracking app

Say you built a store admin app with a function that looks up an order. The file starts like this:

```ts

// @description Look up the status and ETA of a customer order by its order number

// @param {string} orderNumber The order number, for example GM-10423

// @param {string} [email] Optional customer email to confirm ownership

export default async function handler(req, ctx) {

const { orderNumber, email } = JSON.parse(req.body || '{}');

// ... your existing lookup

}

`

Open the Functions panel, turn on Expose as MCP tool for track-order, and the app's endpoint now advertises one tool named track-order with a two-property schema. The tool name is the function name, verbatim.

Ask Claude "where is order GM-10423?" and it calls the tool. What comes back to the agent is your function's response body and nothing else: no status code wrapper, no headers. If the function returns a non-2xx status, the agent gets an error instead of a success with an error-shaped body inside, which is the difference between an agent retrying and an agent confidently reporting a wrong answer.

The endpoint URL

One shape, for every app, on every plan:

`

https://genmb.com/api/mcp/app/{appId}/mcp

`

The app id is the one in your editor URL. That endpoint is separate from GenMB's own builder MCP server at https://genmb.com/api/mcp/mcp, which is how Claude edits and deploys your apps. This one is your app answering for itself.

Connecting a client

Claude Code. One command, with your app's URL:

```bash

claude mcp add --transport http myapp https://genmb.com/api/mcp/app/{appId}/mcp

`

Cursor. Add an entry to ~/.cursor/mcp.json (%USERPROFILE%\.cursor\mcp.json on Windows):

```json

{

"mcpServers": {

"myapp": { "url": "https://genmb.com/api/mcp/app/{appId}/mcp" }

}

}

`

Claude Desktop. Its config file is at ~/Library/Application Support/Claude/claude_desktop_config.json on macOS and %APPDATA%\Claude\claude_desktop_config.json on Windows. Use mcp-remote so the browser sign-in flow runs:

```json

{

"mcpServers": {

"myapp": {

"command": "npx",

"args": ["-y", "mcp-remote", "https://genmb.com/api/mcp/app/{appId}/mcp"]

}

}

}

`

You do not have to copy these by hand. Open any app's Add-ons panel, click Connect Claude / Cursor, and the wizard writes the exact config for your client and tells you where to paste it. Cursor also gets a one-click deeplink.

Who is allowed to call it

Unauthenticated requests get nothing. A request with no valid credential is rejected before it reaches the MCP layer, with a WWW-Authenticate header pointing OAuth-capable clients at the sign-in flow, so most clients just open a browser window and ask you to approve once.

There are two kinds of caller:

  • You, the owner. A platform OAuth token or a GenMB API key identifies you. The check is strict ownership: collaborators on the app do not get the endpoint, because it is not a builder surface. A "no such app" and a "not yours" return the same message, so the endpoint cannot be used to guess which app ids exist.
  • One of your app's end users. Their agent signs in through your app's own sign-in page and gets a token bound to that one app. That token is refused on any other app's endpoint. The end user's identity is forwarded to the function, so your ctx.user checks apply exactly as they do in the browser.

Two limits sit on top. MCP requests are capped per caller per app, and function invocations share one budget per app with the editor's test playground rather than doubling it. End-user invocations get their own per-user cap as well, so one agent cannot drain the quota every other visitor depends on.

One thing the platform cannot do for you: a handler that never reads ctx.user returns the same data to every caller. Expose the lookups and actions you are comfortable making callable, and keep the ownership check inside the handler.

What is free and what is paid

Exposing a function as an MCP tool is free on every plan, and so is calling it from your own client. Connecting a client through browser sign-in is free too.

Two things are paid. Manual API keys (for scripts and CI, and for clients without OAuth support) are a Pro capability. And listing your app in GenMB's public MCP registry, so other people can discover it, needs a paid plan and the app's visibility set to public. Both rules are enforced in the same place, whether you list from the MCP listing panel or by ticking the box on the marketplace publish form.

Listing publishes the tool names, their descriptions, and the endpoint URL. It never publishes your function source, your secrets, or your account details. Input schemas are served only from a listing's detail view, not the browse list. Unlist an app, or flip it back to private, and it leaves the registry within seconds.

Why bother

The reason to do this is not that MCP is new. It is that the integration work you would otherwise do (an API surface, auth, docs, a schema, keeping the schema honest) is work you have already done when you wrote the function. Two comment lines and a switch is the whole remaining gap.

Read the MCP docs, see how agent-ready apps work, or start building.

Share this post

Frequently Asked Questions

Do I need to write any code to turn my app into an MCP server?
No new code. You add an @description line and one @param line per input to the top of a backend function you already have, then turn on "Expose as MCP tool" in the Functions panel. The tool description and JSON Schema are parsed from those comments on every save, so there is no separate config to keep in sync.
What is my app’s MCP endpoint URL?
https://genmb.com/api/mcp/app/{appId}/mcp, where appId is the id in your editor URL. That is different from GenMB’s own builder MCP server at https://genmb.com/api/mcp/mcp, which lets Claude browse, edit and deploy your apps.
Is this free, or do I need a paid plan?
Exposing functions as MCP tools and calling them from your own client is free on every plan, and connecting a client through browser sign-in is free. Manual API keys are a Pro capability, and listing your app in the public MCP registry needs a paid plan plus an app whose visibility is set to public.
Can anyone call my app’s tools once it is exposed?
No. Unauthenticated requests are rejected before they reach the MCP layer. Either you authenticate as the owner (strict ownership: collaborators do not get the endpoint), or one of your app’s end users signs in through your app’s own sign-in page and gets a token bound to that one app. Their identity is forwarded to the function, so your ctx.user checks still apply.
How do I connect Claude Code to my app’s MCP server?
Run claude mcp add --transport http myapp https://genmb.com/api/mcp/app/{appId}/mcp with your app id. On first use Claude Code opens a browser window to sign in and approve. For Cursor or Claude Desktop, open the app’s Add-ons panel and click Connect Claude / Cursor to get the exact config file contents.
Ambuj Agrawal

Ambuj Agrawal

Founder

Award-winning AI author and speaker. Building the future of app development at GenMB.

Follow on LinkedIn

Ready to start building?

Turn your ideas into reality with GenMB's AI-powered app builder.