The plugin exposes exactly three tools through standardDocumentation Index
Fetch the complete documentation index at: https://docs.agentfront.dev/llms.txt
Use this file to discover all available pages before exploring further.
tools/list. Every per-operation OpenAPI tool is hidden behind these — execute_action is the only path to actually invoke an upstream operation, and it’s the sole authorization checkpoint.
search_skill
Search the registry by free-form query.
input
output
SkillRegistry.search() (TF-IDF in v1.2.0). bundleVersion is included when the skill was registered from a bundle — polling clients can use it to detect bundle swaps without depending on notifications/skills/list_changed.
load_skill
Load the full markdown instructions and the executable actions for one skill.
input
output
load_skill once per skill it intends to use, then keep the action schemas in context. Schemas inside actions[] are the same schemas the OpenAPI spec declared; the executor validates input against them on every execute_action.
Throws an MCP-protocol error when the skillId is unknown.
execute_action
Invoke one action of a previously-loaded skill.
input
output (success)
output (failure)
Pipeline
- Resolve
(skillId, actionId)from the plugin-privateHiddenOpRegistry. Unknown action returnsok: falsewitherror: "unknown action ...". - Pin the operation descriptor at call entry — a hot bundle-swap mid-call doesn’t change the descriptor the in-flight call uses.
- Authorize: evaluate the action’s
requiredAuthorities(if any) against the caller’sauthInfovia@frontmcp/auth’sAuthoritiesEngine. Denial returnsok: falsewitherror: "authority denied: ...". - Build the outbound HTTP request through
@frontmcp/adapters/openapi’sbuildRequest— path interpolation, header injection defenses, body serialization, security context resolution all flow through the adapter. - SSRF gate: scheme allowlist (
https:by default), hostname allowlist (built from the active bundle’sservices[].baseUrl), post-DNS IPv4/IPv6 blocklist (RFC 1918, link-local incl. cloud metadata, loopback, ULA), cloud-metadata hostname blocklist. - Fetch with timeout (
op.timeoutMs ?? defaultTimeoutMs) and response-size cap (op.maxResponseBytes ?? defaultMaxResponseBytes). - Parse the response through
parseResponseand return the structured envelope.
execute_action never throws — every failure (auth, schema, network, SSRF) returns ok: false with a structured error string. This keeps the meta-tool surface predictable for the LLM.
Prompts to give the LLM
The exact prompt depends on your agent harness, but these phrasings work well in practice (cribbed from the tool descriptions the plugin ships):- For
search_skill: “Use this tool first to discover what skills exist for the user’s request.” - For
load_skill: “Call this tool once per skill you intend to use. The instructions field is markdown — read it carefully before invoking any action.” - For
execute_action: “This is the only way to invoke an upstream operation. The pipeline auto-validates input, applies authority checks, and returns a structured envelope. Failures are returned asok: falsewith a reason — they don’t throw.”
Skills-only mode
If your FrontMCP server is configured withskills_only mode (set via ?mode=skills_only on the MCP transport URL), the meta-tools stay visible — they’re the only way to use skills, and the plugin treats them as exempt from the skills-only filter. See Skills for the skills-only contract.
What about hidden tools?
By default (exposeOperationsAsInternalTools: true), per-operation tools are registered in scope.tools with internal visibility. This means they:
- Are still callable via the SDK / direct client and can participate in DI, hooks, and observability
- Do NOT appear in the public MCP
tools/listresponse - Are NOT directly callable from MCP clients via
tools/call - Always live in the plugin’s
HiddenOpRegistryso the meta-tools (load_skill,execute_action) can find and run them with the ABAC checkpoint
exposeOperationsAsInternalTools: false, the per-operation tools live only in HiddenOpRegistry (not in scope.tools) and the only way to invoke them is execute_action. Use this stricter mode when you need to guarantee that nothing — not even other plugins or in-process tooling — can bypass the ABAC checkpoint.