@frontmcp/sdk reference

@frontmcp/sdk is the package a FrontMCP server is written with. Its pages fall into three groups. Decorators declare what the server exposes, from @FrontMcp and @App down to @Tool, @Resource and @Prompt. Context members are what this offers inside execute(), like this.auth, this.get() and this.fail(). Entry points run the same classes over HTTP, in a Worker or another web-standard runtime, or inside your own process. Limits, errors, the scope and the z you write schemas with complete the section. This page is for looking things up; Your First Tool teaches the same pieces in order.

import { App, FrontMcp, Tool, ToolContext, z } from "@frontmcp/sdk";

Decorators

Decorators declare what your server exposes.

PageDeclares
@FrontMcpAn MCP server, the apps it hosts, and the settings that apply to all of them
@AppRelated tools, resources, prompts and providers, grouped into an app the server hosts
@ToolA function an AI model can call, with validated input and optional structured output
@ResourceData at a fixed URI that clients can list and read, like a policy document
@ResourceTemplateA family of resources whose URIs follow a pattern, like ticket://{id}
@PromptA reusable message template that users pick in their client and fill in with arguments
@ProviderA shared service, like a database client or a cache, that tools, resources and prompts get with this.get()
@JobA unit of work with validated input that clients run by name, while they wait or in the background, with retries
@WorkflowJobs chained into steps that run in order or in parallel, each step's input built from the results before it
@AgentA tool that runs its own model loop, with its own instructions and tools, so a client's model can hand it a whole task
@SkillA written procedure, with the tools it uses, that clients find and load for their model to follow
@PluginProviders, tools, resources, prompts, skills and hooks, packaged so an app or a whole server turns them on with one line
@AdapterTools, resources and prompts built from another source when the server starts, like a spec or a list of saved views, and replaced while it runs
@ChannelEvents pushed into a connected Claude Code session as they happen
Hook decoratorsWill, Did, Around and Stage: code that runs before, after, around or inside a stage of every request

Context

Inside execute(), this is the call's context. Context classes lists what this is in a tool, resource, prompt, agent, job, channel or skill, and every member each one has. The members used most have pages of their own:

PageDoes
this.authWho is calling, with their scopes, roles and claims, and what each auth mode and entry point fills in
this.contextThe request a call belongs to: its id, trace, auth info and HTTP metadata, and a store that lasts for the request
this.elicit()Asks the user for input in the middle of a tool call, and continues with their answer as typed data
this.progress()Tells the client how far a long call has got, when the client asked for progress
this.notify()Sends log messages to the client while a tool runs, at the levels the client asked for
this.get()Gets a provider, like a ticket store or your configuration
this.fetch()Calls another HTTP service, with the request's tracing headers and a default timeout
this.fail()Ends a tool call with an error the model can read, carrying the code you choose
this.callTool()Calls another tool of the same server, as the same caller
this.respond()Ends a call early with a result you built yourself, or answers a call from a hook

Running a server

Importing a class decorated with @FrontMcp starts FrontMCP's HTTP server. These run the same configuration in other ways:

PageDoes
FrontMcpInstanceBuilds and runs a server from its @FrontMcp configuration: over HTTP, stdio or a Unix socket, as a serverless handler, or in-process
createFetchHandler()Serves MCP from any runtime that speaks web Request and Response, like Cloudflare Workers, Deno, Bun or a framework's route
create() and createDirect()A server inside your own process, whose tools, resources, prompts and jobs you call as async functions, as a user you name
server.registerTool()Adds a tool to a running create() or createDirect() server from code outside it, and removes it again
connect() and client adaptersAn MCP client connected in memory, with the server's tools and results in the shape Claude, OpenAI, LangChain or the Vercel AI SDK expects

Limits, errors and the scope

PageCovers
Guard optionsA tool's rateLimit, concurrency and timeout, @FrontMcp's throttle, whose calls each limit counts, and the error each one produces
Error classesThe SDK's error classes by area, the code a client receives, whether the message survives production, and whether you throw it or FrontMCP does
Error codesWhat FrontMCP's JSON-RPC error codes and tool error results mean, and how to fix them
Scope and registriesthis.scope: the registries you can list and search, what you can add at runtime, and which parts are FrontMCP's own machinery

z, eagerZ and lazyZ

Every example on this site imports z from @frontmcp/sdk, not from zod. It's Zod 4's z with one change: z.object(), z.strictObject(), z.looseObject(), z.union(), z.discriminatedUnion(), z.intersection(), z.record() and z.tuple() return a stand-in, and the real Zod schema is built the first time the schema is used, by a parse or by reading it, like .shape. Methods you chain on a stand-in, like .optional() and .describe(), return stand-ins too. So loading and starting a server doesn't build the objects in its tools' inputSchema: they're built when a client first lists the tools or calls one. The other factories, like z.string() and z.enum(), are Zod's own.

