@frontmcp/skills 1.1.2 → 1.2.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 (176) hide show
  1. package/catalog/TEMPLATE.md +16 -11
  2. package/catalog/frontmcp-authorities/SKILL.md +116 -11
  3. package/catalog/frontmcp-authorities/references/authority-profiles.md +39 -36
  4. package/catalog/frontmcp-authorities/references/claims-mapping.md +7 -0
  5. package/catalog/frontmcp-authorities/references/custom-evaluators.md +63 -14
  6. package/catalog/frontmcp-channels/SKILL.md +36 -0
  7. package/catalog/frontmcp-channels/examples/channel-sources/file-watcher.md +8 -2
  8. package/catalog/frontmcp-channels/examples/channel-sources/replay-buffer.md +111 -30
  9. package/catalog/frontmcp-channels/examples/channel-two-way/whatsapp-bridge.md +45 -3
  10. package/catalog/frontmcp-channels/references/channel-sources.md +11 -3
  11. package/catalog/frontmcp-channels/references/channel-two-way.md +60 -89
  12. package/catalog/frontmcp-config/SKILL.md +111 -8
  13. package/catalog/frontmcp-config/examples/configure-auth-modes/local-self-signed-tokens.md +4 -4
  14. package/catalog/frontmcp-config/examples/configure-auth-modes/remote-enterprise-oauth.md +7 -1
  15. package/catalog/frontmcp-config/examples/configure-deployment-targets/distributed-ha-config.md +1 -1
  16. package/catalog/frontmcp-config/examples/configure-deployment-targets/json-schema-ide-support.md +1 -1
  17. package/catalog/frontmcp-config/examples/configure-deployment-targets/multi-target-with-security.md +12 -9
  18. package/catalog/frontmcp-config/examples/configure-http/cors-restricted-origins.md +2 -2
  19. package/catalog/frontmcp-config/examples/configure-http/entry-path-reverse-proxy.md +1 -1
  20. package/catalog/frontmcp-config/examples/configure-security-headers/csp-report-only.md +1 -1
  21. package/catalog/frontmcp-config/examples/configure-security-headers/full-production-headers.md +1 -1
  22. package/catalog/frontmcp-config/examples/configure-skills-http/audit-log-basic.md +76 -0
  23. package/catalog/frontmcp-config/examples/configure-skills-http/audit-log-redis.md +116 -0
  24. package/catalog/frontmcp-config/examples/configure-skills-http/inject-instructions.md +59 -0
  25. package/catalog/frontmcp-config/references/configure-auth-modes.md +5 -5
  26. package/catalog/frontmcp-config/references/configure-deployment-targets.md +27 -24
  27. package/catalog/frontmcp-config/references/configure-http.md +14 -10
  28. package/catalog/frontmcp-config/references/configure-security-headers.md +2 -2
  29. package/catalog/frontmcp-config/references/configure-session.md +25 -25
  30. package/catalog/frontmcp-config/references/configure-skills-http.md +157 -0
  31. package/catalog/frontmcp-config/references/configure-throttle.md +1 -1
  32. package/catalog/frontmcp-config/references/configure-transport.md +2 -2
  33. package/catalog/frontmcp-deployment/SKILL.md +112 -9
  34. package/catalog/frontmcp-deployment/examples/build-for-browser/browser-build-with-custom-entry.md +23 -11
  35. package/catalog/frontmcp-deployment/examples/build-for-browser/browser-crypto-and-storage.md +44 -17
  36. package/catalog/frontmcp-deployment/examples/build-for-browser/react-provider-setup.md +53 -21
  37. package/catalog/frontmcp-deployment/examples/build-for-cli/cli-binary-build.md +1 -1
  38. package/catalog/frontmcp-deployment/examples/build-for-cli/unix-socket-daemon.md +1 -1
  39. package/catalog/frontmcp-deployment/examples/build-for-mcpb/mcpb-bundle-build.md +1 -1
  40. package/catalog/frontmcp-deployment/examples/build-for-sdk/connect-openai.md +1 -1
  41. package/catalog/frontmcp-deployment/examples/build-for-sdk/multi-platform-connect.md +1 -1
  42. package/catalog/frontmcp-deployment/examples/deploy-to-cloudflare/basic-worker-deploy.md +7 -8
  43. package/catalog/frontmcp-deployment/examples/deploy-to-cloudflare/worker-custom-domain.md +8 -6
  44. package/catalog/frontmcp-deployment/examples/deploy-to-cloudflare/worker-with-kv-storage.md +5 -4
  45. package/catalog/frontmcp-deployment/examples/deploy-to-lambda/cdk-deployment.md +8 -5
  46. package/catalog/frontmcp-deployment/examples/deploy-to-lambda/lambda-handler-with-cors.md +20 -18
  47. package/catalog/frontmcp-deployment/examples/deploy-to-lambda/sam-template-basic.md +8 -5
  48. package/catalog/frontmcp-deployment/examples/deploy-to-node/docker-compose-with-redis.md +3 -3
  49. package/catalog/frontmcp-deployment/examples/deploy-to-node/pm2-with-nginx.md +1 -1
  50. package/catalog/frontmcp-deployment/examples/deploy-to-node/resource-limits.md +2 -2
  51. package/catalog/frontmcp-deployment/examples/deploy-to-node-dockerfile/basic-multistage-dockerfile.md +2 -2
  52. package/catalog/frontmcp-deployment/examples/deploy-to-node-dockerfile/secure-nonroot-dockerfile.md +1 -1
  53. package/catalog/frontmcp-deployment/examples/deploy-to-vercel/vercel-mcp-endpoint-test.md +23 -21
  54. package/catalog/frontmcp-deployment/examples/deploy-to-vercel/vercel-with-kv.md +25 -22
  55. package/catalog/frontmcp-deployment/examples/deploy-to-vercel/vercel-with-skills-cache.md +23 -30
  56. package/catalog/frontmcp-deployment/examples/deploy-to-vercel-config/minimal-vercel-config.md +52 -28
  57. package/catalog/frontmcp-deployment/examples/deploy-to-vercel-config/vercel-config-with-security-headers.md +32 -55
  58. package/catalog/frontmcp-deployment/examples/mcp-client-integration/http-remote.md +9 -0
  59. package/catalog/frontmcp-deployment/references/build-for-browser.md +40 -17
  60. package/catalog/frontmcp-deployment/references/build-for-cli.md +8 -8
  61. package/catalog/frontmcp-deployment/references/deploy-to-cloudflare.md +43 -24
  62. package/catalog/frontmcp-deployment/references/deploy-to-lambda.md +36 -25
  63. package/catalog/frontmcp-deployment/references/deploy-to-node-dockerfile.md +56 -14
  64. package/catalog/frontmcp-deployment/references/deploy-to-node.md +9 -6
  65. package/catalog/frontmcp-deployment/references/deploy-to-vercel-config.md +57 -58
  66. package/catalog/frontmcp-deployment/references/deploy-to-vercel.md +49 -59
  67. package/catalog/frontmcp-deployment/references/mcp-client-integration.md +2 -0
  68. package/catalog/frontmcp-development/SKILL.md +186 -11
  69. package/catalog/frontmcp-development/examples/create-agent/custom-multi-pass-agent.md +1 -1
  70. package/catalog/frontmcp-development/examples/create-agent/nested-agents-with-swarm.md +30 -27
  71. package/catalog/frontmcp-development/examples/create-job/job-with-permissions.md +13 -8
  72. package/catalog/frontmcp-development/examples/create-provider/basic-database-provider.md +33 -23
  73. package/catalog/frontmcp-development/examples/create-provider/config-and-api-providers.md +19 -10
  74. package/catalog/frontmcp-development/examples/create-tool/tool-with-rate-limiting-and-progress.md +3 -3
  75. package/catalog/frontmcp-development/examples/create-workflow/webhook-triggered-workflow.md +6 -4
  76. package/catalog/frontmcp-development/examples/decorators-guide/agent-skill-job-workflow.md +1 -1
  77. package/catalog/frontmcp-development/examples/decorators-guide/basic-server-with-app-and-tools.md +13 -8
  78. package/catalog/frontmcp-development/examples/decorators-guide/multi-app-with-plugins-and-providers.md +50 -23
  79. package/catalog/frontmcp-development/references/create-agent.md +47 -30
  80. package/catalog/frontmcp-development/references/create-job.md +69 -54
  81. package/catalog/frontmcp-development/references/create-plugin-hooks.md +45 -28
  82. package/catalog/frontmcp-development/references/create-plugin.md +10 -8
  83. package/catalog/frontmcp-development/references/create-prompt.md +3 -3
  84. package/catalog/frontmcp-development/references/create-provider.md +91 -51
  85. package/catalog/frontmcp-development/references/create-resource.md +3 -3
  86. package/catalog/frontmcp-development/references/create-skill.md +2 -2
  87. package/catalog/frontmcp-development/references/create-tool.md +7 -7
  88. package/catalog/frontmcp-development/references/create-workflow.md +8 -10
  89. package/catalog/frontmcp-development/references/decorators-guide.md +92 -56
  90. package/catalog/frontmcp-development/references/official-plugins.md +4 -3
  91. package/catalog/frontmcp-development/references/openapi-adapter.md +1 -1
  92. package/catalog/frontmcp-extensibility/SKILL.md +70 -10
  93. package/catalog/frontmcp-extensibility/examples/skill-audit-log/custom-store.md +197 -0
  94. package/catalog/frontmcp-extensibility/examples/skill-audit-log/verify-chain.md +68 -0
  95. package/catalog/frontmcp-extensibility/examples/vectoriadb/product-catalog-search.md +3 -5
  96. package/catalog/frontmcp-extensibility/examples/vectoriadb/semantic-search-with-persistence.md +4 -11
  97. package/catalog/frontmcp-extensibility/examples/vectoriadb/tfidf-keyword-search.md +41 -30
  98. package/catalog/frontmcp-extensibility/references/skill-audit-log.md +233 -0
  99. package/catalog/frontmcp-extensibility/references/vectoriadb.md +73 -63
  100. package/catalog/frontmcp-guides/SKILL.md +84 -27
  101. package/catalog/frontmcp-guides/examples/example-knowledge-base/agent-and-plugin.md +72 -62
  102. package/catalog/frontmcp-guides/examples/example-knowledge-base/vector-search-and-resources.md +32 -43
  103. package/catalog/frontmcp-guides/examples/example-task-manager/auth-and-crud-tools.md +24 -17
  104. package/catalog/frontmcp-guides/examples/example-task-manager/authenticated-e2e-tests.md +23 -21
  105. package/catalog/frontmcp-guides/examples/example-task-manager/redis-provider-with-di.md +47 -39
  106. package/catalog/frontmcp-guides/examples/example-weather-api/server-and-app-setup.md +16 -6
  107. package/catalog/frontmcp-guides/examples/example-weather-api/unit-and-e2e-tests.md +9 -8
  108. package/catalog/frontmcp-guides/references/example-knowledge-base.md +192 -265
  109. package/catalog/frontmcp-guides/references/example-task-manager.md +60 -54
  110. package/catalog/frontmcp-guides/references/example-weather-api.md +22 -24
  111. package/catalog/frontmcp-observability/SKILL.md +66 -2
  112. package/catalog/frontmcp-observability/examples/telemetry-api/skill-counters.md +100 -0
  113. package/catalog/frontmcp-observability/examples/tracing-setup/production-tracing.md +7 -2
  114. package/catalog/frontmcp-observability/examples/vendor-integrations/coralogix-setup.md +6 -2
  115. package/catalog/frontmcp-observability/references/telemetry-api.md +72 -8
  116. package/catalog/frontmcp-observability/references/testing-observability.md +33 -49
  117. package/catalog/frontmcp-observability/references/tracing-setup.md +12 -5
  118. package/catalog/frontmcp-observability/references/vendor-integrations.md +46 -1
  119. package/catalog/frontmcp-production-readiness/SKILL.md +134 -3
  120. package/catalog/frontmcp-production-readiness/examples/common-checklist/caching-and-performance.md +57 -36
  121. package/catalog/frontmcp-production-readiness/examples/common-checklist/observability-setup.md +1 -1
  122. package/catalog/frontmcp-production-readiness/examples/common-checklist/security-hardening.md +102 -6
  123. package/catalog/frontmcp-production-readiness/examples/production-cli-daemon/daemon-socket-config.md +2 -1
  124. package/catalog/frontmcp-production-readiness/examples/production-cli-daemon/graceful-shutdown-cleanup.md +66 -58
  125. package/catalog/frontmcp-production-readiness/examples/production-cli-daemon/security-and-permissions.md +5 -3
  126. package/catalog/frontmcp-production-readiness/examples/production-cloudflare/durable-objects-state.md +2 -1
  127. package/catalog/frontmcp-production-readiness/examples/production-cloudflare/wrangler-config.md +55 -76
  128. package/catalog/frontmcp-production-readiness/examples/production-lambda/cold-start-connection-reuse.md +43 -40
  129. package/catalog/frontmcp-production-readiness/examples/production-lambda/sam-template.md +63 -94
  130. package/catalog/frontmcp-production-readiness/examples/production-lambda/scaling-and-monitoring.md +28 -18
  131. package/catalog/frontmcp-production-readiness/examples/production-node-sdk/multi-instance-cleanup.md +29 -14
  132. package/catalog/frontmcp-production-readiness/examples/production-node-server/graceful-shutdown.md +58 -42
  133. package/catalog/frontmcp-production-readiness/examples/production-node-server/redis-session-scaling.md +5 -2
  134. package/catalog/frontmcp-production-readiness/examples/production-vercel/cold-start-optimization.md +41 -24
  135. package/catalog/frontmcp-production-readiness/examples/production-vercel/vercel-edge-config.md +56 -65
  136. package/catalog/frontmcp-production-readiness/references/common-checklist.md +17 -5
  137. package/catalog/frontmcp-production-readiness/references/production-cli-daemon.md +5 -5
  138. package/catalog/frontmcp-production-readiness/references/production-cloudflare.md +5 -5
  139. package/catalog/frontmcp-production-readiness/references/production-lambda.md +5 -5
  140. package/catalog/frontmcp-production-readiness/references/production-node-sdk.md +5 -5
  141. package/catalog/frontmcp-production-readiness/references/production-node-server.md +1 -1
  142. package/catalog/frontmcp-production-readiness/references/production-vercel.md +5 -5
  143. package/catalog/frontmcp-setup/SKILL.md +88 -0
  144. package/catalog/frontmcp-setup/examples/project-structure-nx/nx-workspace-with-apps.md +10 -4
  145. package/catalog/frontmcp-setup/examples/project-structure-standalone/dev-workflow-commands.md +21 -8
  146. package/catalog/frontmcp-setup/examples/readme-guide/node-server-readme.md +3 -3
  147. package/catalog/frontmcp-setup/references/multi-app-composition.md +4 -3
  148. package/catalog/frontmcp-setup/references/project-structure-nx.md +15 -6
  149. package/catalog/frontmcp-setup/references/project-structure-standalone.md +18 -15
  150. package/catalog/frontmcp-setup/references/readme-guide.md +1 -1
  151. package/catalog/frontmcp-setup/references/setup-project.md +19 -5
  152. package/catalog/frontmcp-setup/references/setup-redis.md +27 -39
  153. package/catalog/frontmcp-setup/references/setup-sqlite.md +25 -18
  154. package/catalog/frontmcp-testing/SKILL.md +102 -15
  155. package/catalog/frontmcp-testing/examples/setup-testing/unit-test-tool-resource-prompt.md +3 -3
  156. package/catalog/frontmcp-testing/examples/test-auth/oauth-flow-test.md +50 -39
  157. package/catalog/frontmcp-testing/examples/test-auth/role-based-access-test.md +52 -29
  158. package/catalog/frontmcp-testing/examples/test-auth/token-factory-test.md +37 -20
  159. package/catalog/frontmcp-testing/examples/test-direct-client/basic-create-test.md +25 -15
  160. package/catalog/frontmcp-testing/examples/test-direct-client/openai-claude-format-test.md +27 -21
  161. package/catalog/frontmcp-testing/examples/test-e2e-handler/basic-e2e-test.md +29 -20
  162. package/catalog/frontmcp-testing/examples/test-e2e-handler/manual-client-with-transport.md +5 -3
  163. package/catalog/frontmcp-testing/examples/test-e2e-handler/tool-call-and-error-e2e.md +35 -26
  164. package/catalog/frontmcp-testing/examples/test-tool-unit/basic-tool-test.md +8 -3
  165. package/catalog/frontmcp-testing/examples/test-tool-unit/schema-validation-test.md +4 -1
  166. package/catalog/frontmcp-testing/examples/test-tool-unit/tool-error-handling-test.md +6 -3
  167. package/catalog/frontmcp-testing/references/setup-testing.md +35 -39
  168. package/catalog/frontmcp-testing/references/test-auth.md +86 -43
  169. package/catalog/frontmcp-testing/references/test-browser-build.md +1 -1
  170. package/catalog/frontmcp-testing/references/test-direct-client.md +29 -19
  171. package/catalog/frontmcp-testing/references/test-e2e-handler.md +31 -19
  172. package/catalog/frontmcp-testing/references/test-tool-unit.md +6 -2
  173. package/catalog/skills-manifest.json +428 -339
  174. package/package.json +1 -1
  175. package/src/manifest.d.ts +13 -0
  176. package/src/manifest.js.map +1 -1
