Point0by 1gr14
  • Меню
    • GitHub
    • Блог
  • Introduction
    • Overview
    • Getting Started
    • Full Overview
    • Benchmarks
    • Points
  • Points
    • Page
    • Layout
    • Component
    • Provider
    • Mountable
    • Query
    • Infinite Query
    • Mutation
    • Action
    • Root
    • Base
    • Plugin
  • Methods
    • Validation
    • Loader
    • Context
    • Middleware
    • Loading & error
    • .with
    • Mapper
    • Transformer
    • Stage Methods
  • Core
    • Navigation
    • SSR
    • RSC
    • Request
    • Response
    • Error handling
    • Env
    • Head
    • MDX
    • Assets
    • File upload
    • OpenAPI
    • Query client
    • Events
    • Infer
  • Engine
    • Engine Config
    • Engine Runtime
    • CLI
    • Dev
    • Build
    • Compiler
    • Generator
    • Project MCP
    • Docs MCP
    • Importer
    • Public dir
    • Testing
    • Deploy
    • Bun or Vite
  • Extra
    • SsrStore
    • CookieStore
    • Basic Auth
    • CORS
    • Cache-Control
    • Compress
  • Examples
    • Basic
    • Vite
    • Better Auth
    • Capacitor
    • Expo
  • 1gr14
    • Главная
    • Start0
    • Поддержать
    • Обучение
    • Группа
    • Блог
    • Автор
  • Сообщество
    • Discord
    • Telegram
  • Опенсорс
    • Point0
    • Route0
    • Error0
    • Flat
  • Аккаунт
    • Войти
    • Регистрация
Point0by 1gr14
Создаю опенсорс во славу Господа Иисуса Христа ☦️
С любовью ко всем разработчикам на свете ❤️
Условия использованияПолитика конфиденциальностиСергей Дмитриев 2026 😎

Project MCP

  • Раздел: Engine

On this page

  1. Wiring it in
  2. The --meta flag
  3. The meta.ts it reads
  4. Four tools
  5. list_points — list with filters
  6. get_point — first match
  7. compile — see the compiled output
  8. trace — follow an import chain
  9. Reference
  10. --meta
  11. Tools
  12. Filters (shared by list_points + get_point)
  13. compile / trace engine resolution
  14. Compiling against a built project
  15. See also

point0-project-mcp is an MCP server shipped with @point0/engine. Point it at your project and an agent (Claude Code, Cursor, …) gets four tools for navigating the codebase: list every point, find one by URL, see what a file compiles to on the server vs. the client, and trace why one file imports another. It reads a generated meta.ts, so it answers questions about your points, not generic ones.

// .mcp.json (Claude Code) — or .cursor/mcp.json for Cursor (byte-identical)
{
  "mcpServers": {
    "point0-project": { "command": "bun", "args": ["run", "mcp:project"] },
    "point0-docs": { "command": "bun", "args": ["run", "mcp:docs"] },
  },
}
// package.json — the script the config above runs
"mcp:project": "point0-project-mcp --meta ./src/generated/point0/meta.ts",

create-point0-app writes both files for you, so a fresh app has the project MCP (and the docs MCP) wired out of the box.

Wiring it in

The config calls bun run mcp:project, not the bin directly. That indirection matters: the script runs with the working directory set to the project root, so a relative --meta path resolves correctly and the workspace point0-project-mcp bin is found.

create-point0-app writes only the Claude Code and Cursor configs. Other MCP clients (VS Code, Windsurf, Zed, …) aren't scaffolded, but the server is plain stdio: give any stdio-MCP client the same command/args (bun run mcp:project) in its own config location.

NAME GOTCHA. Three names look alike, don't mix them up: the bin is point0-project-mcp; the server's advertised name is point0-project (the key under mcpServers); the docs server is a separate bin, point0-docs-mcp. An older point0-mcp bin name shows up in some configs in the wild — it does not exist. Use point0-project-mcp.

The --meta flag

The server takes one or more --meta <path> flags and nothing else:

point0-project-mcp --meta ./src/generated/point0/meta.ts
# multiple metas — one MCP across several apps in a monorepo
point0-project-mcp --meta ./apps/web/src/generated/point0/meta.ts \
                   --meta ./apps/admin/src/generated/point0/meta.ts

