@frontmcp/skills 1.8.6 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/README.md +107 -155
  2. package/catalog/create-tool/SKILL.md +24 -24
  3. package/catalog/create-tool/examples/21-tool-with-availability-constraints.md +1 -1
  4. package/catalog/create-tool/examples/22-tool-with-ui-html-template.md +0 -1
  5. package/catalog/create-tool/examples/23-tool-with-ui-filesource-tsx.md +1 -3
  6. package/catalog/create-tool/examples/24-tool-with-ui-csp-and-bridge.md +4 -6
  7. package/catalog/create-tool/references/availability.md +10 -10
  8. package/catalog/create-tool/references/decorator-options.md +1 -1
  9. package/catalog/create-tool/references/ui-widgets.md +91 -42
  10. package/catalog/frontmcp-authorities/SKILL.md +5 -0
  11. package/catalog/frontmcp-channels/SKILL.md +17 -16
  12. package/catalog/frontmcp-channels/references/channel-sources.md +4 -4
  13. package/catalog/frontmcp-channels/references/channel-two-way.md +1 -1
  14. package/catalog/frontmcp-config/examples/configure-deployment-targets/distributed-ha-config.md +2 -0
  15. package/catalog/frontmcp-config/examples/configure-session/vercel-kv-session.md +1 -2
  16. package/catalog/frontmcp-config/examples/configure-throttle/distributed-redis-throttle.md +3 -3
  17. package/catalog/frontmcp-config/examples/configure-transport/custom-protocol-flags.md +0 -1
  18. package/catalog/frontmcp-config/examples/configure-transport/distributed-sessions-redis.md +0 -1
  19. package/catalog/frontmcp-config/examples/configure-transport/stateless-serverless.md +2 -3
  20. package/catalog/frontmcp-config/examples/configure-transport-protocol-presets/stateless-api-serverless.md +2 -3
  21. package/catalog/frontmcp-config/references/configure-auth.md +1 -1
  22. package/catalog/frontmcp-config/references/configure-deployment-targets.md +68 -16
  23. package/catalog/frontmcp-config/references/configure-http.md +11 -6
  24. package/catalog/frontmcp-config/references/configure-security-headers.md +18 -13
  25. package/catalog/frontmcp-config/references/configure-throttle-guard-config.md +4 -4
  26. package/catalog/frontmcp-config/references/configure-throttle.md +4 -2
  27. package/catalog/frontmcp-config/references/configure-transport.md +4 -5
  28. package/catalog/frontmcp-deployment/SKILL.md +19 -19
  29. package/catalog/frontmcp-deployment/examples/build-for-browser/react-provider-setup.md +5 -3
  30. package/catalog/frontmcp-deployment/examples/deploy-to-node/docker-compose-with-redis.md +1 -1
  31. package/catalog/frontmcp-deployment/examples/deploy-to-node/pm2-with-nginx.md +1 -1
  32. package/catalog/frontmcp-deployment/examples/deploy-to-vercel/vercel-with-kv.md +2 -0
  33. package/catalog/frontmcp-deployment/references/build-for-browser.md +46 -9
  34. package/catalog/frontmcp-deployment/references/build-for-mcpb.md +15 -8
  35. package/catalog/frontmcp-deployment/references/build-for-sdk.md +11 -10
  36. package/catalog/frontmcp-deployment/references/deploy-to-cloudflare.md +5 -1
  37. package/catalog/frontmcp-deployment/references/deploy-to-lambda.md +11 -10
  38. package/catalog/frontmcp-deployment/references/deploy-to-node.md +11 -11
  39. package/catalog/frontmcp-deployment/references/deploy-to-vercel.md +15 -7
  40. package/catalog/frontmcp-deployment/references/mcp-client-integration.md +12 -7
  41. package/catalog/frontmcp-deployment/references/protocol-versions.md +7 -3
  42. package/catalog/frontmcp-development/examples/create-agent/nested-agents-with-swarm.md +50 -10
  43. package/catalog/frontmcp-development/examples/openapi-adapter/ref-security-and-filtering.md +13 -7
  44. package/catalog/frontmcp-development/references/create-adapter.md +14 -0
  45. package/catalog/frontmcp-development/references/create-agent.md +82 -48
  46. package/catalog/frontmcp-development/references/create-plugin-hooks.md +15 -5
  47. package/catalog/frontmcp-development/references/create-plugin.md +23 -2
  48. package/catalog/frontmcp-development/references/create-skill-with-tools.md +7 -2
  49. package/catalog/frontmcp-development/references/create-skill.md +4 -0
  50. package/catalog/frontmcp-development/references/decorators-guide.md +10 -11
  51. package/catalog/frontmcp-development/references/official-adapters.md +1 -1
  52. package/catalog/frontmcp-development/references/official-plugins.md +138 -28
  53. package/catalog/frontmcp-development/references/openapi-adapter.md +52 -2
  54. package/catalog/frontmcp-guides/references/example-task-manager.md +2 -2
  55. package/catalog/frontmcp-guides/references/example-weather-api.md +2 -2
  56. package/catalog/frontmcp-observability/references/metrics-endpoint.md +3 -1
  57. package/catalog/frontmcp-production-readiness/examples/distributed-ha/ha-kubernetes-3-replicas.md +13 -1
  58. package/catalog/frontmcp-production-readiness/examples/production-node-sdk/package-json-config.md +2 -2
  59. package/catalog/frontmcp-production-readiness/references/common-checklist.md +3 -2
  60. package/catalog/frontmcp-production-readiness/references/distributed-ha.md +54 -24
  61. package/catalog/frontmcp-production-readiness/references/health-readiness-endpoints.md +3 -1
  62. package/catalog/frontmcp-setup/examples/nx-workflow/build-test-affected.md +2 -2
  63. package/catalog/frontmcp-setup/examples/nx-workflow/multi-server-deployment.md +9 -5
  64. package/catalog/frontmcp-setup/examples/nx-workflow/scaffold-and-generate.md +1 -1
  65. package/catalog/frontmcp-setup/examples/project-structure-nx/nx-generator-scaffolding.md +2 -2
  66. package/catalog/frontmcp-setup/references/frontmcp-skills-usage.md +28 -15
  67. package/catalog/frontmcp-setup/references/multi-app-composition.md +11 -8
  68. package/catalog/frontmcp-setup/references/nx-workflow.md +68 -28
  69. package/catalog/frontmcp-setup/references/project-structure-nx.md +7 -4
  70. package/catalog/frontmcp-setup/references/setup-project.md +15 -0
  71. package/catalog/frontmcp-setup/references/setup-redis.md +12 -3
  72. package/catalog/frontmcp-setup/references/setup-sqlite.md +21 -14
  73. package/catalog/frontmcp-testing/SKILL.md +28 -23
  74. package/catalog/frontmcp-testing/references/setup-testing.md +39 -2
  75. package/catalog/frontmcp-testing/references/test-auth.md +8 -0
  76. package/catalog/skills-manifest.json +14 -12
  77. package/package.json +1 -1
