@frontmcp/skills 1.8.7 → 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 (62) hide show
  1. package/README.md +107 -155
  2. package/catalog/create-tool/examples/21-tool-with-availability-constraints.md +1 -1
  3. package/catalog/create-tool/references/availability.md +10 -10
  4. package/catalog/create-tool/references/ui-widgets.md +30 -8
  5. package/catalog/frontmcp-authorities/SKILL.md +5 -0
  6. package/catalog/frontmcp-channels/SKILL.md +17 -16
  7. package/catalog/frontmcp-channels/references/channel-sources.md +4 -4
  8. package/catalog/frontmcp-channels/references/channel-two-way.md +1 -1
  9. package/catalog/frontmcp-config/examples/configure-deployment-targets/distributed-ha-config.md +2 -0
  10. package/catalog/frontmcp-config/examples/configure-session/vercel-kv-session.md +1 -2
  11. package/catalog/frontmcp-config/examples/configure-transport/custom-protocol-flags.md +0 -1
  12. package/catalog/frontmcp-config/examples/configure-transport/distributed-sessions-redis.md +0 -1
  13. package/catalog/frontmcp-config/examples/configure-transport/stateless-serverless.md +2 -3
  14. package/catalog/frontmcp-config/examples/configure-transport-protocol-presets/stateless-api-serverless.md +2 -3
  15. package/catalog/frontmcp-config/references/configure-auth.md +1 -1
  16. package/catalog/frontmcp-config/references/configure-deployment-targets.md +54 -20
  17. package/catalog/frontmcp-config/references/configure-http.md +5 -2
  18. package/catalog/frontmcp-config/references/configure-throttle-guard-config.md +1 -1
  19. package/catalog/frontmcp-config/references/configure-throttle.md +4 -2
  20. package/catalog/frontmcp-config/references/configure-transport.md +4 -5
  21. package/catalog/frontmcp-deployment/SKILL.md +19 -19
  22. package/catalog/frontmcp-deployment/examples/deploy-to-node/docker-compose-with-redis.md +1 -1
  23. package/catalog/frontmcp-deployment/examples/deploy-to-node/pm2-with-nginx.md +1 -1
  24. package/catalog/frontmcp-deployment/references/build-for-browser.md +38 -9
  25. package/catalog/frontmcp-deployment/references/build-for-mcpb.md +15 -9
  26. package/catalog/frontmcp-deployment/references/build-for-sdk.md +2 -1
  27. package/catalog/frontmcp-deployment/references/deploy-to-cloudflare.md +3 -1
  28. package/catalog/frontmcp-deployment/references/deploy-to-lambda.md +2 -2
  29. package/catalog/frontmcp-deployment/references/deploy-to-node.md +11 -11
  30. package/catalog/frontmcp-deployment/references/mcp-client-integration.md +12 -7
  31. package/catalog/frontmcp-deployment/references/protocol-versions.md +7 -3
  32. package/catalog/frontmcp-development/examples/create-agent/nested-agents-with-swarm.md +50 -10
  33. package/catalog/frontmcp-development/examples/openapi-adapter/ref-security-and-filtering.md +13 -7
  34. package/catalog/frontmcp-development/references/create-adapter.md +14 -0
  35. package/catalog/frontmcp-development/references/create-agent.md +82 -49
  36. package/catalog/frontmcp-development/references/create-plugin-hooks.md +15 -5
  37. package/catalog/frontmcp-development/references/create-plugin.md +8 -4
  38. package/catalog/frontmcp-development/references/create-skill-with-tools.md +7 -2
  39. package/catalog/frontmcp-development/references/create-skill.md +4 -0
  40. package/catalog/frontmcp-development/references/decorators-guide.md +10 -11
  41. package/catalog/frontmcp-development/references/official-adapters.md +1 -1
  42. package/catalog/frontmcp-development/references/official-plugins.md +127 -24
  43. package/catalog/frontmcp-development/references/openapi-adapter.md +52 -2
  44. package/catalog/frontmcp-observability/references/metrics-endpoint.md +3 -1
  45. package/catalog/frontmcp-production-readiness/examples/distributed-ha/ha-kubernetes-3-replicas.md +12 -1
  46. package/catalog/frontmcp-production-readiness/references/common-checklist.md +2 -1
  47. package/catalog/frontmcp-production-readiness/references/distributed-ha.md +48 -28
  48. package/catalog/frontmcp-setup/examples/nx-workflow/build-test-affected.md +2 -2
  49. package/catalog/frontmcp-setup/examples/nx-workflow/multi-server-deployment.md +9 -5
  50. package/catalog/frontmcp-setup/examples/nx-workflow/scaffold-and-generate.md +1 -1
  51. package/catalog/frontmcp-setup/examples/project-structure-nx/nx-generator-scaffolding.md +1 -1
  52. package/catalog/frontmcp-setup/references/frontmcp-skills-usage.md +28 -15
  53. package/catalog/frontmcp-setup/references/multi-app-composition.md +11 -8
  54. package/catalog/frontmcp-setup/references/nx-workflow.md +53 -21
  55. package/catalog/frontmcp-setup/references/project-structure-nx.md +7 -4
  56. package/catalog/frontmcp-setup/references/setup-project.md +15 -0
  57. package/catalog/frontmcp-setup/references/setup-redis.md +12 -3
  58. package/catalog/frontmcp-setup/references/setup-sqlite.md +12 -8
  59. package/catalog/frontmcp-testing/SKILL.md +16 -12
  60. package/catalog/frontmcp-testing/references/setup-testing.md +14 -1
  61. package/catalog/skills-manifest.json +10 -8
  62. package/package.json +1 -1
@@ -41,7 +41,7 @@ This skill walks you through deploying a FrontMCP server as a standalone Node.js
41
41
  frontmcp build --target node
42
42
  ```
43
43
 
44
- This compiles your TypeScript source, bundles dependencies, and produces a production-ready output in `dist/`. The build output includes compiled JavaScript optimized for Node.js, a `package.json` with production dependencies only, and any static assets.
44
+ This compiles your TypeScript source, bundles dependencies, and writes the output to `dist/node/`: a CommonJS single-file bundle at `dist/node/<name>.bundle.js` (`<name>` is the `name` in `frontmcp.config`, or the unscoped `package.json` name without a config file), a `dist/node/<name>` runner script, and any static assets. The examples below use `my-server` as `<name>`.
45
45
 
46
46
  ## Step 2: Dockerfile (Multi-Stage)
47
47
 
@@ -66,7 +66,7 @@ RUN yarn install --frozen-lockfile --production && yarn cache clean
66
66
  EXPOSE 3000
67
67
  HEALTHCHECK --interval=30s --timeout=5s --retries=3 --start-period=10s \
68
68
  CMD wget -qO- http://localhost:3000/healthz || exit 1
69
- CMD ["node", "dist/main.js"]
69
+ CMD ["node", "dist/node/my-server.bundle.js"]
70
70
  ```
71
71
 
72
72
  The first stage installs all dependencies and builds the project. The second stage copies only the compiled output and production dependencies into a slim image.
@@ -173,7 +173,7 @@ When running without Docker, use PM2 as a process manager:
173
173
  npm install -g pm2
174
174
 
175
175
  # Start the server with cluster mode (one instance per CPU core)
176
- pm2 start dist/main.js --name frontmcp-server -i max
176
+ pm2 start dist/node/my-server.bundle.js --name frontmcp-server -i max
177
177
 
178
178
  # Save the process list for auto-restart on reboot
179
179
  pm2 save
@@ -223,20 +223,20 @@ services:
223
223
 
224
224
  ## Common Patterns
225
225
 