@@ -33,73 +33,81 @@ const db = new TFIDFVectoria({
33
33
  defaultTopK: 10,
34
34
  });
35
35
 
36
- // Add documents (id, text)
37
- db.addDocument('users-list', 'List all users with pagination and filtering');
38
- db.addDocument('users-create', 'Create a new user account with email and password');
39
- db.addDocument('orders-list', 'List orders for a customer with date range filters');
36
+ // Add documents (id, text, metadata) — metadata is required
37
+ db.addDocument('users-list', 'List all users with pagination and filtering', { id: 'users-list' });
38
+ db.addDocument('users-create', 'Create a new user account with email and password', { id: 'users-create' });
39
+ db.addDocument('orders-list', 'List orders for a customer with date range filters', { id: 'orders-list' });
40
40
 
41
- // Build the index (required after adding documents)
42
- db.buildIndex();
41
+ // Reindex (required after adding documents)
42
+ db.reindex();
43
43
 
44
- // Search
45
- const results = db.search('find users', 5);
44
+ // Search — second arg is SearchOptions
45
+ const results = db.search('find users', { topK: 5 });
46
46
  // results: [{ id: 'users-list', score: 0.82 }, { id: 'users-create', score: 0.65 }]
47
47
  ```
48
48
 
49
- ### With Field Weights
49
+ ### Combining Multiple Text Fields
50
50
 
51
- Weight different fields to control scoring influence:
51
+ `TFIDFVectoria` indexes a single text string per document. To match against multiple
52
+ conceptual fields (e.g. name + description + tags), concatenate them into one string
53
+ when calling `addDocument`. Store the original fields in `metadata` so they remain
54
+ available on search results.
52
55
 
53
56
  ```typescript
