This enables platform-specific tools (e.g., Apple Notes on macOS only), runtime-specific resources (e.g., Node.js file system), and environment-gated features (e.g., debug tools in development only).
The availableWhen Option
Add availableWhen to any entry’s metadata to constrain its availability:
Matching Semantics
- AND across fields — all specified fields must match
- OR within arrays — at least one value in an array must match
- Omitted fields — unconstrained (matches everything)
- Empty array — matches nothing (entry is never available)
- No
availableWhen— always available (default)
Available Fields
os (replaces platform)
Operating system, matching process.platform values. Renamed from platform in issue #417 — the old name still works as a deprecated alias.
| Value | OS |
|---|---|
'darwin' | macOS |
'linux' | Linux |
'win32' | Windows |
'freebsd' | FreeBSD |
runtime
JavaScript runtime:
| Value | Runtime |
|---|---|
'node' | Node.js |
'bun' | Bun |
'deno' | Deno |
'edge' | Edge runtime (Vercel, CF) |
'browser' | Browser |
deployment
Coarse deployment mode:
| Value | Description |
|---|---|
'standalone' | Standard server |
'serverless' | Serverless (Lambda, Vercel) |
'distributed' | Multi-pod HA mode |
'browser' | Browser bundle |
provider (issue #417)
Discriminated deploy provider. Lets @Tool({ availableWhen: { provider: ['vercel'] } }) express provider-specific rules where deployment: ['serverless'] is too coarse.
| Value | Detected via env var |
|---|---|
'bare' | No discriminating env var (default) |
'docker' | /.dockerenv exists |
'vercel' | VERCEL |
'lambda' | AWS_LAMBDA_FUNCTION_NAME |
'cloudflare' | CF_PAGES |
'netlify' | NETLIFY |
'azure' | AZURE_FUNCTIONS_ENVIRONMENT |
'gcp' | K_SERVICE |
'fly' | FLY_APP_NAME |
'render' | RENDER |
'railway' | RAILWAY_ENVIRONMENT |
FRONTMCP_PROVIDER=<name> (useful for tests, Docker images without a discriminating env var, etc.).
target (issue #417)
Build target produced by frontmcp build --target <x>. Always 'unknown' in dev (frontmcp dev).
| Value | Source |
|---|---|
'unknown' | Dev mode (no build) or unrecognized value |
'node' | frontmcp build --target node |
'distributed' | frontmcp build --target distributed |
'cli' | frontmcp build --target cli |
'vercel' | frontmcp build --target vercel |
'lambda' | frontmcp build --target lambda |
'cloudflare' | frontmcp build --target cloudflare |
'browser' | frontmcp build --target browser |
'sdk' | frontmcp build --target sdk |
'mcpb' | frontmcp build --target mcpb |
globalThis.FRONTMCP_BUILD_TARGET (inlined by the adapter) → process.env.FRONTMCP_BUILD_TARGET → 'unknown'.
surface (issue #417)
Per-call axis — set by the transport adapter / dispatcher on the request ctx to discriminate “who is calling this tool.” Unlike the other axes (which are constant for the process lifetime), surface varies per request.
| Value | Source |
|---|---|
'mcp' | MCP tools/call (set by the MCP handler) |
'cli' | A frontmcp <verb> subcommand router (issue #409) |
'agent' | Agent-to-tool dispatch (ExecutionContextBase.callTool) |
'job' | Job runner |
'http-trigger' | @Channel HTTP triggers |
env
NODE_ENV value:
| Value | Description |
|---|---|
'production' | Production |
'development' | Development |
'test' | Testing |
Structured errors
When a tool exists but itsavailableWhen constraint fails at call time, FrontMCP throws EntryUnavailableError with a missingAxes array in data (issue #417). Clients can surface “this tool isn’t reachable because provider=vercel / surface=mcp / …” without parsing prose.
Supported Entry Types
availableWhen works on all five entry types:
- Tool
- Resource
- Prompt
- Skill
- Agent
Runtime Context API
Insideexecute() methods, use runtime context helpers for imperative checks:
Available Methods
RuntimeContext exposes the operating system via the os property; platform remains as a deprecated alias for backward compatibility (issue #417) and resolves to the same value. New code should read this.runtimeContext.os directly.
| Method | Returns | Description |
|---|---|---|
this.runtimeContext.os | string | Modern OS axis access — same values as process.platform ('darwin', 'linux', 'win32', …) |
this.isPlatform('darwin') | boolean | Deprecated alias — checks the legacy .platform field. Prefer this.runtimeContext.os === 'darwin'. |
this.isRuntime('node') | boolean | Check JavaScript runtime |
this.isDeployment('serverless') | boolean | Check deployment mode |
this.isEnv('production') | boolean | Check NODE_ENV |
this.runtimeContext | RuntimeContext | Full context object (os, platform, runtime, deployment, env) |
ToolContext, ResourceContext, PromptContext, and AgentContext.
Multi-Platform Pattern
When building tools that serve the same purpose across platforms, use separate files withavailableWhen in each:
Error Handling
When a client tries to call a tool that exists but is unavailable in the current environment, the SDK returns anEntryUnavailableError (HTTP 403) with both the constraint and the current context. The data payload includes a missingAxes array so clients can show a precise reason for the failure without parsing the message string:
ToolNotFoundError (404), helping clients understand why a tool is inaccessible. See the Structured errors reference (if present) for the full schema.
How It Works: Registry-Level Filtering
| Concern | Layer | When | Scope |
|---|---|---|---|
availableWhen | Registry (boot) | Server startup | Process-wide, immutable |
| Authorization | HTTP flow (request) | Per request | Per session/user |
| Rule-based filtering | HTTP flow (request) | Per request | Dynamic, policy-driven |
hideFromDiscovery | Registry (listing) | Per list call | Soft hide (entry still callable) |
availableWhenis a hard constraint — filtered entries cannot be listed OR called- It runs at registry initialization, not in HTTP flows — no per-request overhead
- The runtime context (OS, runtime, deployment, NODE_ENV) is detected once and cached
- Results are logged at boot time for operational visibility
Boot-Time Logging
When entries haveavailableWhen constraints, the SDK logs a summary at startup: