@frontmcp/skills 1.1.2-beta.1 → 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.
- package/catalog/TEMPLATE.md +16 -11
- package/catalog/frontmcp-authorities/SKILL.md +116 -11
- package/catalog/frontmcp-authorities/references/authority-profiles.md +39 -36
- package/catalog/frontmcp-authorities/references/claims-mapping.md +7 -0
- package/catalog/frontmcp-authorities/references/custom-evaluators.md +63 -14
- package/catalog/frontmcp-channels/SKILL.md +36 -0
- package/catalog/frontmcp-channels/examples/channel-sources/file-watcher.md +8 -2
- package/catalog/frontmcp-channels/examples/channel-sources/replay-buffer.md +111 -30
- package/catalog/frontmcp-channels/examples/channel-two-way/whatsapp-bridge.md +45 -3
- package/catalog/frontmcp-channels/references/channel-sources.md +11 -3
- package/catalog/frontmcp-channels/references/channel-two-way.md +60 -89
- package/catalog/frontmcp-config/SKILL.md +111 -8
- package/catalog/frontmcp-config/examples/configure-auth-modes/local-self-signed-tokens.md +4 -4
- package/catalog/frontmcp-config/examples/configure-auth-modes/remote-enterprise-oauth.md +7 -1
- package/catalog/frontmcp-config/examples/configure-deployment-targets/distributed-ha-config.md +1 -1
- package/catalog/frontmcp-config/examples/configure-deployment-targets/json-schema-ide-support.md +1 -1
- package/catalog/frontmcp-config/examples/configure-deployment-targets/multi-target-with-security.md +12 -9
- package/catalog/frontmcp-config/examples/configure-http/cors-restricted-origins.md +2 -2
- package/catalog/frontmcp-config/examples/configure-http/entry-path-reverse-proxy.md +1 -1
- package/catalog/frontmcp-config/examples/configure-security-headers/csp-report-only.md +1 -1
- package/catalog/frontmcp-config/examples/configure-security-headers/full-production-headers.md +1 -1
- package/catalog/frontmcp-config/examples/configure-skills-http/audit-log-basic.md +76 -0
- package/catalog/frontmcp-config/examples/configure-skills-http/audit-log-redis.md +116 -0
- package/catalog/frontmcp-config/examples/configure-skills-http/inject-instructions.md +59 -0
- package/catalog/frontmcp-config/references/configure-auth-modes.md +5 -5
- package/catalog/frontmcp-config/references/configure-deployment-targets.md +27 -24
- package/catalog/frontmcp-config/references/configure-http.md +14 -10
- package/catalog/frontmcp-config/references/configure-security-headers.md +2 -2
- package/catalog/frontmcp-config/references/configure-session.md +25 -25
- package/catalog/frontmcp-config/references/configure-skills-http.md +157 -0
- package/catalog/frontmcp-config/references/configure-throttle.md +1 -1
- package/catalog/frontmcp-config/references/configure-transport.md +2 -2
- package/catalog/frontmcp-deployment/SKILL.md +112 -9
- package/catalog/frontmcp-deployment/examples/build-for-browser/browser-build-with-custom-entry.md +23 -11
- package/catalog/frontmcp-deployment/examples/build-for-browser/browser-crypto-and-storage.md +44 -17
- package/catalog/frontmcp-deployment/examples/build-for-browser/react-provider-setup.md +53 -21
- package/catalog/frontmcp-deployment/examples/build-for-cli/cli-binary-build.md +1 -1
- package/catalog/frontmcp-deployment/examples/build-for-cli/unix-socket-daemon.md +1 -1
- package/catalog/frontmcp-deployment/examples/build-for-mcpb/mcpb-bundle-build.md +1 -1
- package/catalog/frontmcp-deployment/examples/build-for-sdk/connect-openai.md +1 -1
- package/catalog/frontmcp-deployment/examples/build-for-sdk/multi-platform-connect.md +1 -1
- package/catalog/frontmcp-deployment/examples/deploy-to-cloudflare/basic-worker-deploy.md +7 -8
- package/catalog/frontmcp-deployment/examples/deploy-to-cloudflare/worker-custom-domain.md +8 -6
- package/catalog/frontmcp-deployment/examples/deploy-to-cloudflare/worker-with-kv-storage.md +5 -4
- package/catalog/frontmcp-deployment/examples/deploy-to-lambda/cdk-deployment.md +8 -5
- package/catalog/frontmcp-deployment/examples/deploy-to-lambda/lambda-handler-with-cors.md +20 -18
- package/catalog/frontmcp-deployment/examples/deploy-to-lambda/sam-template-basic.md +8 -5
- package/catalog/frontmcp-deployment/examples/deploy-to-node/docker-compose-with-redis.md +3 -3
- package/catalog/frontmcp-deployment/examples/deploy-to-node/pm2-with-nginx.md +1 -1
- package/catalog/frontmcp-deployment/examples/deploy-to-node/resource-limits.md +2 -2
- package/catalog/frontmcp-deployment/examples/deploy-to-node-dockerfile/basic-multistage-dockerfile.md +2 -2
- package/catalog/frontmcp-deployment/examples/deploy-to-node-dockerfile/secure-nonroot-dockerfile.md +1 -1
- package/catalog/frontmcp-deployment/examples/deploy-to-vercel/vercel-mcp-endpoint-test.md +23 -21
- package/catalog/frontmcp-deployment/examples/deploy-to-vercel/vercel-with-kv.md +25 -22
- package/catalog/frontmcp-deployment/examples/deploy-to-vercel/vercel-with-skills-cache.md +23 -30
- package/catalog/frontmcp-deployment/examples/deploy-to-vercel-config/minimal-vercel-config.md +52 -28
- package/catalog/frontmcp-deployment/examples/deploy-to-vercel-config/vercel-config-with-security-headers.md +32 -55
- package/catalog/frontmcp-deployment/examples/mcp-client-integration/http-remote.md +9 -0
- package/catalog/frontmcp-deployment/references/build-for-browser.md +40 -17
- package/catalog/frontmcp-deployment/references/build-for-cli.md +8 -8
- package/catalog/frontmcp-deployment/references/deploy-to-cloudflare.md +43 -24
- package/catalog/frontmcp-deployment/references/deploy-to-lambda.md +36 -25
- package/catalog/frontmcp-deployment/references/deploy-to-node-dockerfile.md +56 -14
- package/catalog/frontmcp-deployment/references/deploy-to-node.md +9 -6
- package/catalog/frontmcp-deployment/references/deploy-to-vercel-config.md +57 -58
- package/catalog/frontmcp-deployment/references/deploy-to-vercel.md +49 -59
- package/catalog/frontmcp-deployment/references/mcp-client-integration.md +2 -0
- package/catalog/frontmcp-development/SKILL.md +186 -11
- package/catalog/frontmcp-development/examples/create-agent/custom-multi-pass-agent.md +1 -1
- package/catalog/frontmcp-development/examples/create-agent/nested-agents-with-swarm.md +30 -27
- package/catalog/frontmcp-development/examples/create-job/job-with-permissions.md +13 -8
- package/catalog/frontmcp-development/examples/create-provider/basic-database-provider.md +33 -23
- package/catalog/frontmcp-development/examples/create-provider/config-and-api-providers.md +19 -10
- package/catalog/frontmcp-development/examples/create-tool/tool-with-rate-limiting-and-progress.md +3 -3
- package/catalog/frontmcp-development/examples/create-workflow/webhook-triggered-workflow.md +6 -4
- package/catalog/frontmcp-development/examples/decorators-guide/agent-skill-job-workflow.md +1 -1
- package/catalog/frontmcp-development/examples/decorators-guide/basic-server-with-app-and-tools.md +13 -8
- package/catalog/frontmcp-development/examples/decorators-guide/multi-app-with-plugins-and-providers.md +50 -23
- package/catalog/frontmcp-development/references/create-agent.md +47 -30
- package/catalog/frontmcp-development/references/create-job.md +69 -54
- package/catalog/frontmcp-development/references/create-plugin-hooks.md +45 -28
- package/catalog/frontmcp-development/references/create-plugin.md +10 -8
- package/catalog/frontmcp-development/references/create-prompt.md +3 -3
- package/catalog/frontmcp-development/references/create-provider.md +91 -51
- package/catalog/frontmcp-development/references/create-resource.md +3 -3
- package/catalog/frontmcp-development/references/create-skill.md +2 -2
- package/catalog/frontmcp-development/references/create-tool.md +7 -7
- package/catalog/frontmcp-development/references/create-workflow.md +8 -10
- package/catalog/frontmcp-development/references/decorators-guide.md +92 -56
- package/catalog/frontmcp-development/references/official-plugins.md +4 -3
- package/catalog/frontmcp-development/references/openapi-adapter.md +1 -1
- package/catalog/frontmcp-extensibility/SKILL.md +70 -10
- package/catalog/frontmcp-extensibility/examples/skill-audit-log/custom-store.md +197 -0
- package/catalog/frontmcp-extensibility/examples/skill-audit-log/verify-chain.md +68 -0
- package/catalog/frontmcp-extensibility/examples/vectoriadb/product-catalog-search.md +3 -5
- package/catalog/frontmcp-extensibility/examples/vectoriadb/semantic-search-with-persistence.md +4 -11
- package/catalog/frontmcp-extensibility/examples/vectoriadb/tfidf-keyword-search.md +41 -30
- package/catalog/frontmcp-extensibility/references/skill-audit-log.md +233 -0
- package/catalog/frontmcp-extensibility/references/vectoriadb.md +73 -63
- package/catalog/frontmcp-guides/SKILL.md +84 -27
- package/catalog/frontmcp-guides/examples/example-knowledge-base/agent-and-plugin.md +72 -62
- package/catalog/frontmcp-guides/examples/example-knowledge-base/vector-search-and-resources.md +32 -43
- package/catalog/frontmcp-guides/examples/example-task-manager/auth-and-crud-tools.md +24 -17
- package/catalog/frontmcp-guides/examples/example-task-manager/authenticated-e2e-tests.md +23 -21
- package/catalog/frontmcp-guides/examples/example-task-manager/redis-provider-with-di.md +47 -39
- package/catalog/frontmcp-guides/examples/example-weather-api/server-and-app-setup.md +16 -6
- package/catalog/frontmcp-guides/examples/example-weather-api/unit-and-e2e-tests.md +9 -8
- package/catalog/frontmcp-guides/references/example-knowledge-base.md +192 -265
- package/catalog/frontmcp-guides/references/example-task-manager.md +60 -54
- package/catalog/frontmcp-guides/references/example-weather-api.md +22 -24
- package/catalog/frontmcp-observability/SKILL.md +66 -2
- package/catalog/frontmcp-observability/examples/telemetry-api/skill-counters.md +100 -0
- package/catalog/frontmcp-observability/examples/tracing-setup/production-tracing.md +7 -2
- package/catalog/frontmcp-observability/examples/vendor-integrations/coralogix-setup.md +6 -2
- package/catalog/frontmcp-observability/references/telemetry-api.md +72 -8
- package/catalog/frontmcp-observability/references/testing-observability.md +33 -49
- package/catalog/frontmcp-observability/references/tracing-setup.md +12 -5
- package/catalog/frontmcp-observability/references/vendor-integrations.md +46 -1
- package/catalog/frontmcp-production-readiness/SKILL.md +134 -3
- package/catalog/frontmcp-production-readiness/examples/common-checklist/caching-and-performance.md +57 -36
- package/catalog/frontmcp-production-readiness/examples/common-checklist/observability-setup.md +1 -1
- package/catalog/frontmcp-production-readiness/examples/common-checklist/security-hardening.md +102 -6
- package/catalog/frontmcp-production-readiness/examples/production-cli-daemon/daemon-socket-config.md +2 -1
- package/catalog/frontmcp-production-readiness/examples/production-cli-daemon/graceful-shutdown-cleanup.md +66 -58
- package/catalog/frontmcp-production-readiness/examples/production-cli-daemon/security-and-permissions.md +5 -3
- package/catalog/frontmcp-production-readiness/examples/production-cloudflare/durable-objects-state.md +2 -1
- package/catalog/frontmcp-production-readiness/examples/production-cloudflare/wrangler-config.md +55 -76
- package/catalog/frontmcp-production-readiness/examples/production-lambda/cold-start-connection-reuse.md +43 -40
- package/catalog/frontmcp-production-readiness/examples/production-lambda/sam-template.md +63 -94
- package/catalog/frontmcp-production-readiness/examples/production-lambda/scaling-and-monitoring.md +28 -18
- package/catalog/frontmcp-production-readiness/examples/production-node-sdk/multi-instance-cleanup.md +29 -14
- package/catalog/frontmcp-production-readiness/examples/production-node-server/graceful-shutdown.md +58 -42
- package/catalog/frontmcp-production-readiness/examples/production-node-server/redis-session-scaling.md +5 -2
- package/catalog/frontmcp-production-readiness/examples/production-vercel/cold-start-optimization.md +41 -24
- package/catalog/frontmcp-production-readiness/examples/production-vercel/vercel-edge-config.md +56 -65
- package/catalog/frontmcp-production-readiness/references/common-checklist.md +17 -5
- package/catalog/frontmcp-production-readiness/references/production-cli-daemon.md +5 -5
- package/catalog/frontmcp-production-readiness/references/production-cloudflare.md +5 -5
- package/catalog/frontmcp-production-readiness/references/production-lambda.md +5 -5
- package/catalog/frontmcp-production-readiness/references/production-node-sdk.md +5 -5
- package/catalog/frontmcp-production-readiness/references/production-node-server.md +1 -1
- package/catalog/frontmcp-production-readiness/references/production-vercel.md +5 -5
- package/catalog/frontmcp-setup/SKILL.md +88 -0
- package/catalog/frontmcp-setup/examples/project-structure-nx/nx-workspace-with-apps.md +10 -4
- package/catalog/frontmcp-setup/examples/project-structure-standalone/dev-workflow-commands.md +21 -8
- package/catalog/frontmcp-setup/examples/readme-guide/node-server-readme.md +3 -3
- package/catalog/frontmcp-setup/references/multi-app-composition.md +4 -3
- package/catalog/frontmcp-setup/references/project-structure-nx.md +15 -6
- package/catalog/frontmcp-setup/references/project-structure-standalone.md +18 -15
- package/catalog/frontmcp-setup/references/readme-guide.md +1 -1
- package/catalog/frontmcp-setup/references/setup-project.md +19 -5
- package/catalog/frontmcp-setup/references/setup-redis.md +27 -39
- package/catalog/frontmcp-setup/references/setup-sqlite.md +25 -18
- package/catalog/frontmcp-testing/SKILL.md +102 -15
- package/catalog/frontmcp-testing/examples/setup-testing/unit-test-tool-resource-prompt.md +3 -3
- package/catalog/frontmcp-testing/examples/test-auth/oauth-flow-test.md +50 -39
- package/catalog/frontmcp-testing/examples/test-auth/role-based-access-test.md +52 -29
- package/catalog/frontmcp-testing/examples/test-auth/token-factory-test.md +37 -20
- package/catalog/frontmcp-testing/examples/test-direct-client/basic-create-test.md +25 -15
- package/catalog/frontmcp-testing/examples/test-direct-client/openai-claude-format-test.md +27 -21
- package/catalog/frontmcp-testing/examples/test-e2e-handler/basic-e2e-test.md +29 -20
- package/catalog/frontmcp-testing/examples/test-e2e-handler/manual-client-with-transport.md +5 -3
- package/catalog/frontmcp-testing/examples/test-e2e-handler/tool-call-and-error-e2e.md +35 -26
- package/catalog/frontmcp-testing/examples/test-tool-unit/basic-tool-test.md +8 -3
- package/catalog/frontmcp-testing/examples/test-tool-unit/schema-validation-test.md +4 -1
- package/catalog/frontmcp-testing/examples/test-tool-unit/tool-error-handling-test.md +6 -3
- package/catalog/frontmcp-testing/references/setup-testing.md +35 -39
- package/catalog/frontmcp-testing/references/test-auth.md +86 -43
- package/catalog/frontmcp-testing/references/test-browser-build.md +1 -1
- package/catalog/frontmcp-testing/references/test-direct-client.md +29 -19
- package/catalog/frontmcp-testing/references/test-e2e-handler.md +31 -19
- package/catalog/frontmcp-testing/references/test-tool-unit.md +6 -2
- package/catalog/skills-manifest.json +428 -339
- package/package.json +1 -1
- package/src/manifest.d.ts +13 -0
- 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
|
-
//
|
|
42
|
-
db.
|
|
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
|
-
###
|
|
49
|
+
### Combining Multiple Text Fields
|
|
50
50
|
|
|
51
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
});
|
|
57
|
+
interface ToolDoc {
|
|
58
|
+
id: string;
|
|
59
|
+
name: string;
|
|
60
|
+
description: string;
|
|
61
|
+
tags: string;
|
|
62
|
+
}
|
|
61
63
|
|
|
62
|
-
db
|
|
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
|
-
|
|
69
|
-
const
|
|
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
|
-
|
|
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',
|
|
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:
|
|
100
|
+
async initialize(faqs: FaqDoc[]) {
|
|
91
101
|
for (const faq of faqs) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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.
|
|
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 {
|
|
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 {
|
|
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 {
|
|
199
|
-
import { VectoriaDB, FileStorageAdapter } from 'vectoriadb';
|
|
200
|
-
import type { DocumentMetadata } from 'vectoriadb';
|
|
207
|
+
import { FileStorageAdapter, VectoriaDB, type DocumentMetadata } from 'vectoriadb';
|
|
201
208
|
|
|
202
|
-
|
|
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',
|
|
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
|
|
259
|
-
| ---------------------------- |
|
|
260
|
-
| `defaultSimilarityThreshold` | number
|
|
261
|
-
| `defaultTopK` | number
|
|
262
|
-
|
|
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 (`
|
|
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
|
-
- [ ] `
|
|
289
|
+
- [ ] `reindex()` called after adding/removing documents (TFIDFVectoria only)
|
|
280
290
|
- [ ] `await db.initialize()` called before any operations (VectoriaDB only)
|
|
281
|
-
- [ ]
|
|
282
|
-
- [ ] Storage adapter configured if persistence is needed
|
|
283
|
-
- [ ] Search tool injects provider via `this.get(
|
|
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,
|
|
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
|
|
173
|
-
return
|
|
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
|
|
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({
|
|
241
|
-
export class
|
|
242
|
-
|
|
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(
|
|
261
|
-
|
|
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-
|
|
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:
|
|
6
|
-
tags:
|
|
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
|
-
-
|
|
9
|
-
- '
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
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
|
|
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-
|
|
56
|
+
model: 'claude-sonnet-4-6',
|
|
51
57
|
apiKey: { env: 'ANTHROPIC_API_KEY' },
|
|
52
58
|
maxTokens: 4096,
|
|
53
59
|
},
|
|
54
|
-
//
|
|
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.
|
|
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,
|
|
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: '
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
ctx.state.set('audit:startTime', Date.now());
|
|
96
|
+
constructor(protected options: AuditLogPluginOptions = {}) {
|
|
97
|
+
super();
|
|
100
98
|
}
|
|
101
99
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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.
|
|
109
|
-
userId: ctx.
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
.fetch(
|
|
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
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
-
|
|
152
|
-
-
|
|
153
|
-
-
|
|
154
|
-
- Using `
|
|
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
|