226
- | Pattern | Correct | Incorrect | Why |
227
- | ------------------------- | ------------------------------------ | ------------------------------------------------ | ------------------------------------------------------------------- |
228
- | Build command | `frontmcp build --target node` | `tsc && node dist/main.js` | The FrontMCP build bundles deps and produces an optimized output |
229
- | Docker base image | `node:24-alpine` (multi-stage) | `node:24` (single stage with dev deps) | Multi-stage keeps the production image small and secure |
230
- | Process manager | PM2 with `-i max` cluster mode | Running `node dist/main.js` directly via `nohup` | PM2 handles restarts, logging, and multi-core clustering |
231
- | Redis hostname in Compose | Service name `redis` | `localhost` or `127.0.0.1` | Containers communicate via Docker's internal DNS, not localhost |
232
- | Environment config | `.env` file or orchestrator env vars | Hardcoded values in source code | Keeps secrets out of the codebase and allows per-environment config |
226
+ | Pattern | Correct | Incorrect | Why |
227
+ | ------------------------- | ------------------------------------ | --------------------------------------- | ------------------------------------------------------------------- |
228
+ | Build command | `frontmcp build --target node` | `tsc && node dist/main.js` | The FrontMCP build bundles deps and produces an optimized output |
229
+ | Docker base image | `node:24-alpine` (multi-stage) | `node:24` (single stage with dev deps) | Multi-stage keeps the production image small and secure |
230
+ | Process manager | PM2 with `-i max` cluster mode | Running the bundle directly via `nohup` | PM2 handles restarts, logging, and multi-core clustering |
231
+ | Redis hostname in Compose | Service name `redis` | `localhost` or `127.0.0.1` | Containers communicate via Docker's internal DNS, not localhost |
232
+ | Environment config | `.env` file or orchestrator env vars | Hardcoded values in source code | Keeps secrets out of the codebase and allows per-environment config |
233
233
 
234
234
  ## Verification Checklist
235
235
 
236
236
  **Build**
237
237
 
238
238
  - [ ] `frontmcp build --target node` completes without errors
239
- - [ ] `dist/main.js` exists and is runnable with `node dist/main.js`
239
+ - [ ] `dist/node/<name>.bundle.js` exists and is runnable with `node dist/node/<name>.bundle.js`
240
240
 
241
241
  **Docker**
242
242
 
@@ -102,12 +102,17 @@ Wire the client at the bridge:
102
102
 
103
103
  Bridge guarantees:
104
104
 
105
- - Stdout is 100% JSON-RPC frames; diagnostics go to `./.frontmcp/dev.log` (override with `--log-file`).
106
- - Session id survives reload (pinned via `FRONTMCP_DEV_FORCE_SESSION_ID`).
107
- - Buffered RPCs during reload drain in FIFO once the child reports ready.
105
+ - Stdout is 100% JSON-RPC frames; diagnostics go to `.frontmcp/dev.log` in the project root (override with `--log-file`), start-up notices (port picked, `.env` loaded) to stderr.
106
+ - Same project setup as `frontmcp dev`: `frontmcp.config.*` (from any subfolder), `entry`, `transport.http.port` / `path`, `env` overlays and `.env`. The server gets `PORT` + `FRONTMCP_HTTP_ENTRY_PATH`; with no port chosen anywhere the bridge picks a free loopback port.
107
+ - The server reports the port and MCP path it really serves (`__FRONTMCP_BOOTSTRAP_COMPLETE__ {"port":…,"path":…}` on stderr), so values hard-coded in `@FrontMcp({ http })` work.
108
+ - The client stays connected across reloads: the bridge uses the `mcp-session-id` the server issues, replays the client's `initialize` handshake on the restarted server, then sends `notifications/tools/list_changed` (and resources/prompts when advertised). Server-side session state starts fresh on each reload.
109
+ - Buffered RPCs during reload drain in FIFO once the new server is ready and initialized. A server that rejects the replayed handshake is a failed reload: it is stopped, and buffered RPCs wait for the next save or answer `dev_reload_deadline`.
108
110
  - Reload deadline + buffer overflow surface structured errors (`dev_server_unreachable` / `dev_buffer_full` / `dev_reload_deadline` — codes -32099 / -32098 / -32097) so the client spinner clears instead of hanging.
111
+ - Closing stdin or `SIGINT` / `SIGTERM` stops the server and everything it started.
109
112
 
110
- Flags: `--stdio`, `--serve`, `--log-file <path>`, `--buffer-size <n>` (default 8), `--reload-deadline-ms <ms>` (default 30000), `-p <port>` (HTTP-mode loopback, default 3000).
113
+ `--serve` runs the entry with the project's `tsx` (`node --import tsx`) so the server owns the IPC channel — keep `tsx` in devDependencies (scaffolded projects have it).
114
+
115
+ Flags: `--stdio`, `--serve`, `--log-file <path>`, `--buffer-size <n>` (default 8), `--reload-deadline-ms <ms>` (default 30000), `-p <port>` (HTTP-mode loopback; default `transport.http.port`, then `PORT`, then a free port), `--auto-port`.
111
116
 
112
117
  ## Stdio Transport
113
118
 
@@ -184,9 +189,9 @@ itself before any framework initialization:
184
189
  }
185
190
  ```
186
191
 
187
- > Do not run the raw `--target node` bundle as `node dist/node/my-server.bundle.js --stdio`
188
- > — that bundle is your `@FrontMcp` server module and starts the HTTP server on
189
- > import. Use the runner above, or set `FRONTMCP_STDIO=1` before the bundle loads.
192
+ > The bundle itself honors the flag too: `node dist/node/my-server.bundle.js --stdio`
193
+ > serves stdio and binds no port (and, run directly, it serves MCP at
194
+ > `transport.http.path` like the runner does).
190
195
 
191
196
  ## HTTP Transport
192
197
 
@@ -188,11 +188,15 @@ resolves with the final result either way.
188
188
  For a remote app, negotiate per remote:
189
189
 
190
190
  ```ts
191
- transportOptions: {
192
- protocolVersion: 'auto';
193
- } // 'legacy' (default) | '2026-07-28' | 'auto'
191
+ App.remote('https://example.com/mcp', {
192
+ transportOptions: {
193
+ protocolVersion: 'auto', // 'legacy' (default) | '2026-07-28' | 'auto'
194
+ },
195
+ });
194
196
  ```
195
197
 
198
+ `'auto'` probes `server/discover` and falls back to the session transports; `'2026-07-28'` always uses the stateless client and requires a URL remote (other transports are refused at connect time).
199
+
196
200
  ## Deprecated in this revision
197
201
 
198
202
  Still functional; do not adopt in new servers:
@@ -2,7 +2,7 @@
2
2
  name: nested-agents-with-swarm
3
3
  reference: create-agent
4
4
  level: advanced
5
- description: 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.
5
+ description: 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.
6
6
  tags:
7
7
  - development
8
8
  - agent
@@ -10,15 +10,17 @@ tags:
10
10
  - agents
11
11
  - swarm
12
12
  features:
13
- - 'Setting `swarm: { canSeeOtherAgents: true, visibleAgents: [...] }` on the orchestrator so peers appear as `use-agent:*` tools'
13
+ - 'Setting `swarm: { canSeeOtherAgents: true, visibleAgents: [...] }` on the orchestrator so peers are offered to its model as `invoke_*` tools'
14
14
  - 'Setting `swarm: { isVisible: true }` (the default) on specialist peers so they can be called'
15
- - Routing is driven by the orchestrator LLM choosing among `use-agent:<peer>` tools, not by a declarative handoff table
15
+ - Routing is driven by the orchestrator LLM choosing among `invoke_<peer>` tools, not by a declarative handoff table
16
+ - 'A nested sub-agent (`agents: [...]`) private to the orchestrator, called from code with `this.invokeAgent()`'
17
+ - '`swarm.maxCallDepth` bounding how deep agents may call each other'
16
18
  - Each agent has its own `llm` config, `tools`, and `systemInstructions` for specialization
17
19
  ---
18
20
 
19
21
  # Multi-Agent Swarm Visibility
20
22
 
21
- 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.
23
+ 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.
22
24
 
23
25
  ## Code
24
26
 
@@ -42,6 +44,8 @@ class LookupInvoiceTool extends ToolContext {
42
44
  name: 'billing_agent',
43
45
  description: 'Handles billing and payment inquiries',
44
46
  llm: { provider: 'anthropic', model: 'claude-sonnet-4-20250514', apiKey: { env: 'ANTHROPIC_API_KEY' } },
47
+ // The arguments of its invoke_billing_agent tool: an agent without an inputSchema receives {}.
48
+ inputSchema: { request: z.string().describe('The billing request') },
45
49
  tools: [LookupInvoiceTool],
46
50
  // isVisible defaults to true; specialists do not need swarm config to be callable.
47
51
  swarm: { isVisible: true },
@@ -51,23 +55,45 @@ class BillingAgent extends AgentContext {}
51
55
 
52
56
  ```typescript