@@ -77,7 +77,8 @@ class CodeReviewerAgent extends AgentContext {
77
77
  - `execute(input: In): Promise<Out>` -- the main method; default runs the agent loop
78
78
  - `completion(prompt: AgentPrompt, options?): Promise<AgentCompletion>` -- make a single LLM call
79
79
  - `streamCompletion(prompt: AgentPrompt, options?): AsyncIterable<AgentCompletionChunk>` -- stream an LLM response
80
- - `executeTool(toolDef, input): Promise<unknown>` -- (protected) invoke one of the agent's inner tools programmatically
80
+ - `executeTool(name, args): Promise<unknown>` -- (protected) run one of the tools the model is offered; every model tool call goes through it, so an override sees them all
81
+ - `invokeAgent(agentId, input): Promise<unknown>` -- (protected) call a nested agent, or a swarm agent this agent sees, and get its output
81
82
 
82
83
  **Inherited Methods:**
83
84
 
@@ -96,7 +97,7 @@ class CodeReviewerAgent extends AgentContext {
96
97
  - `this.llmAdapter` -- the configured LLM adapter instance
97
98
  - `this.toolDefinitions` -- definitions of inner tools available to the agent
98
99
  - `this.toolExecutor` -- executor for invoking inner tools
99
- - `this.metadata` -- agent metadata from the decorator
100
+ - `this.metadata` -- the options given to `@Agent`, with defaults applied (there is no `this.options`)
100
101
  - `this.scope` -- the current scope instance
101
102
  - `this.context` -- the execution context
102
103
 
@@ -308,9 +309,9 @@ class PRReviewerAgent extends AgentContext {
308
309
  }
309
310
  ```
310
311
 
311
- ## Exported Tools
312
+ ## Exports
312
313
 
313
- Use `exports: { tools: [] }` to expose specific tools that the agent makes available to external callers. Unlike inner tools (which the agent uses privately), exported tools appear in the MCP tool listing for clients to invoke directly.
314
+ `exports` shares some of the agent's own resources, prompts and providers with the scope it is registered in (the server, or its parent agent). There is no `exports.tools`: an agent's tools are private to its model, and `exports: { tools }` is refused when the agent is decorated. Register a tool clients should call in the app's `tools`.
314
315
 
315
316
  ```typescript
316
317
  @Agent({
@@ -321,15 +322,24 @@ Use `exports: { tools: [] }` to expose specific tools that the agent makes avail
321
322
  model: 'gpt-4o',
322
323
  apiKey: { env: 'OPENAI_API_KEY' },
323
324
  },
324
- tools: [ExtractTool, TransformTool, LoadTool], // Agent uses these internally
325
- exports: { tools: [ValidateDataTool, StatusTool] }, // These are exposed to MCP clients
325
+ tools: [ExtractTool, TransformTool, LoadTool], // The agent's model uses these
326
+ providers: [WarehouseClient],
327
+ resources: [PipelineStatusResource],
328
+ prompts: [PipelineReportPrompt],
329
+ exports: {
330
+ resources: '*', // listed in resources/list, read with resources/read
331
+ prompts: [PipelineReportPrompt], // listed in prompts/list, got with prompts/get
332
+ providers: [WarehouseClient], // the app's tools can use this.get(WarehouseClient)
333
+ },
326
334
  })
327
335
  class DataPipelineAgent extends AgentContext {}
328
336
  ```
329
337
 
338
+ Each export must be one of the agent's own `resources`, `prompts` or `providers`; otherwise the server refuses to start with `AgentConfigurationError`.
339
+
330
340
  ## Nested Agents (Sub-Agents)
331
341
 
332
- Use the `agents` array to compose agents from smaller, specialized sub-agents. Each sub-agent has its own LLM config, inner tools, and system instructions.
342
+ Use the `agents` array to compose agents from smaller, specialized sub-agents. Each sub-agent has its own LLM config, inner tools, and system instructions. The parent's model is offered each one as an `invoke_<id>` tool next to its own tools, and the parent's code can call them with `this.invokeAgent('<id>', input)`. Nested agents are private: clients are not offered them. A call to one runs through its `invoke_<id>` tool's `tools:call-tool` flow in the parent's scope, also when the parent sets `execution.useToolFlow: false`, so the nested agent's `authorities`, `rateLimit`, `concurrency`, `timeout`, plugin gates and hooks apply as for a client's call. A `rateLimit` or `concurrency` declared only on a nested agent is enforced without a `throttle` option. Under `auth.consent`, the consent given to the parent agent covers its own tools and nested agents, which the consent screen never offers.
333
343
 
334
344
  ```typescript
335
345
  @Agent({
@@ -368,7 +378,7 @@ class CodeAuditorAgent extends AgentContext {}
368
378
 
369
379
  ## Swarm Configuration
370
380
 
371
- Swarm mode lets agents discover and call each other at runtime. The framework registers visible peers as callable tools (`use-agent:<id>`) on the orchestrator's LLM, so the LLM itself decides when to delegate -- there is no declarative routing table.
381
+ Swarm mode lets agents discover and call each other at runtime. The framework offers the orchestrator's LLM each peer it sees as that peer's `invoke_<id>` tool, so the LLM itself decides when to delegate -- there is no declarative routing table. The orchestrator's code can call a peer it sees with `this.invokeAgent('<id>', input)`.
372
382
 
373
383
  ### SwarmConfig Fields
374
384
 
@@ -376,10 +386,10 @@ Swarm mode lets agents discover and call each other at runtime. The framework re
376
386
  | ------------------- | ---------- | ------- | ------------------------------------------------------------------------------------ |
377
387
  | `canSeeOtherAgents` | `boolean` | `false` | If `true`, this agent can discover and call other agents in the same scope |
378
388
  | `visibleAgents` | `string[]` | -- | Whitelist of agent IDs this agent is allowed to see (when `canSeeOtherAgents: true`) |
379
- | `isVisible` | `boolean` | `true` | If `false`, this agent is hidden from peers (cannot be called as `use-agent:<id>`) |
389
+ | `isVisible` | `boolean` | `true` | If `false`, this agent is hidden from peers (never offered or callable by them) |
380
390
  | `maxCallDepth` | `number` | `3` | Maximum nested agent-to-agent call depth (1-10) |
381
391
 
382
- There is no `role`, `handoff`, or `condition` field -- routing is driven by the orchestrator's LLM choosing among the visible `use-agent:*` tools.
392
+ There is no `role`, `handoff`, or `condition` field -- routing is driven by the orchestrator's LLM choosing among the visible `invoke_*` tools.
383
393
 
384
394
  ```typescript
385
395
  @Agent({
@@ -397,7 +407,7 @@ There is no `role`, `handoff`, or `condition` field -- routing is driven by the
397
407
  maxCallDepth: 3,
398
408
  },
399
409
  systemInstructions:
400
- 'Analyze the request and call use-agent:billing_agent or use-agent:technical_agent depending on the topic.',
410
+ 'Analyze the request and call invoke_billing_agent or invoke_technical_agent depending on the topic.',
401
411
  })
402
412
  class TriageAgent extends AgentContext {}
403
413
 
@@ -406,6 +416,8 @@ class TriageAgent extends AgentContext {}
406
416
  name: 'billing_agent',
407
417
  description: 'Handles billing and payment inquiries',
408
418
  llm: { provider: 'anthropic', model: 'claude-sonnet-4-20250514', apiKey: { env: 'ANTHROPIC_API_KEY' } },
419
+ // The arguments of its invoke_billing_agent tool: an agent without an inputSchema receives {}.
420
+ inputSchema: { request: z.string().describe('The billing request') },
409
421
  tools: [LookupInvoiceTool, ProcessRefundTool],
410
422
  // Specialist is visible to peers but does not see others.
411
423
  swarm: { isVisible: true },
@@ -415,39 +427,42 @@ class BillingAgent extends AgentContext {}
415
427
 
416
428
  ### How Routing Works
417
429
 
418
- - When the orchestrator agent runs its LLM loop, every visible peer is exposed as a tool named `use-agent:<id>`.
430
+ - When the orchestrator agent runs its LLM loop, every peer it sees (in `visibleAgents` when set, never one with `isVisible: false`, never itself) is offered as a tool named `invoke_<id>`.
419
431
  - The orchestrator's `systemInstructions` should describe when to call each peer; the LLM decides at runtime.
420
432
  - Peers do not need any swarm config to be callable -- they only need `isVisible: true` (the default).
421
433
  - Set `canSeeOtherAgents: false` (the default) on agents that should never be able to delegate.
434
+ - A peer call runs through the peer's `invoke_<id>` tool, so its `authorities`, `rateLimit`, `concurrency`, `timeout` and hooks apply.
435
+ - `this.invokeAgent('<id>', input)` on an agent that exists but isn't seen throws `AgentVisibilityError` (`AGENT_VISIBILITY_DENIED`); an unknown id throws `AgentNotFoundError`.
436
+
437
+ ### Call Depth
438
+
439
+ `maxCallDepth` counts agent-to-agent calls in one chain, however they are made (the model, `invokeAgent()`, or `this.callTool('invoke_<id>')`): an agent called by a client that calls another makes call 1. The smallest `maxCallDepth` of the agents running applies, and agents without `swarm` count with the default of 3. A deeper call fails with `AgentCallDepthExceededError` (`AGENT_CALL_DEPTH_EXCEEDED`), which stops agents that call each other from looping forever.
422
440
 
423
441
  ## Function-Style Builder
424
442
 
425
- For agents that do not need a class, use the `agent()` function builder.
443
+ For agents that do not need a class, use the `agent()` function builder. The handler is the agent's `execute()`: it receives the validated input and the agent's context, and returns the agent's output. It doesn't run the LLM loop; use a class extending `AgentContext` when the model should drive.
426
444
 
427
445
  ```typescript
428
446
  import { agent, z } from '@frontmcp/sdk';
429
447
 
430
- const QuickSummarizer = agent({
431
- name: 'quick_summarizer',
432
- description: 'Summarizes text quickly',
448
+ const QuickTruncator = agent({
449
+ name: 'quick_truncator',
450
+ description: 'Truncates text to a length',
433
451
  llm: {
434
452
  provider: 'anthropic',
435
453
  model: 'claude-sonnet-4-20250514',
436
454
  apiKey: { env: 'ANTHROPIC_API_KEY' },
437
455
  },
438
456
  inputSchema: {
439
- text: z.string().describe('Text to summarize'),
440
- maxLength: z.number().default(100).describe('Max summary length'),
457
+ text: z.string().describe('Text to truncate'),
458
+ maxLength: z.number().default(100).describe('Max length'),
441
459
  },
442
- })((input, ctx) => {
443
- // Custom logic using ctx for completion calls
444
- return ctx.completion({
445
- messages: [{ role: 'user', content: `Summarize in ${input.maxLength} chars:\n${input.text}` }],
446
- });
447
- });
460
+ })((input) => ({ text: input.text.slice(0, input.maxLength) }));
448
461
  ```
449
462
 
450
- Register it the same way as a class agent: `agents: [QuickSummarizer]`.
463
+ Up to 1.8.7 the handler never ran: every call failed output validation with a list of Zod issues.
464
+
465
+ Register it the same way as a class agent: `agents: [QuickTruncator]`.
451
466
 
452
467
  ## Remote and ESM Loading
453
468
 
@@ -488,7 +503,7 @@ class ReviewApp {}
488
503
  @FrontMcp({
489
504
  info: { name: 'my-server', version: '1.0.0' },
490
505
  apps: [ReviewApp],
491
- agents: [QuickSummarizer], // can also register agents directly on the server
506
+ agents: [QuickTruncator], // can also register agents directly on the server
492
507
  })
493
508
  class MyServer {}
494
509
  ```
@@ -534,7 +549,7 @@ class ExpensiveAgent extends AgentContext {
534
549
 
535
550
  ## Agent with Providers and Plugins
536
551
 
537
- Agents can include their own providers and plugins for self-contained dependency management:
552
+ Agents can include their own providers and plugins for self-contained dependency management. The agent's tools can inject its providers without the app registering them too, and its plugins' hooks run for its tools. Set `execution: { inheritPlugins: true }` to also run the plugins installed on the app and the server for the agent's tools (a plugin installed in both places runs once).
538
553
 
539
554
  ```typescript
540
555
  @Agent({
@@ -559,7 +574,7 @@ class DatabaseAgent extends AgentContext {}
559
574
 
560
575
  ## Agent with Resources and Prompts
561
576
 
562
- Agents can include resources and prompts that are available within the agent's scope:
577
+ Agents can include resources and prompts scoped to the agent. The agent's model is sent tools only, so they reach clients only when exported (`exports: { resources, prompts }`, see [Exports](#exports)); the server reports any that are not exported at startup.
563
578
 
564
579
  ```typescript
565
580
  @Agent({
@@ -576,19 +591,34 @@ Agents can include resources and prompts that are available within the agent's s
576
591
  tools: [WriteFileTool, ReadFileTool],
577
592
  resources: [DocsTemplateResource],
578
593
  prompts: [TechnicalWritingPrompt],
594
+ exports: { resources: '*', prompts: '*' },
579
595
  })
580
596
  class DocsAgent extends AgentContext {}
581
597
  ```
582
598
 
599
+ ## Execution Options
600
+
601
+ | Option | Default | Effect |
602
+ | ------------------------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
603
+ | `execution.maxIterations` | `10` | Max tool-call rounds of the LLM loop |
604
+ | `execution.timeout` | `120000` | Max run time in ms |
605
+ | `execution.inheritParentTools` | `false` | Also offer the model the tools of the scope the agent is registered in, other than agents; they run through that scope's `tools:call-tool` flow |
606
+ | `execution.inheritPlugins` | `false` | Also run the app's and server's plugin hooks for the agent's own tools |
607
+ | `execution.useToolFlow` | `true` | Own tools through its `tools:call-tool` flow (hooks, limits, authorization); `false` runs them directly. Nested agents always use their flow |
608
+ | `execution.enableAutoProgress` | `false` | Send progress notifications during the loop |
609
+ | `execution.enableStreaming` | `false` | Not supported yet: the agent replies once the run completes, and `true` is reported at startup |
610
+
611
+ Through that flow the agent's own tools get the `rateLimit`, `concurrency` and `timeout` they declare (else the `throttle` defaults), as the app's tools do; a `rateLimit` or `concurrency` there is enforced without a `throttle` option. The calls an agent makes during its run (its model's tool calls, its nested and swarm agents) run inside the `throttle.globalConcurrency` slot of the call that runs the agent.
612
+
583
613
  ## Common Patterns
584
614
 
585
- | Pattern | Correct | Incorrect | Why |
586
- | ----------------------- | ----------------------------------------------------------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------- |
587
- | LLM config | `llm: { provider: 'anthropic', model: '...', apiKey: { env: 'KEY' } }` | `llm: { provider: 'anthropic', apiKey: 'sk-hardcoded' }` | Environment variable references prevent leaking secrets in code |
588
- | Inner tools vs exported | `tools: [...]` for agent-private; `exports: { tools: [...] }` for MCP-visible | Putting all tools in `tools` and expecting clients to see them | Inner tools are private to the agent; only exported tools appear in MCP listing |
589
- | Custom execute | Override `execute()` for multi-pass orchestration | Putting all logic in system instructions | Custom `execute()` gives structured control over completion calls and stages |
590
- | Sub-agents | Use `agents: [SubAgent]` for composition | Calling another agent's `execute()` directly | The `agents` array enables proper lifecycle and scope isolation |
591
- | Swarm visibility | `swarm: { canSeeOtherAgents: true, visibleAgents: ['peer'] }` | `swarm: { role, handoff }` (those fields do not exist) | Routing is LLM-driven via `use-agent:*` tools; only visibility is configurable |
615
+ | Pattern | Correct | Incorrect | Why |
616
+ | ------------------------ | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
617
+ | LLM config | `llm: { provider: 'anthropic', model: '...', apiKey: { env: 'KEY' } }` | `llm: { provider: 'anthropic', apiKey: 'sk-hardcoded' }` | Environment variable references prevent leaking secrets in code |
618
+ | Inner tools vs app tools | `tools: [...]` on the agent for its model; `tools: [...]` on the `@App` for clients | `exports: { tools: [...] }` (refused: there is no such export) | An agent's tools are private to its model; clients see the app's tools |
619
+ | Custom execute | Override `execute()` for multi-pass orchestration | Putting all logic in system instructions | Custom `execute()` gives structured control over completion calls and stages |
620
+ | Sub-agents | Use `agents: [SubAgent]` and `this.invokeAgent('sub_agent', input)` | Calling another agent's `execute()` directly, or `tools: [SubAgent]` | The `agents` array enables proper lifecycle, gates and scope isolation |
621
+ | Swarm visibility | `swarm: { canSeeOtherAgents: true, visibleAgents: ['peer'] }` | `swarm: { role, handoff }` (those fields do not exist) | Routing is LLM-driven via `invoke_*` tools; only visibility is configurable |
592
622
 
593
623
  ## Verification Checklist
594
624
 
@@ -606,25 +636,29 @@ class DocsAgent extends AgentContext {}
606
636
  - [ ] LLM adapter connects successfully to the configured provider
607
637
  - [ ] Inner tools are invoked correctly during the agent loop
608
638
  - [ ] `this.completion()` and `this.streamCompletion()` return valid responses
609
- - [ ] Visible peers appear as `use-agent:<id>` tools to the orchestrator agent
639
+ - [ ] Visible peers and nested agents are offered as `invoke_<id>` tools to the orchestrator's model
610
640
 
611
641
  ## Troubleshooting
612
642
 
613
- | Problem | Cause | Solution |
614
- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
615
- | Agent not appearing in tool listing | Not registered in `agents` array | Add agent class to `@App` or `@FrontMcp` `agents` array |
616
- | LLM authentication error | API key not set or incorrect env variable | Verify the environment variable name in `apiKey: { env: '...' }` is set |
617
- | Inner tools not being called | Tools not listed in `tools` array of `@Agent` | Add tool classes to the `tools` field in the `@Agent` decorator |
618
- | Agent times out | No timeout or rate limit configured | Add `timeout: { executeMs: 120_000 }` and `rateLimit` to `@Agent` options |
619
- | Peer agent not callable | Peer has `isVisible: false`, or orchestrator lacks `canSeeOtherAgents: true`, or peer is not in `visibleAgents` whitelist | Set `swarm.isVisible: true` on the peer and `swarm.canSeeOtherAgents: true` (and add the peer to `visibleAgents`) on the orchestrator |
643
+ | Problem | Cause | Solution |
644
+ | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
645
+ | Agent not appearing in tool listing | Not registered in `agents` array | Add agent class to `@App` or `@FrontMcp` `agents` array |
646
+ | LLM authentication error | API key not set or incorrect env variable | Verify the environment variable name in `apiKey: { env: '...' }` is set |
647
+ | Inner tools not being called | Tools not listed in `tools` array of `@Agent` | Add tool classes to the `tools` field in the `@Agent` decorator |
648
+ | Agent times out | No timeout or rate limit configured | Add `timeout: { executeMs: 120_000 }` and `rateLimit` to `@Agent` options |
649
+ | Peer agent not callable | Peer has `isVisible: false`, or orchestrator lacks `canSeeOtherAgents: true`, or peer is not in `visibleAgents` whitelist | Set `swarm.isVisible: true` on the peer and `swarm.canSeeOtherAgents: true` (and add the peer to `visibleAgents`) on the orchestrator |
650
+ | `AGENT_CALL_DEPTH_EXCEEDED` | Agents call each other deeper than `swarm.maxCallDepth` (default 3) allows | Stop the loop in `systemInstructions`, or raise `maxCallDepth` (max 10) on every agent in the chain |
651
+ | `AGENT_VISIBILITY_DENIED` | `this.invokeAgent()` names an agent this agent doesn't see | Add it to `swarm.visibleAgents` (with `canSeeOtherAgents: true`), or nest it in `agents` |
652
+ | Startup warning `declares resources [...] that nothing reads` | The agent's resources or prompts aren't exported; its model is sent tools only | Export them (`exports: { resources, prompts }`) or remove them |
653
+ | Agent call fails with `INVALID_OUTPUT` | The model's reply does not match the agent's `outputSchema` (a value outside an enum, or text that is not JSON) | Tighten the prompt or loosen the schema; the error message names the field, for example `output does not match outputSchema at priority`. The result never carries a stack trace |
620
654
 
621
655
  ## Examples
622
656
 
623
- | Example | Level | Description |
624
- | ---------------------------------------------------------------------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
625
- | [`basic-agent-with-tools`](../examples/create-agent/basic-agent-with-tools.md) | Basic | An autonomous agent that uses inner tools to review GitHub pull requests. |
626
- | [`custom-multi-pass-agent`](../examples/create-agent/custom-multi-pass-agent.md) | Intermediate | An agent that overrides `execute()` to perform multi-pass LLM reasoning with `this.completion()`. |
627
- | [`nested-agents-with-swarm`](../examples/create-agent/nested-agents-with-swarm.md) | Advanced | Composing specialized agents into a swarm where an orchestrator can discover and call peers at runtime as `use-agent:<id>` tools. Routing is driven by the orchestrator's LLM, not a declarative handoff table. |
657
+ | Example | Level | Description |
658
+ | ---------------------------------------------------------------------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
659
+ | [`basic-agent-with-tools`](../examples/create-agent/basic-agent-with-tools.md) | Basic | An autonomous agent that uses inner tools to review GitHub pull requests. |
660
+ | [`custom-multi-pass-agent`](../examples/create-agent/custom-multi-pass-agent.md) | Intermediate | An agent that overrides `execute()` to perform multi-pass LLM reasoning with `this.completion()`. |
661
+ | [`nested-agents-with-swarm`](../examples/create-agent/nested-agents-with-swarm.md) | Advanced | Composing specialized agents into a swarm where an orchestrator can discover and call peers at runtime as `invoke_<id>` tools, plus a nested sub-agent and `this.invokeAgent()`. Routing is driven by the orchestrator's LLM, not a declarative handoff table. |
628
662
 
629
663
  > See all examples in [`examples/create-agent/`](../examples/create-agent/)
630
664
 
@@ -62,6 +62,9 @@ These are the flow names with pre-built hook decorator exports in `@frontmcp/sdk
62
62
  | `resources:read-resource` | Resource reading | `ResourceHook` |
63
63
  | `resources:list-resources` | Resource listing | `ListResourcesHook` |
64
64
  | `resources:list-resource-templates` | Resource template listing | `ListResourceTemplatesHook` |
65
+ | `prompts:get-prompt` | Prompt retrieval | `PromptHook` |
66
+ | `prompts:list-prompts` | Prompt listing | `ListPromptsHook` |
67
+ | `completion:complete` | Argument completion | `CompletionHook` |
65
68
  | `agents:call-agent` | Agent invocation | `AgentCallHook` |
66
69
  | `channels:send-notification` | Channel notification send | `ChannelSendHook` |
67
70
  | `channels:list` | Channel listing | `ChannelListHook` |
@@ -152,10 +155,13 @@ import {
152
155
  AgentCallHook, // FlowHooksOf('agents:call-agent')
153
156
  ChannelListHook, // FlowHooksOf('channels:list')
154
157
  ChannelSendHook, // FlowHooksOf('channels:send-notification')
158
+ CompletionHook, // FlowHooksOf('completion:complete')
155
159
  HttpHook, // FlowHooksOf('http:request')
160
+ ListPromptsHook, // FlowHooksOf('prompts:list-prompts')
156
161
  ListResourcesHook, // FlowHooksOf('resources:list-resources')
157
162
  ListResourceTemplatesHook, // FlowHooksOf('resources:list-resource-templates')
158
163
  ListToolsHook, // FlowHooksOf('tools:list-tools')
164
+ PromptHook, // FlowHooksOf('prompts:get-prompt')
159
165
  ResourceHook, // FlowHooksOf('resources:read-resource')
160
166
  ToolHook, // FlowHooksOf('tools:call-tool')
161
167
  } from '@frontmcp/sdk';
@@ -167,7 +173,7 @@ Usage:
167
173
  const { Will, Did, Around, Stage } = ToolHook;
168
174
  ```
169
175
 
170
- > **Note:** Other internal flows (e.g., `prompts:get-prompt`, `prompts:list-prompts`, `skills:search`, `completion:complete`, transport flows) exist at runtime and can be hooked by passing the flow name to `FlowHooksOf<'flow:name'>('flow:name')`, but they do not currently ship with a pre-built typed export. Prefer the pre-built exports above when one is available.
176
+ > **Note:** Other flows (e.g., `skills:filter`, transport flows) can be hooked by passing the flow name to `FlowHooksOf('flow:name')`. Prefer the pre-built exports above when one is available: exporting them is also what puts a flow's types in the published package, which is why `FlowHooksOf('prompts:get-prompt')`, `'prompts:list-prompts'` and `'completion:complete'` did not typecheck in consumer projects up to 1.8.7.
171
177
 
172
178
  ## call-tool Flow Stages
173
179
 
@@ -310,7 +316,9 @@ export class MyApp {}
310
316
 
311
317
  Plugins are initialized in array order. Hook priority determines execution order within the same stage.
312
318
 
313
- Hooks declared on an app's providers, on its plugins (including plugins nested inside them), and on those plugins' providers run only for that app's tools, resources and prompts (`tools:call-tool`, `resources:read-resource`, `prompts:get-prompt`, `completion:complete`), including the ones its adapters and plugins provide, such as the tools an OpenAPI adapter generates. Plugins registered on the server (`@FrontMcp({ plugins })`) apply to every app. Resources and prompts the server serves outside every app, such as the SEP-2640 `skill://` resources, run every app's hooks.
319
+ Hooks declared on an app's providers, on its plugins (including plugins nested inside them), and on those plugins' providers run only for that app's tools, resources and prompts (`tools:call-tool`, `resources:read-resource`, `prompts:get-prompt`, `completion:complete`), including the ones its adapters and plugins provide, such as the tools an OpenAPI adapter generates. Plugins and providers registered on the server (`@FrontMcp({ plugins, providers })`) apply to every app. Resources and prompts the server serves outside every app, such as the SEP-2640 `skill://` resources, run every app's hooks.
320
+
321
+ A hook declared on a `CONTEXT`-scoped provider (`@Provider({ scope: ProviderScope.CONTEXT })`, a class provider) runs on the instance built for the request or session -- the same instance the request's tools get from `this.get()`. Up to 1.8.7, hooks on server-level and `CONTEXT`-scoped providers were never registered.
314
322
 
315
323
  ## Using Hooks Inside a @Tool Class
316
324
 
@@ -377,11 +385,12 @@ class ProcessOrderTool extends ToolContext {
377
385
  ### Available Stages for Tool Hooks
378
386
 
379
387
  ```
380
- parseInput → findTool → checkToolAuthorization → createToolCallContext
381
- → validateInput → execute → validateOutput → finalize
388
+ parseInput → ensureRemoteCapabilities → findTool → checkToolAuthorization → checkEntryAuthorities
389
+ → createTaskIfRequested → createToolCallContext → checkToolCredentials → acquireQuota → acquireSemaphore
390
+ → validateInput → execute → validateOutput → releaseSemaphore → releaseQuota → applyUI → finalize
382
391
  ```
383
392
 
384
- Any stage can have `@Will`, `@Did`, `@Stage`, or `@Around` hooks.
393
+ A tool-class hook runs on the tool instance, which `createToolCallContext` builds. So it can hook `Did`/`Stage` on `createToolCallContext` and any hook on a later stage. A hook on an earlier stage, `Will`/`Around` on `createToolCallContext`, or a list-flow hook (`ListToolsHook`; listing builds no instance) fails startup with `InvalidHookFlowError` -- put those on a plugin or a provider. The same holds for `@Resource` (`createResourceContext`), `@Prompt` (`createPromptContext`) and `@Agent` (`createAgentContext`) classes. `@Job` classes cannot declare hooks at all (jobs do not run through a hookable flow); hook `tools:call-tool` for the `execute_job` tool instead. Up to 1.8.7 these hooks were accepted and silently never ran.
385
394
 
386
395
  ## Common Patterns
387
396
 
@@ -415,6 +424,7 @@ Any stage can have `@Will`, `@Did`, `@Stage`, or `@Around` hooks.
415
424
  | Problem | Cause | Solution |
416
425
  | --------------------------------------------- | ------------------------------------------------ | --------------------------------------------------------------------------------- |
417
426
  | Hook never fires | Plugin not registered in `plugins` array | Add plugin class to `@App` or `@FrontMcp` `plugins` array |
427
+ | `InvalidHookFlowError` at startup | Entry-class hook that could never run | Move early-stage, list-flow and `@Job` hooks to a plugin or a provider |
418
428
  | Hook fires for wrong flow | Used wrong flow name in `FlowHooksOf` | Verify flow name matches (e.g., `'tools:call-tool'` not `'tool:call'`) |
419
429
  | `@Around` skips the stage entirely | `next()` not called inside the around handler | Always `await next()` to execute the wrapped stage |
420
430
  | Multiple hooks execute in wrong order | Priorities not set or conflicting | Set explicit `priority` values; lower numbers execute first |
@@ -5,7 +5,7 @@ description: Build plugins with providers, context extensions, lifecycle hooks,
5
5
 
6
6
  # Create a FrontMCP Plugin
7
7
 
8
- This skill covers building custom plugins for FrontMCP and using all 6 official plugins. Plugins are modular units that extend server behavior through providers, context extensions, lifecycle hooks, and contributed tools/resources/prompts.
8
+ This skill covers building custom plugins for FrontMCP and using all 7 official plugins. Plugins are modular units that extend server behavior through providers, context extensions, lifecycle hooks, and contributed tools/resources/prompts.
9
9
 
10
10
  ## When to Use This Skill
11
11
 
@@ -68,6 +68,7 @@ For plugins that accept runtime configuration, extend `DynamicPlugin<TOptions, T
68
68
  ```typescript
69
69
  abstract class DynamicPlugin<TOptions extends object, TInput extends object = TOptions> {
70
70
  static dynamicProviders?(options: any): readonly ProviderType[];
71
+ static dynamicTools?(options: any): readonly ToolType[];
71
72
  static init<TThis>(options: InitOptions<TInput>): PluginReturn<TOptions>;
72
73
  get<T>(token: Reference<T>): T;
73
74
  }
@@ -77,6 +78,7 @@ abstract class DynamicPlugin<TOptions extends object, TInput extends object = TO
77
78
  - `TInput` -- the input type users provide to `init()` (may have optional fields)
78
79
  - `init()` creates a provider entry for use in `plugins: [...]` arrays
79
80
  - `dynamicProviders()` returns providers computed from the input options
81
+ - `dynamicTools()` returns tools computed from the input options
80
82
 
81
83
  ## Quick Start: Minimal DynamicPlugin
82
84
 
@@ -328,6 +330,23 @@ export default class MyPlugin extends DynamicPlugin<MyPluginOptions, MyPluginOpt
328
330
 
329
331
  The reverse does not work: an option-derived provider cannot inject a provider that a nested plugin exports.
330
332
 
333
+ ### Options named like plugin metadata, and option-derived tools
334
+
335
+ `init(options)` spreads the options into the plugin's metadata, so an option named like a list-valued metadata key (`tools`, `resources`, `prompts`, `skills`, `adapters`, `plugins`, `exports`, `contextExtensions`, `enforcesMetadata`) used to be read as that list: `RememberPlugin.init({ tools: { enabled: true } })` crashed at startup. A non-array value under one of those keys is now an option and stays out of the metadata; an array still contributes.
336
+
337
+ `providers` follows the same rule: an array adds providers to the plugin; any other value (`MyPlugin.init({ providers: { region: 'eu' } })`) is the plugin's own option and reaches the instance instead of throwing `(extraProviders ?? []) is not iterable` at module load. When the options type declares `providers`, `init()` types the key as that option.
338
+
339
+ To register tools only when an option asks for it, declare `static dynamicTools(options)`, the counterpart of `dynamicProviders`. Its tools are added to those from `@Plugin({ tools })` and from an array `tools` option:
340
+
341
+ ```typescript
342
+ export default class MemoryPlugin extends DynamicPlugin<MemoryOptions, MemoryOptionsInput> {
343
+ static override dynamicTools = (options: MemoryOptionsInput): readonly ToolType[] =>
344
+ options.tools?.enabled ? [RememberTool, RecallTool] : [];
345
+ }
346
+ ```
347
+
348
+ `dynamicTools`, like `dynamicProviders`, runs on the options the plugin is built with: those given to `init(options)`, or the ones an `init({ inject, useFactory })` factory returns at startup. `RememberPlugin.init({ inject, useFactory: () => ({ type: 'memory', tools: { enabled: true } }) })` therefore registers the memory tools.
349
+
331
350
  ### Installing the same plugin in several apps
332
351
 
333
352
  Each app that installs a plugin gets its own copy of the plugin's providers, including CONTEXT-scoped ones. Tools, resources and prompts resolve the nearest definition in their own hierarchy (plugin, then app, then server). So `this.myService` in app A uses A's options even when app B installs `MyPlugin.init()` with different options:
@@ -340,6 +359,8 @@ class BillingApp {}
340
359
  class OpsApp {}
341
360
  ```
342
361
 
362
+ An app that does not install the plugin does not get its providers: there, `this.get(Token)` throws and `this.tryGet(Token)` returns `undefined`. Install the plugin on the server (`@FrontMcp({ plugins })`) to share it with every app. Up to 1.8.7 the providers a plugin derives from its options (`dynamicProviders(options)`, `init({ providers })`) leaked to every other app on the server.
363
+
343
364
  ## Step 5: Extend Metadata and Execution Context
344
365
 
345
366
  FrontMCP provides two extension mechanisms for plugins: **metadata augmentation** (add fields to decorators) and **context extensions** (add properties to `this` in tools/resources/prompts).
@@ -458,7 +479,7 @@ export { MyServiceToken } from './my-plugin.symbols';
458
479
 
459
480
  ## Official Plugins
460
481
 
461
- For official plugin installation, configuration, and examples, see the **official-plugins** skill. FrontMCP provides 6 official plugins: CodeCall, Remember, Approval, Cache, Feature Flags, and Dashboard. Install individually or via `@frontmcp/plugins` (meta-package).
482
+ For official plugin installation, configuration, and examples, see the **official-plugins** skill. FrontMCP provides 7 official plugins: CodeCall, Remember, Approval, Cache, Feature Flags, Dashboard, and WebMCP. Install individually or via `@frontmcp/plugins` (meta-package).
462
483
 
463
484
  ## Recommended Folder Structure
464
485
 
@@ -82,7 +82,9 @@ The `tools` array in `@Skill` metadata supports three ways to reference tools th
82
82
 
83
83
  ### 1. Class Reference
84
84
 
85
- Pass the tool class directly. The framework resolves the tool name and validates it exists in the registry.
85
+ Pass the tool class directly (or `{ tool: ToolClass, purpose?, required? }`). The framework resolves the tool name (the tool's `id` when it declares one, else its `name`) and validates it exists in the registry. The class is only a reference: register the tool in the `tools` of the `@App` or `@FrontMcp` too. A class that isn't a `@Tool` stops the server from starting with `Invalid tool class '<ClassName>'`.
86
+
87
+ Up to 1.8.7 a class reference itself stopped the server from starting (the class lost its `@Tool` name while the skill's options were validated); on those versions, reference the tool by name.
86
88
 
87
89
  ```typescript
88
90
  @Skill({
@@ -170,6 +172,8 @@ class StrictWorkflowSkill extends SkillContext {}
170
172
  | `'warn'` | Logs a warning for missing tools but continues. Use during development when tools may not all be available yet. |
171
173
  | `'ignore'` | Silently ignores missing tools. Use for optional tool references or cross-server skills. |
172
174
 
175
+ In `'strict'` mode the server refuses to start, with `SkillValidationError: Skill '<name>' failed tool validation: missing tools [...]`. Up to 1.8.7 it started anyway, silently.
176
+
173
177
  When a caller loads the skill (`skills/load`, the `skills:load` flow, `GET /skills/{id}`, `/llm_full.txt`), a referenced tool that `availableWhen.surface` doesn't offer that caller (an agent-only tool, for an MCP client) is reported as missing, without its input schema, just as `tools/list` leaves it out. The same skill loaded by an agent lists it as available.
174
178
 
175
179
  ## Instruction Sources
@@ -652,7 +656,7 @@ class AuditServer {}
652
656
 
653
657
  ## CodeCall Compatibility
654
658
 
655
- When the `CodeCallPlugin` is active in `codecall_only` mode, all tools registered on the server are hidden from `list_tools`. The AI client only sees the three CodeCall meta-tools (`codecall:search`, `codecall:describe`, `codecall:execute`). This means skill instructions that reference tool names directly (e.g., "Use the `build_project` tool") become misleading -- the AI cannot call those tools because they do not appear in the tool listing.
659
+ When the `CodeCallPlugin` is active in `codecall_only` mode, all tools registered on the server are hidden from `list_tools`. The AI client only sees the three CodeCall meta-tools (`codecall:search`, `codecall:describe`, `codecall:execute`). This means skill instructions that reference tool names directly (e.g., "Use the `build_project` tool") become misleading -- the AI cannot call those tools: they do not appear in the tool listing, and a direct `tools/call` of a hidden tool is refused as for an unknown tool.
656
660
 
657
661
  ### When This Matters
658
662
 
@@ -755,6 +759,7 @@ class DeployServiceSkill extends SkillContext {}
755
759
  | -------------------------------------------- | ----------------------------------------------------------- | -------------------------------------------------------------------------------------- |
756
760
  | Skill not appearing in `/llm.txt` | `visibility` is set to `'mcp'` | Change to `'both'` or `'http'` to include HTTP discovery |
757
761
  | `toolValidation: 'strict'` throws at startup | A referenced tool is not registered in the scope | Register all referenced tools in the `tools` array of `@App` or `@FrontMcp` |
762
+ | `Invalid tool class` at startup | A class in `tools` isn't decorated with `@Tool` | Reference a `@Tool` class, or the tool's name |
758
763
  | `skillDir()` fails to load | `SKILL.md` file missing or frontmatter is invalid YAML | Ensure the directory contains a `SKILL.md` with valid `---` delimited YAML frontmatter |
759
764
  | Instructions are empty at runtime | `{ file: './path.md' }` path is relative to wrong directory | Use a path relative to the skill file's location, not the project root |
760
765
  | Parameters not visible to AI client | `parameters` defined as a plain object instead of an array | Use array format: `[{ name, description, type, required }]` |
@@ -53,6 +53,8 @@ Create a class extending `SkillContext` and decorate it with `@Skill`. The decor
53
53
  | `allowedTools` | `string` | No | Space-delimited pre-approved tool names (Agent Skills spec) |
54
54
  | `resources` | `SkillResources` | No | Bundled dirs: `{ scripts?, references?, assets? }` |
55
55
 
56
+ With `toolValidation: 'strict'`, the server refuses to start when a referenced tool isn't registered (`SkillValidationError`).
57
+
56
58
  ### Basic Example
57
59
 
58
60
  ```typescript
@@ -158,6 +160,8 @@ class ApiStandardsSkill extends SkillContext {}
158
160
 
159
161
  > **When file and URL instructions are read:** when the server starts, for every skill — the server indexes each skill for `skills/search` and checks its tools then, so a URL is fetched at every start whether or not a client reads the skill. A read that fails is logged (`Failed to load skill <name>: …`) and tried again the first time the skill is loaded; the content is kept once a read succeeds.
160
162
 
163
+ > **Search index:** `skills/search` ranks with TF-IDF from the optional peer `vectoriadb`, loaded the first time a search runs — not at server start. Listing and loading skills (`skills/list`, `skills/load`, `skill://` resources) never need it, so a server whose skills are never searched runs without it installed, and a server started inside a Jest test needs no `--experimental-vm-modules`. The first search without the package fails with an error naming it.
164
+
161
165
  ## SkillContext: loadInstructions() and build()
162
166
 
163
167
  The `SkillContext` class resolves instructions regardless of the source type. When the framework serves a skill, it calls `build()` which internally calls `loadInstructions()`.
@@ -87,7 +87,7 @@ FrontMCP uses a hierarchical decorator system. The nesting order is:
87
87
  | `pagination?` | List operation pagination (`tools/list` endpoint) |
88
88
  | `fetch?` | What `this.fetch()` adds upstream: `forwardCallerTokenTo` / `forwardCustomHeadersTo` origin allow-lists (default: none), `autoInjectTracingHeaders`, `requestTimeout` |
89
89
  | `ui?` | UI rendering config (CDN overrides for widget imports) |
90
- | `extApps?` | Widget-to-host MCP Apps communication (host capabilities, session validation) |
90
+ | `extApps?` | Widget-to-host MCP Apps communication (host capabilities, session validation). `ui/callServerTool` returns the tool's data without its page; `ui/log` answers `result: {}` |
91
91
  | `loader?` | Default npm/ESM package loader for `App.esm()` / `App.remote()` apps |
92
92
 
93
93
  > **Throttle vs per-tool guards:** Server-level `throttle` is a `GuardConfig` object with `global`, `defaultRateLimit`, `defaultConcurrency`, `defaultTimeout` sub-fields that set server-wide defaults. Tool-level `rateLimit`, `concurrency`, `timeout` fields (on `@Tool`) override these defaults per tool.
@@ -123,7 +123,7 @@ class MyServer {}
123
123
  | `tools?` | Array of tool classes or function-built tools |
124
124
  | `resources?` | Array of resource classes or function-built resources |
125
125
  | `prompts?` | Array of prompt classes or function-built prompts |
126
- | `agents?` | Array of agent classes (each exposed as `use-agent:<name>` tool) |
126
+ | `agents?` | Array of agent classes (each exposed as an `invoke_<id>` tool) |
127
127
  | `skills?` | Array of skill definitions |
128
128
  | `plugins?` | App-scoped plugins |
129
129
  | `providers?` | App-scoped DI providers |
@@ -331,10 +331,11 @@ class UserProfileResource extends ResourceContext {
331
331
  | `llm` | LLM configuration (model, provider, temperature, etc.) |
332
332
  | `inputSchema?` | Zod raw shape for agent input |
333
333
  | `outputSchema?` | Zod schema for structured output |
334
- | `tools?` | Tools available to this agent |
335
- | `agents?` | Sub-agents for delegation |
336
- | `exports?` | What capabilities to expose externally |
337
- | `swarm?` | Multi-agent swarm configuration |
334
+ | `tools?` | Tools offered to this agent's model |
335
+ | `agents?` | Nested agents, offered to its model as `invoke_<id>` |
336
+ | `exports?` | `{ resources?, prompts?, providers? }` shared with app |
337
+ | `swarm?` | Which other agents it can call, and how deep |
338
+ | `execution?` | Loop limits, `inheritParentTools`, `inheritPlugins` |
338
339
 
339
340
  ```typescript
340
341
  import { Agent, AgentContext, z } from '@frontmcp/sdk';
@@ -348,11 +349,7 @@ import { Agent, AgentContext, z } from '@frontmcp/sdk';
348
349
  },
349
350
  tools: [WebSearchTool, SummarizeTool],
350
351
  })
351
- class ResearchAgent extends AgentContext {
352
- async execute(input: { topic: string }) {
353
- return this.run(`Research and summarize: ${input.topic}`);
354
- }
355
- }
352
+ class ResearchAgent extends AgentContext {} // the default execute() runs the LLM loop
356
353
  ```
357
354
 
358
355
  ---
@@ -384,6 +381,8 @@ class ResearchAgent extends AgentContext {
384
381
  | `allowedTools?` | Space-delimited pre-approved tool names (Agent Skills spec) |
385
382
  | `resources?` | Bundled dirs: `{ scripts?, references?, assets? }` (Agent Skills spec) |
386
383
 
384
+ `toolValidation: 'strict'` makes the server refuse to start when a referenced tool isn't registered.
385
+
387
386
  ```typescript
388
387
  import { Skill } from '@frontmcp/sdk';
389
388
 
@@ -5,7 +5,7 @@ description: Overview of all official FrontMCP adapters that convert external de
5
5
 
6
6
  # Official Adapters
7
7
 
8
- Adapters convert external definitions (OpenAPI specs, Lambda functions, etc.) into MCP tools, resources, and prompts automatically. They are registered in the `adapters` array of `@App`.
8
+ Adapters convert external definitions (OpenAPI specs, Lambda functions, etc.) into MCP tools, resources, and prompts automatically. They are registered in the `adapters` array of `@App` (that app's entries) or of `@FrontMcp` (entries every app serves).
9
9
 
10
10
  ## When to Use This Skill
11
11