| name | cng-mcp-endpoint |
| description | The concrete Connect&GO (cng) MCP endpoint — how a starter-kit app exposes its functionality as MCP tools for the App Store AI orchestrator, on cng. Use when adding or changing the app's cng MCP endpoint, wiring its auth, or declaring it in app.cng.json. Covers reusing withAppAuthentication (the same brand-agnostic peek-auth token as the UI), the tools/list + tools/call route shape, sharing service-layer logic with the UI, the Node-runtime requirement, and the fact that cng's small REST-only SDK surface is the hard ceiling on what you can expose. Triggers on "cng MCP", "MCP endpoint", "expose cng tools", "tools/list", "tools/call", "App Store AI", "headless cng access", "app_registry_mcp_url", "mcp_url". |
The app's cng MCP endpoint
This is the concrete cng MCP mechanism. The generic model — expose your app to the store AI,
reuse the UI's auth, curate the tool surface (reads by default, gate writes, share logic with the
UI) — lives in mcp-endpoint. Read that for the why and the curation discipline; don't re-derive
it here. This skill is the authoritative how cng wires it.
Not scaffolded today. This starter ships no MCP route for any platform. This skill is how you
add one for cng. When you need a concrete fact (wire protocol, registry key), get it from the
installed @peektravel/app-utilities package / the live doc (javascript-app-utilities) and
TODO(verify) what isn't pinned. Build it as a Node-runtime Next.js Route Handler.
Same authentication as the UI — reuse withAppAuthentication
The MCP endpoint authenticates exactly like every cng UI API route: the brand-agnostic
peek-auth JWT in x-peek-auth: Bearer <token>, verified library-side, yielding an install-scoped
CngAccessService. The only difference from the UI is who holds the token: the browser SPA gets
it from the parent frame; the AI orchestrator obtains its own token for the install and sends
it on the same header. So reuse withAppAuthentication<CngAccessService> (see
cng-embed-and-auth) — do not build a second auth scheme, an API key, or a bearer of your own.
import { type NextRequest, NextResponse } from "next/server";
import { type CngAccessService } from "@peektravel/app-utilities";
import { withAppAuthentication } from "@/lib/with-app";
import { MCP_TOOLS, callTool } from "./tools";
export const POST = withAppAuthentication<CngAccessService>(
async (request: NextRequest, cng: CngAccessService) => {
const req = await request.json();
if (req.method === "tools/list") {
return NextResponse.json({
tools: MCP_TOOLS.map(({ name, description, inputSchema }) => ({ name, description, inputSchema })),
});
}
if (req.method === "tools/call") {
const result = await callTool(req.params., req.., cng);
.({ result });
}
.({ : }, { : });
},
);
The exact MCP wire protocol / transport is volatile — check the installed package (types +
docs/) or the live doc for the precise JSON-RPC methods, the initialize handshake, and the
transport. The shape above (a tools/list returning the definition + a tools/call that
dispatches) is the stable mental model; pin the concrete contract and TODO(verify) gaps.
The tools module — reuse your existing service-layer logic
Each tool is name + description + input schema (JSON Schema) + a handler, and the handler must
call the same service-layer function your cng UI routes already use — don't fork a parallel
implementation.
export const MCP_TOOLS = [
{
name: "list_activities",
description: "List the bookable activities in this Connect&GO account.",
inputSchema: { type: "object", properties: {}, additionalProperties: false },
handler: async (_args: unknown, cng: CngAccessService) => cng.getAllActivities(),
},
] as const;
export async function callTool(name: string, args: unknown, cng: CngAccessService) {
const tool = MCP_TOOLS.find((t) => t.name === name);
if (!tool) throw new Error(`unknown tool: ${name}`);
return tool.handler(args, cng);
}
What to expose — capped by cng's SDK surface
The curation discipline (reads-by-default, gate writes, never expose destructive actions without an
explicit yes, treat tool I/O as PII, write descriptions for a reader that only has the description)
is generic — it lives in mcp-endpoint; follow it there.
The cng-specific reality: cng's typed SDK is small today and is a hard ceiling — the typed
client is the only supported way in (there is no direct-API escape hatch — see cng-backoffice-api),
so you can only expose what it actually offers. Right now that's essentially list_activities →
cng.getAllActivities() (a read,
the safe default). As the cng SDK grows, add one tool per new capability, mirroring the UI actions
you build. Present the proposed list to the user and get sign-off before implementing.
Declaring the endpoint in app.cng.json
Declare the MCP endpoint in the manifest so cng/the orchestrator can find it. The registry
extendable is app_registry_mcp_url@v1 (the same slug across platforms), a registry_extendables
entry with a single parameter, mcp_url (the route cng calls — e.g. /examples/cng/mcp),
alongside the existing app_registry_settings_url@v1:
{
"app_registry_settings_url@v1": { "url": "/examples/cng/main", "url_mode": "prepend_base_url" },
"app_registry_mcp_url@v1": { "mcp_url": "/examples/cng/mcp" }
}
Confirm the current parameters (and whether mcp_url prepends base_url like the settings URL
does) with npx @peektravel/app-cli extensions show app_registry_mcp_url@v1 before shipping —
it prints the extendable's exact fields. That command needs an interactive, signed-in shell (see
cli for the headless caveat). After editing the manifest, re-sync (see cng-manifest-and-deploy).
cng-specific stack flag: Node runtime, not Edge
CngAccessService is Node-only (it verifies the JWT and mints API tokens with Node crypto).
If the route runs on the Edge runtime (export const runtime = "edge", or a host that defaults to
Edge), token verification and the SDK break. Keep the cng MCP route on the Node runtime.
Testing the endpoint
Test it like any authenticated route (see javascript-testing), covering the MCP-specific
assertions: no/invalid token → 401; tools/list returns the definition; tools/call
dispatches to the right handler; each tool stays install-scoped (never reads/acts outside the
verified token's install).
Hard rules
- Same auth as the UI — reuse
withAppAuthentication. No second auth scheme, no API keys.
- Install-scoped only. Build tools from the verified token's
cng client; never act outside
its install scope.
- Share logic with the UI; never fork it. Tools call the same service-layer functions the UI
routes call; keep them in sync as the app grows (also in
AGENTS.md).
CngAccessService is Node-only — keep the route on the Node runtime, not Edge.
- Don't invent the wire protocol or a capability the SDK lacks — the typed SDK is the ceiling on
cng. Get facts from the installed package / live doc;
TODO(verify) gaps. The registry key is
pinned above (app_registry_mcp_url@v1, param mcp_url) — confirm its fields with extensions show rather than guessing.
Related skills
mcp-endpoint (global) — the generic expose-to-the-store-AI / reuse-UI-auth / curate-the-
surface philosophy (don't re-derive it here).
cng-embed-and-auth — the withAppAuthentication pipeline the endpoint reuses verbatim.
cng-backoffice-api — what CngAccessService can do inside a tool handler, the REST-only
capability ceiling, and PII rules for tool I/O.
javascript-nextjs (stack) — the route-handler/runtime flags for a server-to-server endpoint.
cng-manifest-and-deploy — declaring the MCP endpoint URL in app.cng.json / the registry.