53
57
  // src/apps/support/agents/technical.agent.ts
54
- import { Agent, AgentContext } from '@frontmcp/sdk';
58
+ import { Agent, AgentContext, z } from '@frontmcp/sdk';
55
59
 
56
60
  @Agent({
57
61
  id: 'technical_agent',
58
62
  name: 'technical_agent',
59
63
  description: 'Handles technical support issues',
60
64
  llm: { provider: 'anthropic', model: 'claude-sonnet-4-20250514', apiKey: { env: 'ANTHROPIC_API_KEY' } },
65
+ inputSchema: { request: z.string().describe('The technical issue') },
61
66
  systemInstructions: 'You are a technical support specialist. Diagnose issues and provide solutions.',
62
67
  swarm: { isVisible: true },
63
68
  })
64
69
  class TechnicalAgent extends AgentContext {}
65
70
  ```
66
71
 
72
+ ```typescript
73
+ // src/apps/support/agents/sentiment.agent.ts
74
+ import { Agent, AgentContext, z } from '@frontmcp/sdk';
75
+
76
+ // Nested in the triage agent below: private to it, never offered to clients.
77
+ @Agent({
78
+ id: 'sentiment_agent',
79
+ name: 'sentiment_agent',
80
+ description: 'Rates how upset a customer is',
81
+ llm: { provider: 'anthropic', model: 'claude-sonnet-4-20250514', apiKey: { env: 'ANTHROPIC_API_KEY' } },
82
+ inputSchema: { request: z.string() },
83
+ outputSchema: { urgency: z.enum(['low', 'high']) },
84
+ // The model's reply is parsed as the output: ask for JSON that matches outputSchema.
85
+ systemInstructions:
86
+ 'Rate how urgent the request is. Reply only with JSON: {"urgency": "low"} or {"urgency": "high"}.',
87
+ })
88
+ export class SentimentAgent extends AgentContext {}
89
+ ```
90
+
67
91
  ```typescript
68
92
  // src/apps/support/agents/triage.agent.ts
69
93
  import { Agent, AgentContext, z } from '@frontmcp/sdk';
70
94
 
95
+ import { SentimentAgent } from './sentiment.agent';
96
+
71
97
  @Agent({
72
98
  id: 'triage_agent',
73
99
  name: 'triage_agent',
@@ -76,16 +102,28 @@ import { Agent, AgentContext, z } from '@frontmcp/sdk';
76
102
  inputSchema: {
77
103
  request: z.string().describe('The incoming user request'),
78
104
  },
105
+ // Nested sub-agent: offered to this agent's model as invoke_sentiment_agent, and callable from code.
106
+ agents: [SentimentAgent],
79
107
  // Orchestrator: opts in to seeing peers and (optionally) restricts to a whitelist.
80
108
  swarm: {
81
109
  canSeeOtherAgents: true,
82
110
  visibleAgents: ['billing_agent', 'technical_agent'],
83
- maxCallDepth: 3,
111
+ maxCallDepth: 3, // a deeper agent-to-agent chain fails with AGENT_CALL_DEPTH_EXCEEDED
84
112
  },
85
113
  systemInstructions:
86
- 'Analyze the request and delegate by calling either use-agent:billing_agent (for billing/payments) or use-agent:technical_agent (for technical issues).',
114
+ 'Analyze the request and delegate by calling either invoke_billing_agent (for billing/payments) or invoke_technical_agent (for technical issues).',
87
115
  })