ExportWhat it is
zZod 4's z, with the factories above deferred. Use it for every schema in a FrontMCP server.
eagerZZod's own z, the same object import { z } from "zod" gives. Its schemas are built when you call it.
lazyZ(build)A schema that calls build() the first time it's used, and only then. For a schema that's costly to make and may never be used, like one converted from a JSON Schema.
isLazy(schema)true for a stand-in, from z's deferred factories or from lazyZ().
forceMaterialize(schema)The real Zod schema, with every stand-in inside it built.
toJSONSchema(schema)Zod's JSON Schema converter, run on forceMaterialize(schema), so it takes any z schema.

A stand-in behaves like the schema it stands for: it parses, instanceof ZodObject is true, and z.infer gives the same type. The SDK exports Zod's classes, like ZodObject and ZodError, and its types, so a FrontMCP server needs no import from zod. Two things to know:

  • Zod's own toJSONSchema() fails on some z schemas. z.toJSONSchema(), or the function imported from zod, throws TypeError: Cannot set properties of undefined (setting 'ref') when an object or a union in the schema is .optional() or has a .default(), like the optional customer below. Convert with toJSONSchema from @frontmcp/sdk, or build that schema with eagerZ.
  • Schemas from zod work in a tool's inputSchema like the SDK's, as long as they're Zod 4: frontmcp create adds zod ^4.0.0 to a new project. Schemas from Zod 3, zod@3 or zod/v3, aren't recognized: the tool is listed with no properties, and every call fails with INVALID_INPUT and Unknown error occurred when trying to parse input.
Open
import { Tool, ToolContext, z } from "@frontmcp/sdk";

export const TicketInput = {
  title: z.string().min(5).describe("One-line summary of the problem"),
  customer: z.object({ email: z.string().email() }).optional().describe("Who reported it, if known"),
};

@Tool({ name: "create_ticket", description: "Open a new support ticket", inputSchema: TicketInput })
export class CreateTicket extends ToolContext {
  async execute(input: { title: string; customer?: { email: string } }) {
    return { id: "T-4", status: "open", ...input };
  }
}

Starting FrontMCP in your browser…

FrontMCP starts when this example comes into view.

Which page for which task

You want toRead
Give the model something to call@Tool
Share a database client or an API client between tools@Provider and this.get()
Refuse a call with a message the model can act onthis.fail() and Error classes
Check who is calling before you actthis.auth
Confirm with the user before a tool actsthis.elicit()
Run work that takes too long for one tool call@Job
Let a model on your server carry out a whole task@Agent
Run code around every call, like logging or auditingHook decorators, packaged as a @Plugin
Generate tools from a spec, a catalog or a list kept somewhere else@Adapter, or the OpenAPI adapter for an OpenAPI spec
Serve from Cloudflare Workers, Deno or BuncreateFetchHandler()
Call your tools from a script or a test, without HTTPcreate() and createDirect()
Give your tools to an agent loop built on Claude, OpenAI, LangChain or the Vercel AI SDKconnect() and client adapters
Cap how often a tool runs, or how long it may takeGuard options
Find out what an error code meansError codes
Hand a schema to code that calls Zod's own functionsz, eagerZ and lazyZ

The three groups in one server

A decorator for each part of the server, context members inside the tool, and the Tests tab running the same app through a second entry point, createDirect(), in-process and as a named user:

Open
import { App, Provider, PublicMcpError, Tool, ToolContext, z } from "@frontmcp/sdk";

@Provider({ name: "TicketStore" })
export class TicketStore {
  private tickets = [
    { id: "T-1", title: "Cannot log in", status: "open" },
    { id: "T-2", title: "Invoice total is wrong", status: "closed" },
  ];
  find(id: string) {
    return this.tickets.find((t) => t.id === id);
  }
}

@Tool({
  name: "get_ticket",
  description: "Get one support ticket by its id",
  inputSchema: { id: z.string().describe("Ticket id, like T-1") },
})
export class GetTicket extends ToolContext {
  async execute({ id }: { id: string }) {
    const ticket = this.get(TicketStore).find(id); // a provider
    if (!ticket) this.fail(new PublicMcpError(`There's no ticket ${id}.`, "NOT_FOUND")); // an error the model reads
    return { ticket, caller: this.auth.user.sub }; // who is calling
  }
}

@App({ id: "help-desk", name: "Help Desk", tools: [GetTicket], providers: [TicketStore] })
export class HelpDeskApp {}

Starting FrontMCP in your browser…

FrontMCP starts when this example comes into view.