Skip to main content
This guide walks you through creating an npm package that FrontMCP servers can load at runtime via App.esm(). By the end, you’ll have a published package with tools, resources, and prompts that any FrontMCP server can consume.

Prerequisites


The Package Manifest Contract

Every ESM package must export a FrontMcpPackageManifest — an object declaring what the package provides:

Step 1: Scaffold the Package


Step 2: Create Tools

FrontMCP ESM packages support two styles for defining tools:

Plain Object Contract

When using plain objects, each tool must have: The execute function receives the parsed input and must return a CallToolResult with a content array.

Step 3: Create Resources & Prompts

Resource Contract

Prompt Contract


Step 4: Export the Manifest

The package must export a FrontMcpPackageManifest. Three export formats are supported:
The default export format is recommended for clarity and compatibility. FrontMCP’s manifest normalizer tries the default export first, then falls back to named exports.

Step 5: Configure package.json

Declare @frontmcp/sdk and zod as peer dependencies, not regular dependencies. The consuming server provides these at runtime. This keeps bundle sizes small and avoids version conflicts.

Step 6: Build & Publish


Step 7: Consume with App.esm()

Once published, any FrontMCP server can load your package:
The server will discover acme:echo, acme:add, and acme:status at startup.

Manifest Normalization

FrontMCP’s normalizeEsmExport() function tries three paths in order:
  1. Default export as manifest — Checks if module.default has name and version fields. If so, validates against the Zod schema.
  2. Default export as decorated class — Checks if module.default is a class with frontmcp:type reflect-metadata. If so, extracts the @FrontMcp configuration.
  3. Named exports — Scans the module for named exports matching manifest primitive keys (tools, prompts, resources, etc.) and assembles them into a manifest.
If none of these paths produce a valid manifest, an EsmManifestInvalidError is thrown.

Best Practices

Tools loaded from ESM packages are often namespaced (e.g., acme:echo). Include clear descriptions so AI models understand what each tool does without seeing the source code.
@frontmcp/sdk and zod should be peer dependencies. Bundling them causes version conflicts and inflates package size.
Use tsup or a similar tool to produce both .mjs and .cjs outputs. FrontMCP handles both formats, but dual output maximizes compatibility.
ESM consumers use semver ranges (^1.0.0, ~2.1.0). Breaking changes should bump the major version. New features bump minor. Bug fixes bump patch.
Set up a local ESM server or use App.esm() with a file path during development to verify your manifest is correct before publishing.

Troubleshooting

Verify your default export has name, version, and a tools array. Check that each tool object has at least name and execute fields. Enable debug logging on the server to see manifest normalization output.
Check that the package is published and accessible. For private registries, verify the token or tokenEnvVar is set correctly. Try npm view @your/package versions to confirm the version exists.
Ensure your package doesn’t import Node.js-only modules (fs, crypto, path) at the top level if it needs to work in browsers. Use dynamic imports for platform-specific code.
In browser environments, avoid any file system operations. The ESM cache is memory-only. Modules are evaluated via Blob URLs, so ensure your code doesn’t rely on __filename or __dirname.

ESM Packages Reference

Full reference for App.esm(), caching, auth, and hot-reload

ESM Errors

Error classes for loading, caching, and authentication