Rules, enforced at parse time:

  • At least one --meta is required — none throws At least one '--meta <path>' flag is required.
  • Each --meta must be followed by a path — a missing value, or a value that starts with --, throws Each '--meta' flag must be followed by a path.
  • A relative path resolves against the working directory, which is why you launch via bun run mcp:project (cwd = project root).

There are no other flags — no --help, no --version. Anything that isn't a --meta pair is ignored.

CONNECT GOTCHA. --meta is validated lazily: the server connects fine even with no --meta, and the "required" error only surfaces on the first tool call. A clean connection does not mean the flags are right.

The meta.ts it reads

The MCP does not analyze your source live. It loads a generated meta.ts — a snapshot of every point: id, type, name, tags, route, endpoint, source position, validity, SSR flag, parents, layouts. You produce it by setting generate.meta in the engine config:

// src/engine.ts
export const engine = Engine.create({
  file: import.meta.url,
  generate: {
    meta: './generated/point0/meta.ts', // the file the MCP reads
    assetsTypes: './generated/point0/assets.d.ts',
  },
  // …
})

Run point0 generate (your app's generate script) once and the meta appears. It lives under src/generated/, which is gitignored — so a fresh checkout must run generate before the MCP can answer.

You do not restart the MCP after regenerating. The server is long-lived; it re-checks each meta's modification time on every tool call and re-imports it when it changed, so the next call sees the new points.

Four tools

list_points — list with filters

Lists points, with filtering, pagination, and optional field selection.

// arguments
{ "type": "page", "ssr": true, "fields": ["id", "name", "route"], "limit": 50 }
// structuredContent
{
  "points": [/* … */],
  "total": 12,
  "hasMore": false,
  "nextOffset": null,
}

limit defaults to 100, offset to 0. hasMore is true when total > offset + limit, and nextOffset is the offset to pass next (otherwise absent). The result also comes back as pretty-printed JSON text, so an agent that ignores structuredContent still reads it.

get_point — first match

Returns the first point matching the filters — same filter shape as list_points, minus limit/offset. Use it to resolve a concrete coordinate: here's my URL, find me the page, or find me the layout named dashboard.

Ask for one field and you get just that — the cheapest way to resolve a URL to a point id:

// arguments — "here's my URL, find me the page serving it"
{ "url": "/mcp", "fields": ["id"] }
// structuredContent
{ "id": "root:page:mcpPage" }

Drop fields and the full point comes back — the same snapshot list_points returns:

// arguments — "find me the layout named dashboard, with all its details"
{ "type": "layout", "name": "dashboard" }
// structuredContent — the full point record
{
  "id": "root:layout:dashboard",
  "scope": "root",
  "type": "layout",
  "name": "dashboard",
  "tags": ["root", "layout"],
  "description": "App shell with the sidebar nav.",
  "route": "/dashboard",
  "endpoint": null,
  "pos": { "file": "/abs/src/layouts/dashboard.tsx", "line": 4, "column": 0 },
  "valid": true,
  "errors": [],
  "ssr": true,
  "parents": [
    { "id": "root:root:root", "scope": "root", "type": "root", "name": "root" },
  ],
  "layouts": [],
}

From a single URL or name the agent gets the point's id, its source pos (file + line, to jump straight to the definition), whether it server-renders (ssr), its endpoint — the primary HTTP method, the full set of methods it answers to, and the route — if it has one, and the parents/layouts it sits inside. A query or mutation comes back with its endpoint ({ "method": "POST", "methods": ["POST"], "route": "/_point0/root/mutation/sign-in" }) instead of a page route. A query is a GET that also answers to POST (so "methods": ["GET", "POST"]) — the client falls back to a POST body for a binary or over-long input.

No match is not an error: the text comes back as "null" and structuredContent is absent.

compile — see the compiled output

Shows what a file compiles to, using the compiler options from your engine. Useful for seeing which server-only or client-only code is stripped — the same transform as point0 compile.

// arguments
{ "file": "src/mcp.points.tsx", "side": "server", "scope": "root" }
// structuredContent.code — server side: the `.lets` page/action stay as runtime calls
import { root } from './lib/root.js';
export const mcpPage = root.lets('page', 'mcpPage', '/mcp').tag('root', 'page').page(() => <div>MCP page</div>);
export const mcpActionAction = root.lets("action", "mcpAction", 'GET', '/api/mcp').tag('root', 'action').action(() => ({
  ok: true
}));

side ('server' / 'client') and scope are optional — Point0 infers them when it can. hmr: true turns on the HMR fix in the output; it defaults to off here (unlike the CLI, which inherits the engine's hmrFix when you pass no flag). A relative file resolves against the working directory. A compile error is surfaced as a thrown MCP error.

trace — follow an import chain

Traces how one file reaches an import target, through the compiler — useful for understanding why a file pulls in another (and catching a server util that leaked to the client).

// arguments
{
  "target": "./mcp-target.js",
  "source": "src/mcp-source.ts",
  "side": "server",
  "scope": "root",
}
// structuredContent — each line is "file:line:column"
{ "trace": ["/abs/src/mcp-source.ts:3:0", "/abs/src/mcp-mid.ts:1:0", "…"] }

source resolves against the working directory if relative. cwd is optional — it defaults to the engine file's directory, not the process cwd. If no chain is found, the tool throws Trace was not found.

Reference

--meta

FlagMeaning
--meta <path>A generated meta.ts to load. Repeatable. At least one required. Relative paths resolve against the working directory.

No other flags exist (--help / --version are not handled). Validation is lazy: errors surface on the first tool call, not at connect.

Tools

ToolDoesKey inputs
list_pointsList points with filtering + paginationfilter fields, fields, limit (100), offset (0)
get_pointFirst point matching the filterfilter fields, fields
compileCompiled output of a filefile*, side, scope, hmr, engineFile
traceImport chain from a source to a targettarget, source, side, scope, cwd, engineFile

* = required.

Filters (shared by list_points + get_point)

FilterTypeMatches
idstringexact point id
idsstring[]id is in the list
tagsstring | string[]string = any tag equals it; array = all required
scopestringexact scope
typestringexact type — page / layout / query / mutation / action / …
namestringexact name
routestringexact route definition
urlstringfull URL or path, matched against point.route via Route0
endpointMethodstringan HTTP method the endpoint answers to (a query matches both GET and POST)
endpointRoutestringexact endpoint route definition
endpointUrlstringfull URL or path, matched against the endpoint route
validbooleanpoint validity
ssrbooleanSSR flag
filestringexact source file path (point.pos.file)
parentIdstringone of the point's parents has this id
layoutIdstringone of the point's layouts has this id

fields selects which point fields come back. Selectable: id, scope, type, name, tags, description, route, endpoint, pos, valid, errors, ssr, parents, layouts. The import thunk is never selectable and never appears in output.

compile / trace engine resolution

Both tools import the real engine from the meta, then pick a side and scope.

  • engineFile — required only when several metas each carry an engine; with one engine it's inferred. An unknown file, or zero engines, throws.
  • side / scope — inferred when unambiguous. A scope available on both server and client without an explicit side throws, as does a missing scope.
  • Compiler disabled for the chosen side/scope throws Compiler is disabled for <side> scope "<scope>".
  • Both set NODE_ENV to development when it's unset, and throw on a value outside production / development / test. (list_points / get_point never touch NODE_ENV.)

Compiling against a built project

compile and trace apply the same transform as a dev build by default. To run the built transform instead — the same as point0 compile --built — pass built: true on the call:

{ "file": "src/pages/home.tsx", "built": true } // compile / trace input

It mirrors the CLI flag, per call. (POINT0_BUILT=true in the server's env is the fallback default when the input is omitted.)

See also

  • Generator — produces the meta.ts this server reads.
  • Compiler — the transform behind compile and trace; also the point0 compile CLI.
  • CLI — point0 generate and friends.
  • Docs MCP — the companion point0-docs-mcp server for searching the Point0 docs.
НазадGeneratorДалееDocs MCP

Enjoying Point0?

Give it a star on GitHub, ask questions, and follow what's new

Star Point0

A star on GitHub helps more developers find Point0
Star on GitHub

Start0

Point0 integrated into the Start0 SaaS boilerplate — the best way to use it
Explore Start0

YouTube

Tutorials, walkthroughs, and updates
YouTube

Discord

Questions and chat in English
Join Discord

Telegram

News channel and chat in Russian
ChannelChat

Twitter

Posts and updates
Twitter