---
title: "How to publish /.well-known/mcp/server-card.json on Lovable"
slug: mcp-server-card-lovable
published: 2026-10-07T13:17:46.607582+00:00
updated: 2026-10-07T13:17:46.607582+00:00
author: "AIScan Content Team"
author_url: https://aiscan.site/about
category: "AI Readiness"
tags: MCP, server card, Lovable, well-known, check:P2, platform:lovable
description: "Publish /.well-known/mcp/server-card.json on Lovable: a file in public/ for either template, or an escaped TanStack Start route, with real curl output."
url: https://aiscan.site/blog/mcp-server-card-lovable
---

Lovable builds two kinds of project, and an MCP server card lives in a different place in each. Get the template wrong and you either edit a file that never ships or create a route that answers 404. This guide covers the card for check P2 on both templates. I built a TanStack Start app and a React + Vite app, and every response below was captured from a running server.

The card is the JSON document at `/.well-known/mcp/server-card.json` that tells an agent where your MCP server lives. Its schema is still a draft, so read [Publish an MCP Server Card So Agents Can Find Your Tools](https://aiscan.site/blog/publish-mcp-server-card) first. This post is only the Lovable mechanics for check P2, which [AIScan grades](https://aiscan.site/docs/checks/capabilities).

## Quick summary

| If your Lovable project is on... | Do this | Time |
|---|---|---|
| React + Vite (older projects) | Add `public/.well-known/mcp/server-card.json` and publish | 5 min |
| TanStack Start (new apps since 13 May 2026), fixed card | Same file in `public/` | 5 min |
| TanStack Start, card with CORS and cache headers | Add the route file `src/routes/[.]well-known.mcp.server-card[.]json.ts` | 10 min |
| Either, still on a preview or `lovable.app` workspace URL | Connect a custom domain before judging the result | varies |

Check first: run `npx aiscan-cli yoursite.com` and read check P2 in the Capabilities group.

## Which Lovable template are you on?

According to [Lovable's SEO and AI search docs](https://docs.lovable.dev/features/seo-aeo), apps created from 13 May 2026 use TanStack Start with server-side rendering, and older apps use React + Vite. Look for `routeTree.gen.ts` under `src/`: if it exists, you are on TanStack Start. A plain `index.html` at the project root with a `src/main.tsx` means React + Vite. Lovable also documents an [upgrade path to TanStack Start](https://docs.lovable.dev/features/upgrade-to-tanstack-start).

## Option 1: a file in public/

This works on both templates. Ask the Lovable agent in the project chat to create `public/.well-known/mcp/server-card.json` with your card:

```json
{
  "serverInfo": { "name": "example-mcp", "version": "1.0.0" },
  "description": "Search the example catalog",
  "transport": { "type": "streamable-http", "url": "https://example.com/mcp" },
  "tools": [{ "name": "search", "description": "Search the catalog" }]
}
```

Replace the example values with your real server name, endpoint and tools. The field names follow the shape in AIScan's own P2 guidance and are a convention, not a ratified schema.

On a React + Vite build, `vite build` copied the file untouched to `dist/.well-known/mcp/server-card.json`, and `vite preview` answered with `200` and `application/json`. On TanStack Start the dev server served the same file as `application/json` with `Cache-Control: no-cache`. You cannot set other headers on a plain file from the project code.

## Option 2: a TanStack Start server route

Use this when the card needs CORS headers for browser-based clients. The folder trick from other frameworks does not work here. TanStack Router reads a `.` in a file name as a path separator, so each literal dot has to be escaped. Per the [file naming conventions](https://tanstack.com/router/latest/docs/routing/file-naming-conventions), square brackets escape special characters, and `script[.]js.tsx` becomes `/script.js`.

Create `src/routes/[.]well-known.mcp.server-card[.]json.ts`:

```ts
import { createFileRoute } from '@tanstack/react-router'

const card = {
  serverInfo: { name: 'example-mcp', version: '1.0.0' },
  description: 'Search the example catalog',
  transport: { type: 'streamable-http', url: 'https://example.com/mcp' },
}

export const Route = createFileRoute('/.well-known/mcp/server-card.json')({
  server: {
    handlers: {
      GET: () =>
        Response.json(card, {
          headers: {
            'Access-Control-Allow-Origin': '*',
            'Cache-Control': 'public, max-age=3600',
          },
        }),
    },
  },
})
```

I tested this on `@tanstack/react-start` 1.168.60, `@tanstack/react-router` 1.170.41 and Vite 8.3.3. The generated route tree listed the path `/.well-known/mcp/server-card.json`, and the endpoint returned `200`, `application/json`, `access-control-allow-origin: *` and `cache-control: public, max-age=3600`. A request with a trailing slash also returned `200`.

## The one-character mistake

Leave the second dot unescaped and you get a different URL. I renamed the file to `[.]well-known.mcp.server-card.json.ts` and the route tree registered `/.well-known/mcp/server-card/json`. That path returned `200`. The path AIScan checks, `/.well-known/mcp/server-card.json`, returned `404`. Nothing errors at build time, so the file looks right and the card is missing. Open `routeTree.gen.ts` and read the `path:` line for your route.

## Publish, then test the live domain

A preview is not the live site. [Lovable's docs](https://docs.lovable.dev/features/seo-aeo) say branded workspace URLs are never indexable, and that you should connect a custom domain to build search presence. Publish the project, then test the domain agents will use. How hosting serves files to crawlers is described on Lovable's [hosting page](https://docs.lovable.dev/features/hosting).

## Verify the card

Run AIScan first:

```bash
npx aiscan-cli yoursite.com
```

Find **P2 · MCP Server Card** in the Capabilities group. It should pass. By hand, one request does the same job:

```bash
curl -si https://yoursite.com/.well-known/mcp/server-card.json
```

Expected output for the route version, trimmed:

```
HTTP/2 200
content-type: application/json
access-control-allow-origin: *
cache-control: public, max-age=3600

{"serverInfo":{"name":"example-mcp","version":"1.0.0"}, ...}
```

For the `public/` file you will see `200` and `application/json` without the two custom headers.

## When the card still fails

- **404 on the route version.** The file name has an unescaped dot, or the dev server has not regenerated `routeTree.gen.ts`. Check the `path:` line.
- **200 with an HTML body.** A React + Vite single-page app answers unknown paths with the app shell. In `vite preview`, a misspelled `server-card.jsn` returned `200` with `text/html`. Look at `content-type` and the first bytes of the body, never only the status code. I did not test Lovable's own host for this behavior, so check it with `curl`.
- **Works on the preview, fails on your domain.** The project was edited but not published again.

## Where to go next

Run `npx aiscan-cli yoursite.com`, confirm check P2, then browse the other fixes in the [AIScan guides](https://aiscan.site/guides). The [capabilities checks reference](https://aiscan.site/docs/checks/capabilities) lists P1 to P4 and E2. If your project also lacks a feed, [the RSS guide for Lovable](https://aiscan.site/blog/rss-feed-lovable) covers the same template split, and the [Lovable page](https://aiscan.site/docs/platforms/lovable) lists every guide for the platform. The [Next.js version of this guide](https://aiscan.site/blog/mcp-server-card-nextjs) covers the same check on another framework.