54
- const db = new TFIDFVectoria({
55
- fields: {
56
- name: { weight: 3 }, // Name matches are 3x more important
57
- description: { weight: 2 }, // Description matches are 2x
58
- tags: { weight: 1 }, // Tags are baseline
59
- },
60
- });
57
+ interface ToolDoc {
58
+ id: string;
59
+ name: string;
60
+ description: string;
61
+ tags: string;
62
+ }
61
63
 
62
- db.addDocument('weather-tool', {
64
+ const db = new TFIDFVectoria<ToolDoc>();
65
+
66
+ const doc = {
67
+ id: 'weather-tool',
63
68
  name: 'get_weather',
64
69
  description: 'Fetch current weather conditions for a city',
65
70
  tags: 'weather forecast temperature',
66
- });
71
+ };
67
72
 
68
- db.buildIndex();
69
- const results = db.search('temperature forecast', 5);
73
+ // Concatenate fields into one searchable text blob
74
+ const text = `${doc.name} ${doc.description} ${doc.tags}`;
75
+ db.addDocument(doc.id, text, doc);
76
+
77
+ db.reindex();
78
+ const results = db.search('temperature forecast', { topK: 5 });
79
+ // results[0].metadata.name === 'get_weather'
70
80
  ```
71
81
 
72
82
  ### FrontMCP Provider Pattern
73
83
 
74
84
  ```typescript