88
- class TriageAgent extends AgentContext {}
116
+ class TriageAgent extends AgentContext {
117
+ async execute(input: { request: string }) {
118
+ // Code can call a nested agent, or a peer it sees, and gets its output back.
119
+ const sentiment = (await this.invokeAgent('sentiment_agent', { request: input.request })) as { urgency: string };
120
+ if (sentiment.urgency === 'high') {
121
+ return this.invokeAgent('technical_agent', { request: input.request });
122
+ }
123
+ // Otherwise let the model route among invoke_billing_agent / invoke_technical_agent.
124
+ return super.execute(input);
125
+ }
126
+ }
89
127
  ```
90
128
 
91
129
  ```typescript
@@ -101,9 +139,11 @@ class SupportApp {}
101
139
 
102
140
  ## What This Demonstrates
103
141
 
104
- - Setting `swarm: { canSeeOtherAgents: true, visibleAgents: [...] }` on the orchestrator so peers appear as `use-agent:*` tools
142
+ - Setting `swarm: { canSeeOtherAgents: true, visibleAgents: [...] }` on the orchestrator so peers are offered to its model as `invoke_*` tools
105
143
  - Setting `swarm: { isVisible: true }` (the default) on specialist peers so they can be called
106
- - Routing is driven by the orchestrator LLM choosing among `use-agent:<peer>` tools, not by a declarative handoff table
144
+ - Routing is driven by the orchestrator LLM choosing among `invoke_<peer>` tools, not by a declarative handoff table
145
+ - A nested sub-agent (`agents: [...]`) private to the orchestrator, called from code with `this.invokeAgent()`
146
+ - `swarm.maxCallDepth` bounding how deep agents may call each other
107
147
  - Each agent has its own `llm` config, `tools`, and `systemInstructions` for specialization
108
148
 
109
149
  ## Related
@@ -8,7 +8,7 @@ features:
8
8
  - 'Secure defaults: external `$ref` resolution off, spec-URL redirects not followed, internal/private targets blocked (DNS-resolved) — on `mcp-from-openapi` >= 2.5.0'
9
9
  - 'Opting back into external refs with `allowedProtocols`, and restricting the spec URL + `$ref`s with `allowedHosts`'
10
10
  - 'Using `allowInternalIPs` for trusted internal/local targets (governs the spec URL and `$ref`s)'
11
- - 'Filtering operations with `includeOperations`, `excludeOperations`, and `filterFn`'
11
+ - 'Filtering operations with `readOnlyOnly`, `includeTags`, `includePaths`, `excludeMethods`, `includeOperations`, `excludeOperations`, and `filterFn`'
12
12
  - 'Combining security hardening with operation filtering for a production-ready setup'
13
13
  ---
14
14
 
@@ -20,8 +20,8 @@ Demonstrates configuring $ref / spec-URL resolution security to prevent SSRF att
20
20
 
