How to build an MCP server with Lovable

MCP is the thing everyone asks us about and almost nobody defines the same way twice. Here's what an MCP server actually is, when it's worth building, the three files you write in Lovable, and the auth decision that decides whether the whole thing is safe.

First, the thing everyone conflates

There are two different MCP conversations happening at once and they use identical words. The first is connectors: plugging Notion, Linear or Figma into your builder so the AI writing your app can read your issues and docs. That's MCP pointed inward, at your workflow.

The second is your app as an MCP server: you expose a handful of named tools, and anyone using ChatGPT, Claude, Cursor or Codex can call them. That's MCP pointed outward, at your customers. This guide is about the second one.

The mental model that helps: an MCP server is a menu you hand to an AI. Each item has a name, a description, a shape of input it accepts, and a note on whether calling it is safe or destructive. The assistant reads the menu and decides what to order.

When it's actually worth building

Good fits are boring and useful. Look something up in your product. Create a record. List the things a user owns. Run a check. Anything a customer would otherwise do by opening your app, clicking four times, and copying a value out.

The constraint that catches people out: an MCP tool call is a synchronous request with a client-side timeout on the assistant's end. If the work takes tens of seconds — video generation, a big scrape, a multi-step model chain, OCR over a large document — the assistant shows the call as stuck or interrupted even when your app finished the job fine.

So don't expose the heavy thing as a tool. Expose the fast parts around it: start the job, list the results, fetch one. Let the long-running work stay inside your app where it belongs.

And be honest about whether you need it at all. If nobody is asking to drive your product from an assistant, an MCP server is a demo, not a feature.

The three things you actually write

This is the part that surprises people: you don't write the HTTP endpoint, the JSON-RPC envelope, the transport, or the OAuth metadata route. Those are generated. You write three things.

One file per tool. A server entry that imports the tools and gives the server a name, a version and a line of instructions. One line in the build config that turns the whole thing into a live endpoint. That's the shape of it.

A tool file is small enough to read in one screen — a name, a title, a description, a validated input schema, behaviour hints, and a handler that returns content:

The handler is ordinary application code. It can query your database, call your own logic, hit a third-party API. The only rules are: return quickly, validate the input, and throw a proper tool error when something the caller did was wrong, so the assistant can explain the failure instead of shrugging.

export default defineTool({
  name: "get_order",
  title: "Get order",
  description: "Look up one order by its reference for the signed-in customer.",
  inputSchema: { reference: z.string().min(1) },
  annotations: { readOnlyHint: true, destructiveHint: false },
  handler: async ({ reference }, ctx) => {
    // ctx carries the verified caller — never trust an id from the input
    const order = await findOrder(ctx.getUserId(), reference);
    return { content: [{ type: "text", text: JSON.stringify(order) }] };
  },
});

The auth decision, made properly

This is the only genuinely consequential choice in the whole build, and it takes about ten seconds to get wrong.

Public means no login. Anyone on the internet who has the URL can call every tool and read everything those tools return. That is fine for a currency converter or a docs search. It is catastrophic for anything keyed to a user account.

OAuth means the caller signs in as a real user of your app, and every query runs as that user, with your row-level security intact. If a tool touches customer data, this is the only correct answer. It's more setup — a consent screen, a verified token, tools that derive the user from the token rather than the input — but it's the difference between a feature and a breach.

Two rules we hold without negotiation. Never take a user id from tool input; derive it from the verified token. And never put a service-role key behind an unauthenticated endpoint — that hands your entire database to every caller who finds the URL. If a public tool comes back empty because your security policies block it, that's not a bug to work around. That's the data telling you it isn't public.

Tool descriptions are the product

Nobody believes this until they watch an assistant pick the wrong tool three times in a row. The model chooses what to call based on the name, the title, the description and the hints. That metadata is the interface, in the same way button labels are the interface of a UI.

Write descriptions as one clear sentence about what the tool does and when to use it. Say what it returns. Mark read-only tools as read-only and destructive tools as destructive — assistants treat those hints as permission to act without asking, or not.

Fewer, sharper tools beat a long list of overlapping ones. If two tools could plausibly answer the same request, the model will flip a coin, and your users will experience that as your product being unreliable.

The mistakes we keep fixing

Reading environment variables at the top of the file. The server entry gets evaluated at build time and again on cold start, before secrets exist. Read them inside the handler, where the request is.

Long-running handlers, covered above. The single most common cause of 'my MCP server doesn't work' when the server is fine.

Vague descriptions. 'Handles data' tells the model nothing, so it either never calls the tool or calls it constantly.

Skipping OAuth because it looked like more work, on an app with user accounts. This is the one that ends up in an incident write-up.

Forgetting the boring parts: a favicon, a real server name and title, an instructions line. Those show up verbatim in every connector list your customers see. A server called my-app-mcp with no icon looks abandoned before anyone calls a tool.

How we'd approach your first one

Pick three tools, not fifteen. One read that answers the question your support inbox gets most, one list, one write that's genuinely useful and clearly scoped.

Wire OAuth from the start if there's any user data involved. Retrofitting auth onto a public server is more work than building it right, and in between you're exposed.

Then connect it to Claude or ChatGPT yourself and use it like a customer would for an hour. You'll rewrite half your tool descriptions, and that hour is the difference between a server that demos and a server that people keep connected.

MCP servers, answered.

What is an MCP server?
An MCP server is a small piece of your app that exposes named tools over the Model Context Protocol, so an AI assistant like ChatGPT, Claude, Cursor or Codex can call them directly. Instead of the assistant guessing about your product, it can look things up and act inside it.
Can I connect my Lovable app to ChatGPT or Claude?
Yes. That's exactly what building an MCP server in Lovable gives you — a URL you paste into ChatGPT, Claude, Cursor or Codex, after which your app's tools show up in that assistant's tool list.
Is an MCP server the same as an API?
Under the hood it's HTTP, so it rhymes with an API. The difference is the contract: MCP tools carry names, descriptions and behaviour hints written for a model to read, and the transport handles discovery and auth in a way assistants already understand. You're not documenting an API for humans, you're describing capabilities for an agent.
Do I need OAuth, or can the server be public?
If any tool touches user data, use OAuth. Each caller then signs in as a real user of your app and your row-level security still applies. Public — no login — is only defensible when every tool serves data you'd happily put on a public page, because anyone on the internet with the URL can call it.
How long does it take to build one?
A first working server with two or three read tools is an afternoon. Adding OAuth, write tools, and getting the tool descriptions good enough that an assistant picks the right one reliably is where the rest of the week goes.
Can you build the MCP server for us?
Yes. We do this as a fixed-scope week: we scope the tools, wire the auth properly, and hand you a server your customers can connect to from their own assistant.

Want the server, not the guide?

We scope the tools, wire the auth properly, and hand you an MCP server your customers can connect to from their own assistant. Fixed scope, one week.