75
- import { Provider, ProviderScope } from '@frontmcp/sdk';
76
85
  import { TFIDFVectoria } from 'vectoriadb';
77
86
 
78
- export const FAQSearch = Symbol('FAQSearch');
87
+ import { Provider, ProviderScope } from '@frontmcp/sdk';
88
+
89
+ interface FaqDoc {
90
+ id: string;
91
+ question: string;
92
+ answer: string;
93
+ tags: string;
94
+ }
79
95
 
80
- @Provider({ name: 'faq-search', provide: FAQSearch, scope: ProviderScope.GLOBAL })
96
+ @Provider({ name: 'faq-search', scope: ProviderScope.GLOBAL })
81
97
  export class FAQSearchProvider {
82
- private db = new TFIDFVectoria({
83
- fields: {
84
- question: { weight: 3 },
85
- answer: { weight: 1 },
86
- tags: { weight: 2 },
87
- },
88
- });
98
+ private db = new TFIDFVectoria<FaqDoc>();
89
99
 
90
- async initialize(faqs: Array<{ id: string; question: string; answer: string; tags: string }>) {
100
+ async initialize(faqs: FaqDoc[]) {
91
101
  for (const faq of faqs) {
92
- this.db.addDocument(faq.id, {
93
- question: faq.question,
94
- answer: faq.answer,
95
- tags: faq.tags,
96
- });
102
+ // Concatenate fields into a single searchable text; preserve fields in metadata
103
+ const text = `${faq.question} ${faq.answer} ${faq.tags}`;
104
+ this.db.addDocument(faq.id, text, faq);
97
105
  }
98
- this.db.buildIndex();
106
+ this.db.reindex();
99
107
  }
100
108
 
101
109
  search(query: string, limit = 5) {
102
- return this.db.search(query, limit);
110
+ return this.db.search(query, { topK: limit });
103
111
  }
104
112
  }
105
113
  ```
@@ -111,7 +119,7 @@ Uses transformer models for true semantic understanding. "find users" matches "l
111
119
  ### Basic Usage
112
120
 
113
121
  ```typescript
114
- import { VectoriaDB, DocumentMetadata } from 'vectoriadb';
122
+ import { DocumentMetadata, VectoriaDB } from 'vectoriadb';
115
123
 
116
124
  interface ProductDoc extends DocumentMetadata {
117
125
  name: string;
@@ -179,34 +187,33 @@ const results = await db.search('wireless audio', {
179
187
  ### Persistence with Storage Adapters
180
188
 
181
189
  ```typescript
182
- import { VectoriaDB, FileStorageAdapter } from 'vectoriadb';
190
+ import { FileStorageAdapter, VectoriaDB } from 'vectoriadb';
183
191
 
184
192
  const db = new VectoriaDB<MyDoc>({
185
193
  storageAdapter: new FileStorageAdapter({ cacheDir: './.cache/vectors' }),
186
194
  });
187
195
 
196
+ // initialize() automatically loads from cache if available and valid
188
197
  await db.initialize();
198
+
189
199
  // After adding documents, persist to disk
190
200
  await db.saveToStorage();
191
- // On next startup, restores without re-embedding
192
- await db.loadFromStorage();
201
+ // On next startup, calling initialize() again restores without re-embedding
193
202
  ```
194
203
 
195
204
  ### FrontMCP Provider Pattern
196
205
 
197
206
  ```typescript
198
- import { Provider, ProviderScope } from '@frontmcp/sdk';
199
- import { VectoriaDB, FileStorageAdapter } from 'vectoriadb';
200
- import type { DocumentMetadata } from 'vectoriadb';
207
+ import { FileStorageAdapter, VectoriaDB, type DocumentMetadata } from 'vectoriadb';
201
208
 
202
- export const KnowledgeBase = Symbol('KnowledgeBase');
209
+ import { Provider, ProviderScope } from '@frontmcp/sdk';
203
210
 
204
211
  interface Article extends DocumentMetadata {
205
212
  title: string;
206
213
  category: string;
207
214
  }
208
215
 
209
- @Provider({ name: 'knowledge-base', provide: KnowledgeBase, scope: ProviderScope.GLOBAL })
216
+ @Provider({ name: 'knowledge-base', scope: ProviderScope.GLOBAL })
210
217
  export class KnowledgeBaseProvider {
211
218
  private db: VectoriaDB<Article>;
212
219
  private ready: Promise<void>;
@@ -255,11 +262,14 @@ export class KnowledgeBaseProvider {
255
262
 
256
263
  ### TFIDFVectoria Options
257
264
 
258
- | Option | Type | Default | Description |
259
- | ---------------------------- | -------------------------- | ------- | ------------------------ |
260
- | `defaultSimilarityThreshold` | number | `0.0` | Minimum similarity score |
261
- | `defaultTopK` | number | `10` | Default results limit |
262
- | `fields` | Record<string, { weight }> | None | Field-weighted indexing |
265
+ | Option | Type | Default | Description |
266
+ | ---------------------------- | ------ | ------- | ------------------------ |
267
+ | `defaultSimilarityThreshold` | number | `0.0` | Minimum similarity score |
268
+ | `defaultTopK` | number | `10` | Default results limit |
269
+
270
+ > `TFIDFVectoria` indexes a single text string per document. To search across multiple
271
+ > conceptual fields, concatenate them when calling `addDocument(id, text, metadata)` and
272
+ > keep the originals in `metadata`.
263
273
 
264
274
  ## Choosing Between Engines
265
275
 
@@ -269,26 +279,26 @@ export class KnowledgeBaseProvider {
269
279
  | **Initialization** | Synchronous, instant | Async, first-run model download |
270
280
  | **Search quality** | Keyword-based (exact/fuzzy) | Semantic (understands meaning) |
271
281
  | **Best for** | Tool discovery, FAQ, <10K docs | Knowledge base, recommendations, any size |
272
- | **Reindex needed** | Yes (`buildIndex()` after add) | No (auto-indexed on add) |
282
+ | **Reindex needed** | Yes (`reindex()` after add) | No (auto-indexed on add) |
273
283
  | **Persistence** | Not built-in | FileStorageAdapter |
274
284
 
275
285
  ## Verification Checklist
276
286
 
277
287
  - [ ] Correct engine chosen based on requirements (TFIDFVectoria vs VectoriaDB)
278
288
  - [ ] Provider wraps the database with proper initialization
279
- - [ ] `buildIndex()` called after adding documents (TFIDFVectoria only)
289
+ - [ ] `reindex()` called after adding/removing documents (TFIDFVectoria only)
280
290
  - [ ] `await db.initialize()` called before any operations (VectoriaDB only)
281
- - [ ] Field weights configured based on domain relevance
282
- - [ ] Storage adapter configured if persistence is needed
283
- - [ ] Search tool injects provider via `this.get(TOKEN)`
291
+ - [ ] For TFIDFVectoria, multi-field documents concatenated into one text string
292
+ - [ ] Storage adapter configured if persistence is needed (VectoriaDB only)
293
+ - [ ] Search tool injects provider via `this.get(ProviderClass)`
284
294
 
285
295
  ## Examples
286
296
 
287
- | Example | Level | Description |
288
- | ------------------------------------------------------------------------------------------------ | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
289
- | [`product-catalog-search`](../examples/vectoriadb/product-catalog-search.md) | Advanced | Shows advanced VectoriaDB usage with typed document metadata, batch operations, filtered search by multiple criteria, and batch indexing of a product catalog. |
290
- | [`semantic-search-with-persistence`](../examples/vectoriadb/semantic-search-with-persistence.md) | Intermediate | Shows how to use `VectoriaDB` for semantic search with transformer models, filtered search, and `FileStorageAdapter` for persistence across restarts. |
291
- | [`tfidf-keyword-search`](../examples/vectoriadb/tfidf-keyword-search.md) | Basic | Shows how to use `TFIDFVectoria` for zero-dependency keyword search in a FrontMCP provider, with field weights and index building. |
297
+ | Example | Level | Description |
298
+ | ------------------------------------------------------------------------------------------------ | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
299
+ | [`product-catalog-search`](../examples/vectoriadb/product-catalog-search.md) | Advanced | Shows advanced VectoriaDB usage with typed document metadata, batch operations, filtered search by multiple criteria, and batch indexing of a product catalog. |
300
+ | [`semantic-search-with-persistence`](../examples/vectoriadb/semantic-search-with-persistence.md) | Intermediate | Shows how to use `VectoriaDB` for semantic search with transformer models, filtered search, and `FileStorageAdapter` for persistence across restarts. |
301
+ | [`tfidf-keyword-search`](../examples/vectoriadb/tfidf-keyword-search.md) | Basic | Shows how to use `TFIDFVectoria` for zero-dependency keyword search in a FrontMCP provider. `TFIDFVectoria` indexes a single text string per document, so multi-field documents are concatenated into one searchable blob; the original fields are kept in `metadata` for use on search results. |
292
302
 
293
303
  > See all examples in [`examples/vectoriadb/`](../examples/vectoriadb/)
294
304
 
@@ -160,7 +160,7 @@ export class GetWeatherTool extends ToolContext {
160
160
  **Resource** (`create-resource`):
161
161
 
162
162
  ```typescript
163
- import { Resource, ResourceContext } from '@frontmcp/sdk';
163
+ import { ReadResourceResult, Resource, ResourceContext } from '@frontmcp/sdk';
164
164
 
165
165
  @Resource({
166
166
  uri: 'weather://cities',
@@ -169,8 +169,16 @@ import { Resource, ResourceContext } from '@frontmcp/sdk';
169
169
  mimeType: 'application/json',
170
170
  })
171
171
  export class CitiesResource extends ResourceContext {
172
- async read() {
173
- return JSON.stringify(['London', 'Tokyo', 'New York', 'Paris', 'Sydney']);
172
+ async execute(uri: string): Promise<ReadResourceResult> {
173
+ return {
174
+ contents: [
175
+ {
176
+ uri,
177
+ mimeType: 'application/json',
178
+ text: JSON.stringify(['London', 'Tokyo', 'New York', 'Paris', 'Sydney']),
179
+ },
180
+ ],
181
+ };
174
182
  }
175
183
  }
176
184
  ```
@@ -225,27 +233,32 @@ export default class TaskManagerServer {}
225
233
  **Provider for shared storage** (`create-provider`):
226
234
 
227
235
  ```typescript
228
- import type { Token } from '@frontmcp/di';
229
- import { Provider } from '@frontmcp/sdk';
230
-
231
- export interface TaskStore {
232
- create(task: Task): Promise<Task>;
233
- list(userId: string): Promise<Task[]>;
234
- update(id: string, data: Partial<Task>): Promise<Task>;
235
- delete(id: string): Promise<void>;
236
- }
237
-
238
- export const TASK_STORE: Token<TaskStore> = Symbol('TaskStore');
236
+ import { Provider, ProviderScope } from '@frontmcp/sdk';
239
237
 
240
- @Provider({ token: TASK_STORE })
241
- export class RedisTaskStoreProvider implements TaskStore {
242
- // Redis-backed implementation
238
+ @Provider({ name: 'task-store', scope: ProviderScope.GLOBAL })
239
+ export class TaskStoreProvider {
240
+ async create(task: Task): Promise<Task> {
241
+ /* Redis-backed implementation */
242
+ }
243
+ async list(userId: string): Promise<Task[]> {
244
+ /* ... */
245
+ }
246
+ async update(id: string, data: Partial<Task>): Promise<Task> {
247
+ /* ... */
248
+ }
249
+ async delete(id: string): Promise<void> {
250
+ /* ... */
251
+ }
243
252
  }
244
253
  ```
245
254
 
255
+ > Use the class itself as the DI token (`this.get(TaskStoreProvider)`). For factory-built singletons (e.g. when you need async setup before the class is constructed), use `AsyncProvider({ provide, name, scope, useFactory })` instead.
256
+
246
257
  **Tool with DI** (`create-tool` + `create-provider`):
247
258
 
248
259
  ```typescript
260
+ import { Tool, ToolContext, UnauthorizedError, z } from '@frontmcp/sdk';
261
+
249
262
  @Tool({
250
263
  name: 'create_task',
251
264
  description: 'Create a new task',
@@ -257,8 +270,10 @@ export class RedisTaskStoreProvider implements TaskStore {
257
270
  })
258
271
  export class CreateTaskTool extends ToolContext {
259
272
  async execute(input: { title: string; priority: string }) {
260
- const store = this.get(TASK_STORE);
261
- return store.create({ title: input.title, priority: input.priority, status: 'pending' });
273
+ const store = this.get(TaskStoreProvider);
274
+ const userId = this.auth?.user.sub;
275
+ if (!userId) this.fail(new UnauthorizedError('Authentication required'));
276
+ return store.create({ title: input.title, priority: input.priority, status: 'pending', userId });
262
277
  }
263
278
  }
264
279
  ```
@@ -325,21 +340,20 @@ export default class KnowledgeBaseServer {}
325
340
  },
326
341
  llm: {
327
342
  provider: 'anthropic',
328
- model: 'claude-sonnet-4-5',
343
+ model: 'claude-sonnet-4-6',
329
344
  apiKey: { env: 'ANTHROPIC_API_KEY' },
330
345
  maxTokens: 4096,
331
346
  }, // provider and model are client-configurable
347
+ execution: { maxIterations: 5 },
348
+ systemInstructions:
349
+ 'Search for relevant documents using search_docs, synthesize findings, and produce a structured summary with source attribution.',
332
350
  tools: [SearchDocsTool, IngestDocumentTool],
333
351
  })
334
- export class ResearcherAgent extends AgentContext {
335
- async execute(input: { topic: string; depth: string }) {
336
- return this.run(
337
- `Research "${input.topic}" at ${input.depth} depth. Search for relevant documents, synthesize findings, and provide a structured summary.`,
338
- );
339
- }
340
- }
352
+ export class ResearcherAgent extends AgentContext {}
341
353
  ```
342
354
 
355
+ > The framework drives the LLM tool-use loop via the `agents:call-agent` flow — you don't override `execute()`. Configure iteration limits and other runtime knobs through the `@Agent({ execution: { maxIterations } })` block.
356
+ >
343
357
  > **Full working code:** See `references/example-knowledge-base.md`
344
358
 
345
359
  ---
@@ -413,8 +427,51 @@ export class ResearcherAgent extends AgentContext {
413
427
  | Tests pass but coverage below 95% | Missing error path or branch tests | Run `jest --coverage` and add tests for uncovered lines |
414
428
  | Provider state leaking between requests | Using module-level state instead of DI | Move state into a `@Provider` scoped per request |
415
429
 
430
+ ## Examples
431
+
432
+ Each reference has matching examples under [`examples/<reference>/`](./examples/):
433
+
434
+ ### `example-knowledge-base`
435
+
436
+ | Example | Level | Description |
437
+ | ------------------------------------------------------------------------------------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
438
+ | [`agent-and-plugin`](./examples/example-knowledge-base/agent-and-plugin.md) | Advanced | Shows an autonomous research agent with inner tools and configurable depth, and a plugin that hooks into tool execution for audit logging. |
439
+ | [`multi-app-composition`](./examples/example-knowledge-base/multi-app-composition.md) | Basic | Shows how to compose multiple apps (Ingestion, Search, Research) into a single server with shared providers, plugins, and agent registration. |
440
+ | [`vector-search-and-resources`](./examples/example-knowledge-base/vector-search-and-resources.md) | Intermediate | Shows a semantic search tool with embedding generation and a resource template for retrieving documents by ID using URI parameters. |
441
+
442
+ ### `example-task-manager`
443
+
444
+ | Example | Level | Description |
445
+ | --------------------------------------------------------------------------------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
446
+ | [`auth-and-crud-tools`](./examples/example-task-manager/auth-and-crud-tools.md) | Basic | Shows how to create CRUD tools with authentication, using `this.context.session` for user isolation and `this.get()` for dependency injection. |
447
+ | [`authenticated-e2e-tests`](./examples/example-task-manager/authenticated-e2e-tests.md) | Advanced | Shows how to write E2E tests with authentication using `TestTokenFactory`, and unit tests for tools that require session context. |
448
+ | [`redis-provider-with-di`](./examples/example-task-manager/redis-provider-with-di.md) | Intermediate | Shows how to create a Redis-backed provider with a DI token, lifecycle hooks (`onInit`/`onDestroy`), and how tools inject it. |
449
+
450
+ ### `example-weather-api`
451
+
452
+ | Example | Level | Description |
453
+ | ------------------------------------------------------------------------------------------ | ------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
454
+ | [`server-and-app-setup`](./examples/example-weather-api/server-and-app-setup.md) | Basic | Shows the server entry point, app registration, and static resource for a beginner FrontMCP weather API server. |
455
+ | [`unit-and-e2e-tests`](./examples/example-weather-api/unit-and-e2e-tests.md) | Intermediate | Shows how to write unit tests for tools by mocking context methods, and E2E tests using `McpTestClient` and `TestServer`. |
456
+ | [`weather-tool-with-schemas`](./examples/example-weather-api/weather-tool-with-schemas.md) | Basic | Shows how to create a tool with Zod input and output schemas, use `this.fetch()` for HTTP calls, and handle errors with `this.fail()`. |
457
+
458
+ ## Accessing This Skill
459
+
460
+ Skills are distributed as plain SKILL.md files plus a sibling `references/`
461
+ and `examples/` tree, so consumers can pick whichever access mode fits:
462
+
463
+ | Mode | How it works |
464
+ | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
465
+ | **Filesystem** | Read `libs/skills/catalog/frontmcp-guides/` directly from a clone of the catalog repo, or from a published `@frontmcp/skills` install. SKILL.md is the entry point. |
466
+ | **`frontmcp` CLI** | `frontmcp skills list`, `frontmcp skills read frontmcp-guides`, `frontmcp skills read frontmcp-guides:references/<file>.md`, `frontmcp skills install frontmcp-guides` — no server required. |
467
+ | **MCP `skill://`** | When a developer mounts this skill into their own FrontMCP server (`@FrontMcp({ skills: [...] })`), the SDK exposes it via SEP-2640 resources: `skill://frontmcp-guides/SKILL.md`, `skill://frontmcp-guides/references/{file}.md`, etc. The server’s `skill://index.json` returns the SEP-2640 discovery document for everything mounted on it. |
468
+
469
+ The catalog itself is **not** an MCP server. The `skill://` URIs only resolve
470
+ when a server has been configured to host this skill.
471
+
416
472
  ## Reference
417
473
 
418
474
  - [Your First Tool](https://docs.agentfront.dev/frontmcp/guides/your-first-tool)
419
475
  - Domain routers: `frontmcp-development`, `frontmcp-deployment`, `frontmcp-testing`, `frontmcp-config`
420
476
  - Core skills: `setup-project`, `create-tool`, `create-resource`, `create-provider`, `create-agent`, `configure-auth`, `setup-testing`
477
+ - Mandatory boundaries: import MCP protocol types and `McpError` from `@frontmcp/protocol` (never directly from `@modelcontextprotocol/sdk`); use `@frontmcp/utils` for crypto and file-system operations.
@@ -2,20 +2,26 @@
2
2
  name: agent-and-plugin
3
3
  reference: example-knowledge-base
4
4
  level: advanced
5
- description: 'Shows an autonomous research agent with inner tools and configurable depth, and a plugin that hooks into tool execution for audit logging.'
6
- tags: [guides, knowledge-base, knowledge, base, agent, plugin]
5
+ description: Shows an autonomous research agent with inner tools and a real `ToolHook`-based plugin that hooks into the `tools:call-tool` flow for audit logging.
6
+ tags:
7
+ - guides
8
+ - knowledge-base
9
+ - knowledge
10
+ - base
11
+ - agent
12
+ - plugin
7
13
  features:
8
- - 'Agent with `@Agent` decorator, LLM config, inner tools, and system instructions'
9
- - 'Using `this.run(prompt, { maxIterations })` to execute the LLM tool-use loop'
10
- - "Configurable behavior via input schema (`depth: 'shallow' | 'deep'`)"
11
- - 'Plugin hooks: `onToolExecuteBefore`, `onToolExecuteAfter`, `onToolExecuteError`'
12
- - 'Using `ctx.state.set/get()` for flow state instead of mutating `rawInput`'
13
- - 'Non-blocking audit logging (`.catch()` prevents audit failures from breaking tools)'
14
+ - Agent with `@Agent` decorator, LLM config, inner tools, and system instructions
15
+ - 'Configuring the inner-loop limit via `@Agent({ execution: { maxIterations } })` (framework drives iteration; no `this.run(...)`)'
16
+ - 'Plugin built on real `ToolHook` decorators: `@ToolHook.Will/Did/Around("execute")`'
17
+ - Using `flowCtx.state.set/get()` for hook-local state
18
+ - Using `flowCtx.state.required.toolContext` to read tool metadata and authInfo inside hooks
19
+ - Non-blocking audit logging (`.catch()` prevents audit failures from breaking tools)
14
20
  ---
15
21
 
16
22
  # Knowledge Base: Research Agent and Audit Log Plugin
17
23
 
18
- Shows an autonomous research agent with inner tools and configurable depth, and a plugin that hooks into tool execution for audit logging.
24
+ Shows an autonomous research agent with inner tools and a real `ToolHook`-based plugin that hooks into the `tools:call-tool` flow for audit logging.
19
25
 
20
26
  ## Code
21
27
 
@@ -47,45 +53,38 @@ import { SearchDocsTool } from '../../search/tools/search-docs.tool';
47
53
  },
48
54
  llm: {
49
55
  provider: 'anthropic',
50
- model: 'claude-sonnet-4-20250514',
56
+ model: 'claude-sonnet-4-6',
51
57
  apiKey: { env: 'ANTHROPIC_API_KEY' },
52
58
  maxTokens: 4096,
53
59
  },
54
- // Inner tools: the agent can call these during its execution
60
+ // Cap the inner tool-use loop. The framework not your code — drives iteration.
61
+ execution: { maxIterations: 5 },
62
+ // Inner tools: the agent can call these during its execution.
55
63
  tools: [SearchDocsTool, IngestDocumentTool],
56
64
  systemInstructions: `You are a research assistant with access to a knowledge base.
57
65
  Your job is to:
58
66
  1. Search the knowledge base for relevant documents using the search_docs tool.
59
67
  2. Analyze the results and identify key themes.
60
- 3. If depth is "deep", perform multiple searches with refined queries.
68
+ 3. When depth is "deep", perform multiple searches with refined queries; for "shallow", a single search is enough.
61
69
  4. Synthesize findings into a structured summary with source attribution.
62
- Always cite which documents support your findings.`,
70
+ Always cite which documents support your findings, and return JSON matching the output schema.`,
63
71
  })
64
- export class ResearcherAgent extends AgentContext {
65
- async execute(input: { topic: string; depth: 'shallow' | 'deep' }) {
66
- const maxIterations = input.depth === 'deep' ? 5 : 2;
67
- const prompt = [
68
- `Research the following topic: "${input.topic}"`,
69
- `Depth: ${input.depth} (max ${maxIterations} search iterations)`,
70
- 'Search the knowledge base, analyze results, and produce a structured summary.',
71
- 'Return your findings as JSON matching the output schema.',
72
- ].join('\n');
73
-
74
- // this.run() executes the LLM loop with inner tools
75
- return this.run(prompt, { maxIterations });
76
- }
77
- }
72
+ export class ResearcherAgent extends AgentContext {}
78
73
  ```
79
74
 
80
75
  ```typescript
81
76
  // src/plugins/audit-log.plugin.ts
82
- import { Plugin, type PluginHookContext } from '@frontmcp/sdk';
77
+ import { DynamicPlugin, FlowCtxOf, Plugin, ToolHook } from '@frontmcp/sdk';
78
+
79
+ export interface AuditLogPluginOptions {
80
+ endpoint?: string;
81
+ }
83
82
 
84
83
  @Plugin({
85
- name: 'AuditLog',
84
+ name: 'audit-log',
86
85
  description: 'Logs all tool invocations for audit compliance',
87
86
  })
88
- export class AuditLogPlugin {
87
+ export default class AuditLogPlugin extends DynamicPlugin<AuditLogPluginOptions> {
89
88
  private readonly logs: Array<{
90
89
  timestamp: string;
91
90
  tool: string;
@@ -94,52 +93,63 @@ export class AuditLogPlugin {
94
93
  success: boolean;
95
94
  }> = [];
96
95
 
97
- async onToolExecuteBefore(ctx: PluginHookContext): Promise<void> {
98
- // Store start time in flow state (not in rawInput)
99
- ctx.state.set('audit:startTime', Date.now());
96
+ constructor(protected options: AuditLogPluginOptions = {}) {
97
+ super();
100
98
  }
101
99
 
102
- async onToolExecuteAfter(ctx: PluginHookContext): Promise<void> {
103
- const startTime = ctx.state.get('audit:startTime') as number;
104
- const duration = Date.now() - startTime;
100
+ // Will('execute') runs immediately before the tool's execute() record start time on the flow state.
101
+ @ToolHook.Will('execute', { priority: 100 })
102
+ async onWillExecute(flowCtx: FlowCtxOf<'tools:call-tool'>): Promise<void> {
103
+ flowCtx.state.set('audit:startTime', Date.now());
104
+ }
105
+
106
+ // Did('execute') runs after a successful execute() — compute duration and log success.
107
+ @ToolHook.Did('execute', { priority: 100 })
108
+ async onDidExecute(flowCtx: FlowCtxOf<'tools:call-tool'>): Promise<void> {
109
+ const startTime = flowCtx.state.get('audit:startTime') as number | undefined;
110
+ const ctx = flowCtx.state.required.toolContext;
105
111
 
106
112
  const entry = {
107
113
  timestamp: new Date().toISOString(),
108
- tool: ctx.toolName,
109
- userId: ctx.session?.userId,
110
- duration,
114
+ tool: ctx.metadata.name,
115
+ userId: (ctx.authInfo as any)?.user?.sub as string | undefined,
116
+ duration: startTime ? Date.now() - startTime : 0,
111
117
  success: true,
112
118
  };
113
119
  this.logs.push(entry);
114
120
 
115
- // In production, send to an external logging service
116
- if (process.env.AUDIT_LOG_ENDPOINT) {
117
- await ctx
118
- .fetch(process.env.AUDIT_LOG_ENDPOINT, {
121
+ if (this.options.endpoint) {
122
+ // Audit logging should never block tool execution — fire-and-forget.
123
+ void ctx
124
+ .fetch(this.options.endpoint, {
119
125
  method: 'POST',
120
126
  headers: { 'Content-Type': 'application/json' },
121
127
  body: JSON.stringify(entry),
122
128
  })
123
- .catch(() => {
124
- // Audit logging should not block tool execution
125
- });
129
+ .catch(() => undefined);
126
130
  }
127
131
  }
128
132
 
129
- async onToolExecuteError(ctx: PluginHookContext): Promise<void> {
130
- const startTime = ctx.state.get('audit:startTime') as number;
131
- const duration = Date.now() - startTime;
132
-
133
- this.logs.push({
134
- timestamp: new Date().toISOString(),
135
- tool: ctx.toolName,
136
- userId: ctx.session?.userId,
137
- duration,
138
- success: false,
139
- });
133
+ // Around('execute') wraps the call so we can capture errors as well.
134
+ @ToolHook.Around('execute', { priority: 100 })
135
+ async aroundExecute(flowCtx: FlowCtxOf<'tools:call-tool'>, next: () => Promise<unknown>): Promise<unknown> {
136
+ try {
137
+ return await next();
138
+ } catch (err) {
139
+ const startTime = flowCtx.state.get('audit:startTime') as number | undefined;
140
+ const ctx = flowCtx.state.required.toolContext;
141
+ this.logs.push({
142
+ timestamp: new Date().toISOString(),
143
+ tool: ctx.metadata.name,
144
+ userId: (ctx.authInfo as any)?.user?.sub as string | undefined,
145
+ duration: startTime ? Date.now() - startTime : 0,
146
+ success: false,
147
+ });
148
+ throw err;
149
+ }
140
150
  }
141
151
 
142
- getLogs(): typeof this.logs {
152
+ getLogs(): ReadonlyArray<(typeof this.logs)[number]> {
143
153
  return [...this.logs];
144
154
  }
145
155
  }
@@ -148,10 +158,10 @@ export class AuditLogPlugin {
148
158
  ## What This Demonstrates
149
159
 
150
160
  - Agent with `@Agent` decorator, LLM config, inner tools, and system instructions
151
- - Using `this.run(prompt, { maxIterations })` to execute the LLM tool-use loop
152
- - Configurable behavior via input schema (`depth: 'shallow' | 'deep'`)
153
- - Plugin hooks: `onToolExecuteBefore`, `onToolExecuteAfter`, `onToolExecuteError`
154
- - Using `ctx.state.set/get()` for flow state instead of mutating `rawInput`
161
+ - Configuring the inner-loop limit via `@Agent({ execution: { maxIterations } })` (framework drives iteration; no `this.run(...)`)
162
+ - Plugin built on real `ToolHook` decorators: `@ToolHook.Will/Did/Around("execute")`
163
+ - Using `flowCtx.state.set/get()` for hook-local state
164
+ - Using `flowCtx.state.required.toolContext` to read tool metadata and authInfo inside hooks
155
165
  - Non-blocking audit logging (`.catch()` prevents audit failures from breaking tools)
156
166
 
157
167
  ## Related