libs/skills/catalog/frontmcp-auth-ui/SKILL.md
Use when customizing, branding, or replacing the built-in FrontMCP OAuth pages (the login, consent, federated-select, incremental-authorization, and error pages) with your own React components. Covers the auth.ui slot-to-file map and auth.extras name-to-handler map on the auth config (no decorator, no class); the @frontmcp/ui/auth React hooks, the AuthPageWrapper component, and mountAuthPage (client-rendered via an esm.sh import-map plus a per-file server-side transform, with no bundling and no SSR); and the framework-owned CSRF and CSP. Triggers: custom login page, brand the consent screen, replace the OAuth UI, custom authorization UI, style the auth pages, multi-step login. The skill for CUSTOM AUTHORIZATION UI (distinct from auth config in frontmcp-config and permissions in frontmcp-authorities).
npx skillsauth add agentfront/frontmcp frontmcp-auth-uiInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
auth.ui)Entry point for replacing FrontMCP's built-in OAuth pages (login, consent, federated, incremental, error) with your own React components. Custom UI is a simple slot→file map (auth.ui) plus an extras name→handler map (auth.extras) on the auth config — there is no decorator and no class. The references/custom-auth-ui.md reference has the full API; the examples/ show a single login slot and a multi-step extras form.
local/remote mode login page with a custom React componentauth.extrasauth.ui / auth.extras in @frontmcp/sdk) and which is the client (@frontmcp/ui/auth)AuthFlowState fields a slot component receives, or the /oauth/ui/extra routelogin / authenticate config in frontmcp-config → configure-auth instead (no React, no build step)create-tool → ui-widgetsauth.ui keeps the built-in pagesDecision: Use this skill when you want to render your OWN component for an auth slot. Use
configure-auth's declarativeloginconfig when tweaking the built-in page's fields is enough.
local or remote auth mode (see frontmcp-config → configure-auth)@frontmcp/ui, react, and react-dom installed (react/react-dom are peer deps of @frontmcp/ui)@frontmcp/ui/auth hooks (see references/custom-auth-ui.md).tsx path under auth.ui — a RELATIVE path auto-anchored to the config file's directory (no fileURLToPath)auth.extras[name] handler function for any mid-flow validated fieldauth: @FrontMcp({ auth: { mode: 'local', ui: { login: './…' }, extras: { … } } }) (per-app under splitByApp — put them on the @App({ auth }) that owns the pages)| Scenario | Reference | Description |
| -------------------------------------------------------------- | ---------------- | ------------------------------------------------------------------------------------ |
| Replace any built-in auth page with a React component | custom-auth-ui | auth.ui: { slot: './file.tsx' }, the hooks, <AuthPageWrapper>, mountAuthPage |
| Add a server-validated mid-flow field (multi-step form) | custom-auth-ui | auth.extras: { name: handler }, the accumulator, useExtraField / useAddedItems |
| Look up the flow-state fields a component receives | custom-auth-ui | AuthFlowState field table (no PII) |
| Understand the served route and the framework-owned CSRF + CSP | custom-auth-ui | /oauth/ui/extra, the esm.sh import-map + inline module, security ownership |
Custom UI is a slot→file map on the auth config. Point each slot at a sibling .tsx/.jsx source (default export); the SDK transpiles it once server-side and inlines it as an ES module, with deps loaded from esm.sh via an import-map and mountAuthPage appended for you — exactly as @Tool({ ui }) leads with the FileSource { file } form:
// src/server.ts
import { App } from '@frontmcp/sdk'; // or @FrontMcp
@App({
auth: {
mode: 'local',
// slot → RELATIVE .tsx, auto-anchored to THIS config file's directory.
ui: { login: './auth/login.tsx' },
// extra name → handler fn (no class).
extras: { 'envs:add': async (input, ctx) => ({ ok: true, addedItems: [{ key: String(input.key) }] }) },
},
})
export default class Server {}
A relative auth.ui path resolves against the directory of the file that declares the @App/@FrontMcp config — captured automatically at decoration time. No fileURLToPath needed. Absolute paths pass through; on capture failure the framework falls back to process.cwd() with a warning.
| Pattern | Correct | Incorrect | Why |
| ------------------ | ---------------------------------------------------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| Slot registration | auth: { ui: { login: './login.tsx' } } | auth: { ui: [LoginAuthUi] } (array of classes) | Custom UI is a slot→file MAP now — no decorator, no class |
| Path anchoring | Relative './login.tsx' (auto-anchored to the config file) | fileURLToPath(new URL('./login.tsx', import.meta.url)) | The framework captures the config file's dir for you — manual anchoring is no longer needed |
| Extra registration | auth: { extras: { 'envs:add': handlerFn } } | auth: { extras: [AddEnvExtra] } (annotated class) | Extras are an extra-name → handler-function MAP now |
| CSRF | Let the hooks round-trip csrfToken | Generating / checking a token in your component | The server mints + verifies CSRF; your component must not |
| User identity | User-typed fields live in your own <form> inputs | Putting email/name into AuthFlowState | AuthFlowState is PII-free by contract — it carries OAuth client ids + control fields only |
| Wrapper form | Wrap UI in <AuthPageWrapper> (renders the finish <form> + hidden fields) | Hand-rolling pending_auth_id / csrf hidden inputs | The wrapper injects the control fields so a no-JS submit still works |
GET /oauth/authorize returns a page with an EMPTY #frontmcp-auth-root (not the built-in page) — the component's rendered markup is NOT in the HTTP response<script type="module"> with the TRANSPILED component (React.createElement, NOT a bundle/IIFE/react-dom/server) + an import { mountAuthPage } from '@frontmcp/ui/auth' tail<script type="importmap"> mapping react + @frontmcp/ui/auth → https://esm.sh/..., with ?external=react,react-dom on the @frontmcp/* URLs (single React)window.__FRONTMCP_AUTH__ with the flow state (and no PII — no email/name field)frame-ancestors 'none', https://esm.sh allowed, NO 'unsafe-eval') and X-Frame-Options: DENY/oauth/ui/<slot>.js route (it 404s — the module is inlined, not served separately)auth.extras) a valid POST /oauth/ui/extra returns { ok: true, addedItems }, an invalid one returns 400, and a bad csrf returns 400auth.ui falls back to the built-in page unchanged| Problem | Cause | Solution |
| ---------------------------------------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Built-in page still shows | Slot not in auth.ui, or the .tsx failed to transpile | Confirm the slot is in auth: { ui: { … } } on the scope that owns the pages; check logs — a transpile error falls back to the built-in page |
| Blank page / @frontmcp/ui/auth 404s in the browser | @frontmcp/ui/auth isn't on esm.sh (unpublished monorepo) | Publish @frontmcp/ui, OR map it to a locally-served ESM URL via @FrontMcp({ ui: { cdnOverrides } }) (see references/custom-auth-ui.md → Local dev / offline) |
| ENOENT / component not found at runtime | Path doesn't resolve from the config file's dir | Use a path relative to the config file (auto-anchored), or an absolute path |
| Hooks throw "must be used inside …" | Component rendered without <AuthPageWrapper> | Mount via mountAuthPage(Component) (it wraps for you) or wrap manually in <AuthPageWrapper> |
| extras POST returns 400 (csrf) | The submitted csrf ≠ the server-minted token | Let the hooks send it — useExtraField / submitExtra attach pending_auth_id + csrf automatically |
| Custom page can't import @frontmcp/ui/auth | Package (or react/react-dom) not installed | npm install @frontmcp/ui react react-dom |
Each reference has matching examples under examples/<reference>/:
custom-auth-ui| Example | Level | Description |
| ----------------------------------------------------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------ |
| login-slot | Intermediate | Replace the built-in login page with a custom React component via auth.ui: { login: './login.tsx' } and useAuthFlow. |
| multi-step-auth-extra | Advanced | Add a server-validated multi-step field with auth.extras: { 'envs:add': fn }, useExtraField, and useAddedItems. |
Skills are distributed as plain SKILL.md files plus a sibling references/
and examples/ tree, so consumers can pick whichever access mode fits:
| Mode | How it works |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Filesystem | Read libs/skills/catalog/frontmcp-auth-ui/ directly from a clone of the catalog repo, or from a published @frontmcp/skills install. SKILL.md is the entry point. |
| frontmcp CLI | frontmcp skills list, frontmcp skills read frontmcp-auth-ui, frontmcp skills read frontmcp-auth-ui:references/<file>.md, frontmcp skills install frontmcp-auth-ui — no server required. |
| MCP skill:// | When a developer mounts this skill into their own FrontMCP server (@FrontMcp({ skills: [...] })), the SDK exposes it via SEP-2640 resources: skill://frontmcp-auth-ui/SKILL.md, skill://frontmcp-auth-ui/references/{file}.md, etc. The server's skill://index.json returns the SEP-2640 discovery document for everything mounted on it. |
The catalog itself is not an MCP server. The skill:// URIs only resolve
when a server has been configured to host this skill.
auth.ui)frontmcp-config (→ configure-auth for the declarative login config), create-tool (→ ui-widgets for tool widgets)tools
ALWAYS use this skill when the user asks to build, modify, or audit a FrontMCP tool. Covers everything inside `@Tool({...})`: class and function-style tools, Zod input/output schemas with derived `execute()` types, dependency injection (`this.get` / `this.tryGet`), error handling (`this.fail`, MCP error classes), throttling (rate-limit / concurrency / timeout), auth providers (single / multi / vault), availability constraints (`availableWhen`), elicitation (`this.elicit`), interactive UI widgets via `@Tool({ ui })` (MCP Apps / SEP-1865 — including `.tsx` FileSource, CSP, `window.FrontMcpBridge`, host-detect `resourceMode`), annotations (`readOnlyHint` / `destructiveHint` / …), `examples` metadata, registration in `@App({ tools })`, and per-tool unit testing. Does NOT cover: - Read-only data exposed via a URI — use `create-resource` - Conversation templates / system prompts — use `create-prompt` - Multi-tool orchestration loops — use `create-agent` - Background work / pipelines — use `create-job` / `create-workflow` - Server-level config (transport, sessions, auth modes) — use `config` / `auth` Triggers: `@Tool`, ToolContext, tool decorator, MCP tool, snake_case tool name, inputSchema, outputSchema, ToolInputOf, ToolOutputOf, `@Tool({ ui })`, tool UI widget, MCP Apps widget, FileSource widget, `.tsx` widget, ui.csp, ui.resourceMode, window.FrontMcpBridge, tool annotations, readOnlyHint, destructiveHint, rate-limit tool, throttle tool, concurrency tool, tool timeout, this.fail, this.respond, this.fetch, this.notify, this.progress, this.elicit, ElicitationDisabledError, ToolContext.execute, this.get(TOKEN), this.tryGet, register tool in @App, tool examples metadata, availableWhen, missingAxes, `tool()` function builder, Tool.esm, Tool.remote, PublicMcpError, ResourceNotFoundError, MCP_ERROR_CODES, ui://widget.
tools
Use when adding tracing, structured logging, metrics, or monitoring to a FrontMCP server. Covers zero-config OpenTelemetry distributed tracing across all flows; the this.telemetry API for custom spans, events, and attributes in tools, plugins, agents, and skills; structured JSON logging with trace correlation and configurable sinks (Winston, Pino, stdout); the off-by-default /metrics endpoint (process and framework metrics, Prometheus-compatible); vendor integrations (Coralogix, Datadog, Logz.io, Grafana Cloud, or any OTLP backend); and testing spans, log correlation, and instrumentation. Triggers: observability, telemetry, tracing, logging, monitoring, OpenTelemetry, OTel, spans, metrics, Prometheus, Datadog, Coralogix, Logz.io, Grafana, Winston, Pino.
development
Use when pushing real-time notifications or events into Claude Code (or another MCP client) sessions, or building two-way chat bridges. Covers channel source types: incoming webhooks (such as GitHub), app error events, agent-completion and job-completion alerts, service connectors, file watchers, and replay buffers; plus two-way conversational bridges connecting WhatsApp, Telegram, Slack, and Discord to a Claude Code session. Triggers: push notifications, real-time alerts, webhook channel, chat bridge, WhatsApp / Telegram / Slack / Discord, agent completion alert, job status notification, error forwarding, server-to-client messaging. The skill for CHANNELS and NOTIFICATIONS.
development
Use when implementing authorization and access control for FrontMCP tools, resources, prompts, or skills, deciding who may invoke what. Covers the RBAC, ABAC, and ReBAC models and when to choose each; JWT claims mapping per identity provider (Auth0, Keycloak, Okta, Cognito, Frontegg); reusable named authority profiles; and custom authority evaluators for domain-specific policy. This is about who-can-do-what (permissions, roles, scopes), distinct from configuring auth modes and login (see frontmcp-config) and custom login UI (see frontmcp-auth-ui). Triggers: authorization, access control, RBAC, ABAC, ReBAC, permissions, roles, scopes, policy enforcement, JWT claims, restrict who can call a tool.