@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.
- package/README.md +107 -155
- package/catalog/create-tool/SKILL.md +24 -24
- package/catalog/create-tool/examples/21-tool-with-availability-constraints.md +1 -1
- package/catalog/create-tool/examples/22-tool-with-ui-html-template.md +0 -1
- package/catalog/create-tool/examples/23-tool-with-ui-filesource-tsx.md +1 -3
- package/catalog/create-tool/examples/24-tool-with-ui-csp-and-bridge.md +4 -6
- package/catalog/create-tool/references/availability.md +10 -10
- package/catalog/create-tool/references/decorator-options.md +1 -1
- package/catalog/create-tool/references/ui-widgets.md +91 -42
- package/catalog/frontmcp-authorities/SKILL.md +5 -0
- package/catalog/frontmcp-channels/SKILL.md +17 -16
- package/catalog/frontmcp-channels/references/channel-sources.md +4 -4
- package/catalog/frontmcp-channels/references/channel-two-way.md +1 -1
- package/catalog/frontmcp-config/examples/configure-deployment-targets/distributed-ha-config.md +2 -0
- package/catalog/frontmcp-config/examples/configure-session/vercel-kv-session.md +1 -2
- package/catalog/frontmcp-config/examples/configure-throttle/distributed-redis-throttle.md +3 -3
- package/catalog/frontmcp-config/examples/configure-transport/custom-protocol-flags.md +0 -1
- package/catalog/frontmcp-config/examples/configure-transport/distributed-sessions-redis.md +0 -1
- package/catalog/frontmcp-config/examples/configure-transport/stateless-serverless.md +2 -3
- package/catalog/frontmcp-config/examples/configure-transport-protocol-presets/stateless-api-serverless.md +2 -3
- package/catalog/frontmcp-config/references/configure-auth.md +1 -1
- package/catalog/frontmcp-config/references/configure-deployment-targets.md +68 -16
- package/catalog/frontmcp-config/references/configure-http.md +11 -6
- package/catalog/frontmcp-config/references/configure-security-headers.md +18 -13
- package/catalog/frontmcp-config/references/configure-throttle-guard-config.md +4 -4
- package/catalog/frontmcp-config/references/configure-throttle.md +4 -2
- package/catalog/frontmcp-config/references/configure-transport.md +4 -5
- package/catalog/frontmcp-deployment/SKILL.md +19 -19
- package/catalog/frontmcp-deployment/examples/build-for-browser/react-provider-setup.md +5 -3
- package/catalog/frontmcp-deployment/examples/deploy-to-node/docker-compose-with-redis.md +1 -1
- package/catalog/frontmcp-deployment/examples/deploy-to-node/pm2-with-nginx.md +1 -1
- package/catalog/frontmcp-deployment/examples/deploy-to-vercel/vercel-with-kv.md +2 -0
- package/catalog/frontmcp-deployment/references/build-for-browser.md +46 -9
- package/catalog/frontmcp-deployment/references/build-for-mcpb.md +15 -8
- package/catalog/frontmcp-deployment/references/build-for-sdk.md +11 -10
- package/catalog/frontmcp-deployment/references/deploy-to-cloudflare.md +5 -1
- package/catalog/frontmcp-deployment/references/deploy-to-lambda.md +11 -10
- package/catalog/frontmcp-deployment/references/deploy-to-node.md +11 -11
- package/catalog/frontmcp-deployment/references/deploy-to-vercel.md +15 -7
- package/catalog/frontmcp-deployment/references/mcp-client-integration.md +12 -7
- package/catalog/frontmcp-deployment/references/protocol-versions.md +7 -3
- package/catalog/frontmcp-development/examples/create-agent/nested-agents-with-swarm.md +50 -10
- package/catalog/frontmcp-development/examples/openapi-adapter/ref-security-and-filtering.md +13 -7
- package/catalog/frontmcp-development/references/create-adapter.md +14 -0
- package/catalog/frontmcp-development/references/create-agent.md +82 -48
- package/catalog/frontmcp-development/references/create-plugin-hooks.md +15 -5
- package/catalog/frontmcp-development/references/create-plugin.md +23 -2
- package/catalog/frontmcp-development/references/create-skill-with-tools.md +7 -2
- package/catalog/frontmcp-development/references/create-skill.md +4 -0
- package/catalog/frontmcp-development/references/decorators-guide.md +10 -11
- package/catalog/frontmcp-development/references/official-adapters.md +1 -1
- package/catalog/frontmcp-development/references/official-plugins.md +138 -28
- package/catalog/frontmcp-development/references/openapi-adapter.md +52 -2
- package/catalog/frontmcp-guides/references/example-task-manager.md +2 -2
- package/catalog/frontmcp-guides/references/example-weather-api.md +2 -2
- package/catalog/frontmcp-observability/references/metrics-endpoint.md +3 -1
- package/catalog/frontmcp-production-readiness/examples/distributed-ha/ha-kubernetes-3-replicas.md +13 -1
- package/catalog/frontmcp-production-readiness/examples/production-node-sdk/package-json-config.md +2 -2
- package/catalog/frontmcp-production-readiness/references/common-checklist.md +3 -2
- package/catalog/frontmcp-production-readiness/references/distributed-ha.md +54 -24
- package/catalog/frontmcp-production-readiness/references/health-readiness-endpoints.md +3 -1
- package/catalog/frontmcp-setup/examples/nx-workflow/build-test-affected.md +2 -2
- package/catalog/frontmcp-setup/examples/nx-workflow/multi-server-deployment.md +9 -5
- package/catalog/frontmcp-setup/examples/nx-workflow/scaffold-and-generate.md +1 -1
- package/catalog/frontmcp-setup/examples/project-structure-nx/nx-generator-scaffolding.md +2 -2
- package/catalog/frontmcp-setup/references/frontmcp-skills-usage.md +28 -15
- package/catalog/frontmcp-setup/references/multi-app-composition.md +11 -8
- package/catalog/frontmcp-setup/references/nx-workflow.md +68 -28
- package/catalog/frontmcp-setup/references/project-structure-nx.md +7 -4
- package/catalog/frontmcp-setup/references/setup-project.md +15 -0
- package/catalog/frontmcp-setup/references/setup-redis.md +12 -3
- package/catalog/frontmcp-setup/references/setup-sqlite.md +21 -14
- package/catalog/frontmcp-testing/SKILL.md +28 -23
- package/catalog/frontmcp-testing/references/setup-testing.md +39 -2
- package/catalog/frontmcp-testing/references/test-auth.md +8 -0
- package/catalog/skills-manifest.json +14 -12
- 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(
|
|
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` --
|
|
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
|
-
##
|
|
312
|
+
## Exports
|
|
312
313
|
|
|
313
|
-
|
|
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], //
|
|
325
|
-
|
|
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
|
|
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 (
|
|
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 `
|
|
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
|
|
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
|
|
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
|
|
431
|
-
name: '
|
|
432
|
-
description: '
|
|
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
|
|
440
|
-
maxLength: z.number().default(100).describe('Max
|
|
457
|
+
text: z.string().describe('Text to truncate'),
|
|
458
|
+
maxLength: z.number().default(100).describe('Max length'),
|
|
441
459
|
},
|
|
442
|
-
})((input
|
|
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
|
-
|
|
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: [
|
|
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
|
|
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
|
|
586
|
-
|
|
|
587
|
-
| LLM config
|
|
588
|
-
| Inner tools vs
|
|
589
|
-
| Custom execute
|
|
590
|
-
| Sub-agents
|
|
591
|
-
| Swarm visibility
|
|
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
|
|
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
|
|
614
|
-
|
|
|
615
|
-
| Agent not appearing in tool listing
|
|
616
|
-
| LLM authentication error
|
|
617
|
-
| Inner tools not being called
|
|
618
|
-
| Agent times out
|
|
619
|
-
| Peer agent not callable
|
|
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 `
|
|
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
|
|
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 →
|
|
381
|
-
→
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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 `
|
|
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
|
|
335
|
-
| `agents?` |
|
|
336
|
-
| `exports?` |
|
|
337
|
-
| `swarm?` |
|
|
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
|
|