21
21
  ```typescript
22
22
  // src/server.ts
23
- import { FrontMcp, App } from '@frontmcp/sdk';
24
23
  import { OpenapiAdapter } from '@frontmcp/adapters';
24
+ import { App, FrontMcp } from '@frontmcp/sdk';
25
25
 
26
26
  @App({
27
27
  name: 'secure-app',
@@ -45,8 +45,10 @@ import { OpenapiAdapter } from '@frontmcp/adapters';
45
45
  },
46
46
  },
47
47
  generateOptions: {
48
- // Only expose read operations to MCP clients
49
- filterFn: (op) => op.method === 'get',
48
+ // Only expose read-only operations (readOnlyHint: true — GET/HEAD/OPTIONS/TRACE by default)
49
+ readOnlyOnly: true,
50
+ // ...and only the partner's public tag
51
+ includeTags: ['public'],
50
52
  // Skip deprecated endpoints
51
53
  includeDeprecated: false,
52
54
  },
@@ -88,8 +90,12 @@ import { OpenapiAdapter } from '@frontmcp/adapters';
88
90
  generateOptions: {
89
91
  // Exclude admin and dangerous operations
90
92
  excludeOperations: ['deleteAll', 'resetDatabase', 'adminPanel'],
91
- // Only include billing-related paths
92
- filterFn: (op) => op.path.startsWith('/billing') || op.path.startsWith('/invoices'),
93
+ // Never expose deletes (lower-case HTTP method names)
94
+ excludeMethods: ['delete'],
95
+ // Only include billing-related paths (globs: `*` within a segment, `**` across segments)
96
+ includePaths: ['/billing/**', '/invoices/**'],
97
+ // Anything a declarative filter can't express goes in filterFn (runs after the others)
98
+ filterFn: (op) => !op.summary?.toLowerCase().includes('experimental'),
93
99
  },
94
100
  }),
95
101
  ],
@@ -109,7 +115,7 @@ class MyServer {}
109
115
  - Secure defaults: external `$ref` resolution off, spec-URL redirects not followed, internal/private targets blocked (DNS-resolved) — on `mcp-from-openapi` >= 2.5.0
110
116
  - Opting back into external refs with `allowedProtocols`, and restricting the spec URL + `$ref`s with `allowedHosts`
111
117
  - Using `allowInternalIPs` for trusted internal/local targets (governs the spec URL and `$ref`s)
112
- - Filtering operations with `includeOperations`, `excludeOperations`, and `filterFn`
118
+ - Filtering operations with `readOnlyOnly`, `includeTags`, `includePaths`, `excludeMethods`, `includeOperations`, `excludeOperations`, and `filterFn`
113
119
  - Combining security hardening with operation filtering for a production-ready setup
114
120
 
115
121
  ## Related
@@ -92,6 +92,8 @@ class MyApiAdapter extends DynamicAdapter<MyAdapterOptions> {
92
92
  class MyApp {}
93
93
  ```
94
94
 
95
+ To serve the adapter's tools, resources and prompts from every app, register it on the server instead with `@FrontMcp({ adapters: [MyApiAdapter.init({ ... })] })`. Like a server-level plugin, each scope (a standalone or `splitByApp` app gets its own) builds its own adapter from the `init()` options and runs `fetch()`; a hand-written `{ provide, useValue }` record is one adapter that every scope shares. Disposing the server calls each adapter's `stopPolling()` and drops its `onUpdate()` subscription. Up to 1.8.7 `@FrontMcp({ adapters })` was silently dropped, and disposing did not stop adapter polling.
96
+
95
97
  ## FrontMcpAdapterResponse
96
98
 
97
99
  The `fetch()` method returns tools, resources, and prompts to register:
@@ -120,6 +122,18 @@ const adapter = MyApiAdapter.init({
120
122
  @App({ adapters: [adapter] })
121
123
  ```
122
124
 
125
+ When the options depend on a provider, use `init({ name, inject, useFactory })`. The factory returns the adapter's **options** (or a promise of them) and the adapter is built from them, named by the `name` given to `init()`:
126
+
127
+ ```typescript
128
+ MyApiAdapter.init({
129
+ name: 'my-api',
130
+ inject: () => [ApiConfig] as const,
131
+ useFactory: (config: ApiConfig) => ({ name: 'my-api', endpoint: config.endpoint, apiKey: config.apiKey }),
132
+ });
133
+ ```
134
+
135
+ A factory may instead return an adapter instance, used as is when its `options.name` is the `name` given to `init()`; an instance named otherwise, or anything else, fails startup with an `InvalidEntityError`.
136
+
123
137
  ## Nx Generator
124
138
 
125
139
  ```bash
@@ -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,26 +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 |
620
- | 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 |
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 |
621
654
 
622
655
  ## Examples
623
656
 
624
- | Example | Level | Description |
625
- | ---------------------------------------------------------------------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
626
- | [`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. |
627
- | [`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()`. |
628
- | [`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. |
629
662
 
630
663
  > See all examples in [`examples/create-agent/`](../examples/create-agent/)
631
664