libs/skills/catalog/frontmcp-guides/SKILL.md
Tutorials, end-to-end walkthroughs, and complete reference projects for FrontMCP. Use when you want a getting-started guide, a full worked example, or to learn best practices by following a step-by-step build rather than a single API reference. Includes a beginner weather-API server (tool plus static resource, Zod schemas, E2E tests), an authenticated task manager (CRUD tools, Redis storage, OAuth, Vercel deploy, authenticated E2E tests), and a multi-app knowledge base (vector search, semantic resources, an AI research agent, audit logging). Triggers: tutorial, walkthrough, how do I build, getting started, end-to-end example, sample project, learn FrontMCP, full app example.
npx skillsauth add agentfront/frontmcp frontmcp-guidesInstall 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.
Complete build walkthroughs and best practices for FrontMCP servers. Each example starts from an empty directory and ends with a deployed, tested server. Every pattern references the specific skill that teaches it.
create-tool under frontmcp-development/references/)frontmcp-development, frontmcp-deployment, etc.)frontmcp-skills-usage)Decision: Use this skill when you want to see how everything fits together. Open individual references under each router's
references/directory when you need focused instruction.
frontmcp CLI available globally (npm install -g frontmcp)Before writing any code, answer these questions:
multi-app-composition)project-structure-standalone, project-structure-nx)configure-auth)configure-session)frontmcp-deployment)configure-transport)setup-testing)Skills used: setup-project, create-tool, create-resource, setup-testing, deploy-to-node
A simple MCP server that exposes a weather lookup tool and a resource for supported cities.
weather-api/
├── src/
│ ├── main.ts # @FrontMcp server (deploy-to-node)
│ ├── weather.app.ts # @App with tools and resources
│ ├── tools/
│ │ └── get-weather.tool.ts # @Tool: fetch weather by city (create-tool)
│ └── resources/
│ └── cities.resource.ts # @Resource: list supported cities (create-resource)
├── test/
│ ├── get-weather.tool.spec.ts # Unit tests (setup-testing)
│ └── weather.e2e.spec.ts # E2E protocol test (setup-testing)
└── package.json
Server entry point (setup-project):
import { FrontMcp } from '@frontmcp/sdk';
import { WeatherApp } from './weather.app';
@FrontMcp({
info: { name: 'weather-api', version: '1.0.0' },
apps: [WeatherApp],
})
export default class WeatherServer {}
Tool (create-tool):
import { Tool, ToolContext, z } from '@frontmcp/sdk';
@Tool({
name: 'get_weather',
description: 'Get current weather for a city',
inputSchema: {
city: z.string().min(1).describe('City name'),
},
outputSchema: {
temperature: z.number(),
condition: z.string(),
humidity: z.number(),
},
})
export class GetWeatherTool extends ToolContext {
async execute(input: { city: string }) {
const city = encodeURIComponent(input.city);
const data = await this.fetch(`https://api.weather.example.com/v1?city=${city}`);
const json = await data.json();
return { temperature: json.temp, condition: json.condition, humidity: json.humidity };
}
}
Resource (create-resource):
import { ReadResourceResult, Resource, ResourceContext } from '@frontmcp/sdk';
@Resource({
uri: 'weather://cities',
name: 'Supported Cities',
description: 'List of cities with weather data',
mimeType: 'application/json',
})
export class CitiesResource extends ResourceContext {
async execute(uri: string): Promise<ReadResourceResult> {
return {
contents: [
{
uri,
mimeType: 'application/json',
text: JSON.stringify(['London', 'Tokyo', 'New York', 'Paris', 'Sydney']),
},
],
};
}
}
Full working code: See
references/example-weather-api.md
Skills used: setup-project, create-tool, create-provider, configure-auth, configure-session, setup-redis, setup-testing, deploy-to-vercel
An authenticated task management server with CRUD tools, Redis storage, and OAuth.
task-manager/
├── src/
│ ├── main.ts # @FrontMcp with auth: { mode: 'remote' }
│ ├── tasks.app.ts # @App with CRUD tools + provider
│ ├── providers/
│ │ └── task-store.provider.ts # @Provider: Redis-backed task storage (create-provider)
│ ├── tools/
│ │ ├── create-task.tool.ts # @Tool: create a task (create-tool)
│ │ ├── list-tasks.tool.ts # @Tool: list tasks (create-tool)
│ │ ├── update-task.tool.ts # @Tool: update task status (create-tool)
│ │ └── delete-task.tool.ts # @Tool: delete a task (create-tool)
│ └── types/
│ └── task.ts # Shared task interface
├── test/
│ ├── *.spec.ts # Unit tests per tool
│ └── tasks.e2e.spec.ts # E2E with auth flow
├── vercel.json # Vercel config (deploy-to-vercel)
└── package.json
Server with auth (configure-auth, configure-session, setup-redis):
@FrontMcp({
info: { name: 'task-manager', version: '1.0.0' },
apps: [TasksApp],
auth: { mode: 'remote', provider: 'https://auth.example.com', clientId: 'my-client-id' },
redis: { provider: 'redis', host: process.env.REDIS_URL ?? 'localhost' },
})
export default class TaskManagerServer {}
Provider for shared storage (create-provider):
import { Provider, ProviderScope } from '@frontmcp/sdk';
@Provider({ name: 'task-store', scope: ProviderScope.GLOBAL })
export class TaskStoreProvider {
async create(task: Task): Promise<Task> {
/* Redis-backed implementation */
}
async list(userId: string): Promise<Task[]> {
/* ... */
}
async update(id: string, data: Partial<Task>): Promise<Task> {
/* ... */
}
async delete(id: string): Promise<void> {
/* ... */
}
}
Use the class itself as the DI token (
this.get(TaskStoreProvider)). For factory-built singletons (e.g. when you need async setup before the class is constructed), useAsyncProvider({ provide, name, scope, useFactory })instead.
Tool with DI (create-tool + create-provider):
import { Tool, ToolContext, UnauthorizedError, z } from '@frontmcp/sdk';
@Tool({
name: 'create_task',
description: 'Create a new task',
inputSchema: {
title: z.string().min(1).describe('Task title'),
priority: z.enum(['low', 'medium', 'high']).default('medium'),
},
outputSchema: { id: z.string(), title: z.string(), priority: z.string(), status: z.string() },
})
export class CreateTaskTool extends ToolContext {
async execute(input: { title: string; priority: string }) {
const store = this.get(TaskStoreProvider);
const userId = this.auth?.user.sub;
if (!userId) this.fail(new UnauthorizedError('Authentication required'));
return store.create({ title: input.title, priority: input.priority, status: 'pending', userId });
}
}
Full working code: See
references/example-task-manager.md
Skills used: setup-project, multi-app-composition, create-tool, create-resource, create-agent, create-skill-with-tools, create-plugin, official-plugins, configure-auth, deploy-to-vercel
A multi-app knowledge base with AI-powered search, document ingestion, and an autonomous research agent.
knowledge-base/
├── src/
│ ├── main.ts # @FrontMcp composing 3 apps
│ ├── ingestion/
│ │ ├── ingestion.app.ts # @App: document ingestion
│ │ ├── tools/ingest-document.tool.ts
│ │ └── providers/vector-store.provider.ts
│ ├── search/
│ │ ├── search.app.ts # @App: search and retrieval
│ │ ├── tools/search-docs.tool.ts
│ │ └── resources/doc.resource.ts
│ ├── research/
│ │ ├── research.app.ts # @App: AI research agent
│ │ └── agents/researcher.agent.ts # @Agent: autonomous research loop
│ └── plugins/
│ └── audit-log.plugin.ts # @Plugin: audit logging
├── test/
│ └── *.spec.ts
├── vercel.json
└── package.json
Multi-app composition (multi-app-composition):
@FrontMcp({
info: { name: 'knowledge-base', version: '1.0.0' },
apps: [IngestionApp, SearchApp, ResearchApp],
plugins: [AuditLogPlugin],
auth: { mode: 'remote', provider: 'https://auth.example.com', clientId: 'my-client-id' },
redis: { provider: 'redis', host: process.env.REDIS_URL ?? 'localhost' },
})
export default class KnowledgeBaseServer {}
AI Research Agent (create-agent):
@Agent({
name: 'research_topic',
description: 'Research a topic across the knowledge base and synthesize findings',
inputSchema: {
topic: z.string().describe('Research topic'),
depth: z.enum(['shallow', 'deep']).default('shallow'),
},
llm: {
provider: 'anthropic',
model: 'claude-sonnet-4-6',
apiKey: { env: 'ANTHROPIC_API_KEY' },
maxTokens: 4096,
}, // provider and model are client-configurable
execution: { maxIterations: 5 },
systemInstructions:
'Search for relevant documents using search_docs, synthesize findings, and produce a structured summary with source attribution.',
tools: [SearchDocsTool, IngestDocumentTool],
})
export class ResearcherAgent extends AgentContext {}
The framework drives the LLM tool-use loop via the
agents:call-agentflow — you don't overrideexecute(). Configure iteration limits and other runtime knobs through the@Agent({ execution: { maxIterations } })block.Full working code: See
references/example-knowledge-base.md
| Practice | Why | Reference |
| ------------------------------------------------------ | ----------------------------------------------------------------- | ------------------------------------- |
| Start with the @App boundaries, not individual tools | Apps define module boundaries; tools are implementation details | multi-app-composition |
| Choose auth mode and storage before writing tools | Auth affects session handling, which affects storage requirements | configure-auth, configure-session |
| Pick your deployment target early | Target determines transport, storage, and build constraints | frontmcp-deployment |
| Practice | Why | Reference |
| ------------------------------------------------- | ----------------------------------------------------------- | ------------------------------ |
| One class per file with <name>.<type>.ts naming | Consistency, generator compatibility, clear imports | project-structure-standalone |
| Group by feature, not by type, for 10+ components | Feature folders scale better than flat tools/ directories | project-structure-standalone |
| Extract shared logic into @Provider classes | Testable, lifecycle-managed, injected via DI | create-provider |
| Practice | Why | Reference |
| ----------------------------------------------- | ------------------------------------------------------------- | ----------------- |
| Always define outputSchema on tools | Prevents data leaks, enables CodeCall chaining | create-tool |
| Use this.fail() with MCP error classes | Proper error codes in protocol responses | create-tool |
| Use this.get(TOKEN) not this.tryGet(TOKEN)! | Clear error on missing dependency vs silent null | create-provider |
| Use Zod raw shapes, not z.object() | Framework wraps internally; double-wrapping breaks validation | create-tool |
| Pattern | Correct | Incorrect | Why |
| ----------------- | ------------------------------------------- | ----------------------------------------- | --------------------------------------------------------- |
| Project start | Plan apps and auth first, then build tools | Jump straight into writing tools | Architecture decisions are expensive to change later |
| Code organization | Feature folders with <name>.<type>.ts | Flat directory with generic names | Scales to large projects and matches generator output |
| Shared state | @Provider with DI token | Module-level singleton or global variable | DI is testable, lifecycle-managed, and scoped per request |
| Error handling | this.fail(new ResourceNotFoundError(...)) | throw new Error('not found') | MCP error codes enable proper client error handling |
| Testing | Unit tests per component + E2E for protocol | Only E2E tests or only unit tests | Both layers catch different types of bugs |
outputSchema defined<name>.<type>.ts naming convention.spec.ts extension| Problem | Cause | Solution |
| ---------------------------------------- | ------------------------------------------------------ | ------------------------------------------------------------ |
| Unsure where to start | No project plan | Run through the Planning Checklist above before writing code |
| Architecture feels wrong | Wrong app boundaries or component types | Review the Scenario Routing Table in frontmcp-development |
| Feature works locally but fails deployed | Environment-specific config (storage, auth, transport) | Check the Target Comparison in frontmcp-deployment |
| Tests pass but coverage below 95% | Missing error path or branch tests | Run jest --coverage and add tests for uncovered lines |
| Provider state leaking between requests | Using module-level state instead of DI | Move state into a @Provider scoped per request |
Each reference has matching examples under examples/<reference>/:
example-knowledge-base| Example | Level | Description |
| ------------------------------------------------------------------------------------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
| agent-and-plugin | Advanced | Shows an autonomous research agent with inner tools and configurable depth, and a plugin that hooks into tool execution for audit logging. |
| multi-app-composition | Basic | Shows how to compose multiple apps (Ingestion, Search, Research) into a single server with shared providers, plugins, and agent registration. |
| vector-search-and-resources | Intermediate | Shows a semantic search tool with embedding generation and a resource template for retrieving documents by ID using URI parameters. |
example-task-manager| Example | Level | Description |
| --------------------------------------------------------------------------------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| auth-and-crud-tools | Basic | Shows how to create CRUD tools with authentication, using this.context.session for user isolation and this.get() for dependency injection. |
| authenticated-e2e-tests | Advanced | Shows how to write E2E tests with authentication using TestTokenFactory, and unit tests for tools that require session context. |
| redis-provider-with-di | Intermediate | Shows how to create a Redis-backed provider with a DI token, lifecycle hooks (onInit/onDestroy), and how tools inject it. |
example-weather-api| Example | Level | Description |
| ------------------------------------------------------------------------------------------ | ------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
| server-and-app-setup | Basic | Shows the server entry point, app registration, and static resource for a beginner FrontMCP weather API server. |
| unit-and-e2e-tests | Intermediate | Shows how to write unit tests for tools by mocking context methods, and E2E tests using McpTestClient and TestServer. |
| weather-tool-with-schemas | Basic | Shows how to create a tool with Zod input and output schemas, use this.fetch() for HTTP calls, and handle errors with this.fail(). |
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-guides/ 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-guides, frontmcp skills read frontmcp-guides:references/<file>.md, frontmcp skills install frontmcp-guides — 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-guides/SKILL.md, skill://frontmcp-guides/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.
frontmcp-development, frontmcp-deployment, frontmcp-testing, frontmcp-configsetup-project, create-tool, create-resource, create-provider, create-agent, configure-auth, setup-testing (each lives under its parent router's references/ directory, e.g. frontmcp-development/references/create-tool.md)McpError from @frontmcp/protocol (never directly from @modelcontextprotocol/sdk); use @frontmcp/utils for crypto and file-system operations.tools
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).
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.