@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.
- 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
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: verify-chain
|
|
3
|
+
reference: skill-audit-log
|
|
4
|
+
level: intermediate
|
|
5
|
+
description: Verify a stored chain offline using verifyChain and the bundle-signing key registry.
|
|
6
|
+
tags: [extensibility, audit, verification, chain, rs256]
|
|
7
|
+
features:
|
|
8
|
+
- 'verifyChain returns { ok, breakAt?, reason? } and exits with the first detected break'
|
|
9
|
+
- 'defaultAuditSignatureVerifier dispatches on record.signatureAlg (HS256 or RS256)'
|
|
10
|
+
- 'Trusted-keys registry maps signatureKeyId → public key PEM'
|
|
11
|
+
- 'iterate() reads the chain in order from any SkillAuditStore implementation'
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Verify Audit Chain
|
|
15
|
+
|
|
16
|
+
Verify a stored chain offline using verifyChain and the bundle-signing key registry.
|
|
17
|
+
|
|
18
|
+
## Code
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
// scripts/verify-audit-chain.ts
|
|
22
|
+
import { defaultAuditSignatureVerifier, StorageAdapterAuditStore, verifyChain } from '@frontmcp/adapters/skills';
|
|
23
|
+
import { createStorageAdapter } from '@frontmcp/utils';
|
|
24
|
+
|
|
25
|
+
async function main() {
|
|
26
|
+
const storage = await createStorageAdapter({
|
|
27
|
+
provider: 'redis',
|
|
28
|
+
host: process.env.REDIS_HOST!,
|
|
29
|
+
port: 6379,
|
|
30
|
+
keyPrefix: 'mcp:skill-audit:',
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const store = new StorageAdapterAuditStore(storage);
|
|
34
|
+
const records = await store.iterate();
|
|
35
|
+
|
|
36
|
+
const trustedKeys: Record<string, string> = {
|
|
37
|
+
// Map every keyId you have ever rotated through, not just the current one.
|
|
38
|
+
'bundle-signing-2026-01': process.env.BUNDLE_SIGNING_PUBLIC_KEY_2026_01!,
|
|
39
|
+
'bundle-signing-2025-12': process.env.BUNDLE_SIGNING_PUBLIC_KEY_2025_12!,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const result = verifyChain(records, trustedKeys, defaultAuditSignatureVerifier);
|
|
43
|
+
|
|
44
|
+
if (!result.ok) {
|
|
45
|
+
console.error(`Chain broken at sequence ${result.breakAt}: ${result.reason}`);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
console.log(`Verified ${records.length} records, chain intact.`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
main().catch((err) => {
|
|
53
|
+
console.error(err);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## What This Demonstrates
|
|
59
|
+
|
|
60
|
+
- verifyChain returns { ok, breakAt?, reason? } and exits with the first detected break
|
|
61
|
+
- defaultAuditSignatureVerifier dispatches on record.signatureAlg (HS256 or RS256)
|
|
62
|
+
- Trusted-keys registry maps signatureKeyId → public key PEM
|
|
63
|
+
- iterate() reads the chain in order from any SkillAuditStore implementation
|
|
64
|
+
|
|
65
|
+
## Related
|
|
66
|
+
|
|
67
|
+
- See `skill-audit-log` for architecture and threat model
|
|
68
|
+
- See `custom-store` for streaming records to S3
|
|
@@ -25,8 +25,6 @@ import { FileStorageAdapter, VectoriaDB, type DocumentMetadata } from 'vectoriad
|
|
|
25
25
|
|
|
26
26
|
import { Provider, ProviderScope } from '@frontmcp/sdk';
|
|
27
27
|
|
|
28
|
-
export const ProductSearch = Symbol('ProductSearch');
|
|
29
|
-
|
|
30
28
|
// Typed metadata for product documents
|
|
31
29
|
interface ProductDoc extends DocumentMetadata {
|
|
32
30
|
name: string;
|
|
@@ -35,7 +33,7 @@ interface ProductDoc extends DocumentMetadata {
|
|
|
35
33
|
inStock: boolean;
|
|
36
34
|
}
|
|
37
35
|
|
|
38
|
-
@Provider({ name: 'product-search',
|
|
36
|
+
@Provider({ name: 'product-search', scope: ProviderScope.GLOBAL })
|
|
39
37
|
export class ProductSearchProvider {
|
|
40
38
|
private db: VectoriaDB<ProductDoc>;
|
|
41
39
|
private ready: Promise<void>;
|
|
@@ -106,7 +104,7 @@ export class ProductSearchProvider {
|
|
|
106
104
|
// src/tools/find-products.tool.ts
|
|
107
105
|
import { Tool, ToolContext, z } from '@frontmcp/sdk';
|
|
108
106
|
|
|
109
|
-
import {
|
|
107
|
+
import { ProductSearchProvider } from '../providers/product-search.provider';
|
|
110
108
|
|
|
111
109
|
@Tool({
|
|
112
110
|
name: 'find_products',
|
|
@@ -134,7 +132,7 @@ import { ProductSearch } from '../providers/product-search.provider';
|
|
|
134
132
|
})
|
|
135
133
|
export class FindProductsTool extends ToolContext {
|
|
136
134
|
async execute(input: { query: string; category?: string; maxPrice?: number; inStockOnly: boolean; limit: number }) {
|
|
137
|
-
const search = this.get(
|
|
135
|
+
const search = this.get(ProductSearchProvider);
|
|
138
136
|
|
|
139
137
|
const results = await search.search(
|
|
140
138
|
input.query,
|
package/catalog/frontmcp-extensibility/examples/vectoriadb/semantic-search-with-persistence.md
CHANGED
|
@@ -25,14 +25,12 @@ import { FileStorageAdapter, VectoriaDB, type DocumentMetadata } from 'vectoriad
|
|
|
25
25
|
|
|
26
26
|
import { Provider, ProviderScope } from '@frontmcp/sdk';
|
|
27
27
|
|
|
28
|
-
export const KnowledgeBase = Symbol('KnowledgeBase');
|
|
29
|
-
|
|
30
28
|
interface Article extends DocumentMetadata {
|
|
31
29
|
title: string;
|
|
32
30
|
category: string;
|
|
33
31
|
}
|
|
34
32
|
|
|
35
|
-
@Provider({ name: 'knowledge-base',
|
|
33
|
+
@Provider({ name: 'knowledge-base', scope: ProviderScope.GLOBAL })
|
|
36
34
|
export class KnowledgeBaseProvider {
|
|
37
35
|
private db: VectoriaDB<Article>;
|
|
38
36
|
private ready: Promise<void>;
|
|
@@ -67,14 +65,9 @@ export class KnowledgeBaseProvider {
|
|
|
67
65
|
} else {
|
|
68
66
|
await this.db.add(id, text, metadata);
|
|
69
67
|
}
|
|
70
|
-
// Persist to disk —
|
|
68
|
+
// Persist to disk — initialize() automatically restores from cache on next startup
|
|
71
69
|
await this.db.saveToStorage();
|
|
72
70
|
}
|
|
73
|
-
|
|
74
|
-
async loadFromDisk() {
|
|
75
|
-
await this.ready;
|
|
76
|
-
await this.db.loadFromStorage();
|
|
77
|
-
}
|
|
78
71
|
}
|
|
79
72
|
```
|
|
80
73
|
|
|
@@ -82,7 +75,7 @@ export class KnowledgeBaseProvider {
|
|
|
82
75
|
// src/tools/semantic-search.tool.ts
|
|
83
76
|
import { Tool, ToolContext, z } from '@frontmcp/sdk';
|
|
84
77
|
|
|
85
|
-
import {
|
|
78
|
+
import { KnowledgeBaseProvider } from '../providers/knowledge-base.provider';
|
|
86
79
|
|
|
87
80
|
@Tool({
|
|
88
81
|
name: 'semantic_search',
|
|
@@ -105,7 +98,7 @@ import { KnowledgeBase } from '../providers/knowledge-base.provider';
|
|
|
105
98
|
})
|
|
106
99
|
export class SemanticSearchTool extends ToolContext {
|
|
107
100
|
async execute(input: { query: string; category?: string; limit: number }) {
|
|
108
|
-
const kb = this.get(
|
|
101
|
+
const kb = this.get(KnowledgeBaseProvider);
|
|
109
102
|
|
|
110
103
|
const results = await kb.search(input.query, {
|
|
111
104
|
category: input.category,
|
|
@@ -2,19 +2,28 @@
|
|
|
2
2
|
name: tfidf-keyword-search
|
|
3
3
|
reference: vectoriadb
|
|
4
4
|
level: basic
|
|
5
|
-
description:
|
|
6
|
-
tags:
|
|
5
|
+
description: 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.
|
|
6
|
+
tags:
|
|
7
|
+
- extensibility
|
|
8
|
+
- vectoriadb
|
|
9
|
+
- keyword-search
|
|
10
|
+
- tfidf
|
|
11
|
+
- keyword
|
|
12
|
+
- search
|
|
7
13
|
features:
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
14
|
+
- Using `TFIDFVectoria` for zero-dependency keyword search (no model downloads)
|
|
15
|
+
- Calling `addDocument(id, text, metadata)` with a single concatenated text string
|
|
16
|
+
- Calling `reindex()` after adding documents (required for TFIDFVectoria)
|
|
17
|
+
- Wrapping the search engine in a FrontMCP provider with `ProviderScope.GLOBAL`
|
|
18
|
+
- Injecting the provider into tools via `this.get(FAQSearchProvider)`
|
|
13
19
|
---
|
|
14
20
|
|
|
15
21
|
# TFIDFVectoria: Lightweight Keyword Search Provider
|
|
16
22
|
|
|
17
|
-
Shows how to use `TFIDFVectoria` for zero-dependency keyword search in a FrontMCP
|
|
23
|
+
Shows how to use `TFIDFVectoria` for zero-dependency keyword search in a FrontMCP
|
|
24
|
+
provider. `TFIDFVectoria` indexes a single text string per document, so multi-field
|
|
25
|
+
documents are concatenated into one searchable blob; the original fields are kept
|
|
26
|
+
in `metadata` for use on search results.
|
|
18
27
|
|
|
19
28
|
## Code
|
|
20
29
|
|
|
@@ -24,32 +33,32 @@ import { TFIDFVectoria } from 'vectoriadb';
|
|
|
24
33
|
|
|
25
34
|
import { Provider, ProviderScope } from '@frontmcp/sdk';
|
|
26
35
|
|
|
27
|
-
|
|
36
|
+
interface FaqDoc {
|
|
37
|
+
id: string;
|
|
38
|
+
question: string;
|
|
39
|
+
answer: string;
|
|
40
|
+
tags: string;
|
|
41
|
+
}
|
|
28
42
|
|
|
29
|
-
@Provider({ name: 'faq-search',
|
|
43
|
+
@Provider({ name: 'faq-search', scope: ProviderScope.GLOBAL })
|
|
30
44
|
export class FAQSearchProvider {
|
|
31
|
-
private db = new TFIDFVectoria({
|
|
32
|
-
|
|
33
|
-
question: { weight: 3 }, // Question matches are 3x more important
|
|
34
|
-
answer: { weight: 1 }, // Answer matches are baseline
|
|
35
|
-
tags: { weight: 2 }, // Tag matches are 2x
|
|
36
|
-
},
|
|
45
|
+
private db = new TFIDFVectoria<FaqDoc>({
|
|
46
|
+
defaultTopK: 10,
|
|
37
47
|
});
|
|
38
48
|
|
|
39
|
-
async initialize(faqs:
|
|
49
|
+
async initialize(faqs: FaqDoc[]) {
|
|
40
50
|
for (const faq of faqs) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
});
|
|
51
|
+
// Concatenate fields into a single searchable text string;
|
|
52
|
+
// preserve the original fields in metadata.
|
|
53
|
+
const text = `${faq.question} ${faq.answer} ${faq.tags}`;
|
|
54
|
+
this.db.addDocument(faq.id, text, faq);
|
|
46
55
|
}
|
|
47
|
-
// Required after adding documents —
|
|
48
|
-
this.db.
|
|
56
|
+
// Required after adding documents — rebuilds IDF and embeddings
|
|
57
|
+
this.db.reindex();
|
|
49
58
|
}
|
|
50
59
|
|
|
51
60
|
search(query: string, limit = 5) {
|
|
52
|
-
return this.db.search(query, limit);
|
|
61
|
+
return this.db.search(query, { topK: limit });
|
|
53
62
|
}
|
|
54
63
|
}
|
|
55
64
|
```
|
|
@@ -58,7 +67,7 @@ export class FAQSearchProvider {
|
|
|
58
67
|
// src/tools/search-faq.tool.ts
|
|
59
68
|
import { Tool, ToolContext, z } from '@frontmcp/sdk';
|
|
60
69
|
|
|
61
|
-
import {
|
|
70
|
+
import { FAQSearchProvider } from '../providers/faq-search.provider';
|
|
62
71
|
|
|
63
72
|
@Tool({
|
|
64
73
|
name: 'search_faq',
|
|
@@ -72,19 +81,21 @@ import { FAQSearch } from '../providers/faq-search.provider';
|
|
|
72
81
|
z.object({
|
|
73
82
|
id: z.string(),
|
|
74
83
|
score: z.number(),
|
|
84
|
+
question: z.string(),
|
|
75
85
|
}),
|
|
76
86
|
),
|
|
77
87
|
},
|
|
78
88
|
})
|
|
79
89
|
export class SearchFaqTool extends ToolContext {
|
|
80
90
|
async execute(input: { query: string; limit: number }) {
|
|
81
|
-
const faqSearch = this.get(
|
|
91
|
+
const faqSearch = this.get(FAQSearchProvider);
|
|
82
92
|
const results = faqSearch.search(input.query, input.limit);
|
|
83
93
|
|
|
84
94
|
return {
|
|
85
95
|
results: results.map((r) => ({
|
|
86
96
|
id: r.id,
|
|
87
97
|
score: r.score,
|
|
98
|
+
question: r.metadata.question,
|
|
88
99
|
})),
|
|
89
100
|
};
|
|
90
101
|
}
|
|
@@ -94,10 +105,10 @@ export class SearchFaqTool extends ToolContext {
|
|
|
94
105
|
## What This Demonstrates
|
|
95
106
|
|
|
96
107
|
- Using `TFIDFVectoria` for zero-dependency keyword search (no model downloads)
|
|
97
|
-
-
|
|
98
|
-
- Calling `
|
|
108
|
+
- Calling `addDocument(id, text, metadata)` with a single concatenated text string
|
|
109
|
+
- Calling `reindex()` after adding documents (required for TFIDFVectoria)
|
|
99
110
|
- Wrapping the search engine in a FrontMCP provider with `ProviderScope.GLOBAL`
|
|
100
|
-
- Injecting the provider into tools via `this.get(
|
|
111
|
+
- Injecting the provider into tools via `this.get(FAQSearchProvider)`
|
|
101
112
|
|
|
102
113
|
## Related
|
|
103
114
|
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: skill-audit-log
|
|
3
|
+
description: Tamper-evident, hash-chained audit log for skill action executions — pluggable signer, pluggable store, offline verification.
|
|
4
|
+
tags: [extensibility, audit, skills, tamper-evident, signature, chain]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Skill Audit Log
|
|
8
|
+
|
|
9
|
+
The `@frontmcp/adapters/skills` module provides a tamper-evident, hash-chained audit log for skill action executions. Every authority pass / authority fail / HTTP success / HTTP failure phase emitted by `execute-action.tool.ts` is captured, signed, and chained so any later mutation breaks signature verification.
|
|
10
|
+
|
|
11
|
+
## Architecture
|
|
12
|
+
|
|
13
|
+
The writer exposes one method per phase rather than a generic `append`. Each
|
|
14
|
+
phase method assembles its payload internally and routes through a shared
|
|
15
|
+
chain pipeline:
|
|
16
|
+
|
|
17
|
+
```text
|
|
18
|
+
ExecuteActionTool.execute()
|
|
19
|
+
├── writer.writeAuthorityPass(ctx) // authority-check-pass
|
|
20
|
+
├── writer.writeAuthorityFail(ctx, { reason }) // authority-check-fail
|
|
21
|
+
├── writer.writeHttpCallSuccess(ctx, { status, output }) // http-call-success
|
|
22
|
+
└── writer.writeHttpCallFailure(ctx, { status, error }) // http-call-failure
|
|
23
|
+
└── shared pipeline:
|
|
24
|
+
├── store.tail() → previous record
|
|
25
|
+
├── store.nextSequence() → atomic monotonic counter
|
|
26
|
+
├── compute prevHash from previous record
|
|
27
|
+
├── assemble SkillAuditRecord { sequence, prevHash, phase, ... }
|
|
28
|
+
├── SkillAuditSigner.sign(record) → { signature, keyId, alg }
|
|
29
|
+
└── store.appendAtSequence(signedRecord)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Each `SkillAuditRecord` carries:
|
|
33
|
+
|
|
34
|
+
| Field | Description |
|
|
35
|
+
| ---------------- | ------------------------------------------------------------------------------------------------------- |
|
|
36
|
+
| `sequence` | Strictly increasing position in the chain |
|
|
37
|
+
| `prevHash` | SHA-256 hex of the previous record's canonical bytes (genesis sentinel `'0'.repeat(64)` for sequence 1) |
|
|
38
|
+
| `signature` | Base64url signature over the canonical record bytes |
|
|
39
|
+
| `signatureKeyId` | The signer key identifier — used by verifiers to look up the public key |
|
|
40
|
+
| `signatureAlg` | `'HS256'` or `'RS256'` |
|
|
41
|
+
| `phase` | `'authority-check-pass' \| 'authority-check-fail' \| 'http-call-success' \| 'http-call-failure'` |
|
|
42
|
+
| `skillId` | The skill that owns the action |
|
|
43
|
+
| `actionId` | The action that was executed |
|
|
44
|
+
| `subject` | Authenticated principal — redacted per `subjectMode` |
|
|
45
|
+
| `bundleVersion` | The bundle version active at the time of the call |
|
|
46
|
+
|
|
47
|
+
## Configuration
|
|
48
|
+
|
|
49
|
+
Wire the audit subsystem through `skillsConfig.audit` on `@FrontMcp`:
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
import {
|
|
53
|
+
Hs256AuditSigner,
|
|
54
|
+
MemoryAuditStore,
|
|
55
|
+
setSkillAuditFactory,
|
|
56
|
+
SkillAuditWriter,
|
|
57
|
+
SkillAuditWriterToken,
|
|
58
|
+
} from '@frontmcp/adapters/skills';
|
|
59
|
+
|
|
60
|
+
// Inject the audit module into the SDK at boot. The factory returns the
|
|
61
|
+
// module record; the SDK itself constructs SkillAuditWriter from
|
|
62
|
+
// (store, signer, logger, metrics?, options?) so that subjectMode and other
|
|
63
|
+
// options pass through skillsConfig.audit verbatim.
|
|
64
|
+
setSkillAuditFactory(() => ({
|
|
65
|
+
SkillAuditWriterToken,
|
|
66
|
+
SkillAuditWriter,
|
|
67
|
+
Hs256AuditSigner,
|
|
68
|
+
MemoryAuditStore,
|
|
69
|
+
}));
|
|
70
|
+
|
|
71
|
+
@FrontMcp({
|
|
72
|
+
info: { name: 'svr', version: '1.0.0' },
|
|
73
|
+
apps: [MainApp],
|
|
74
|
+
skillsConfig: {
|
|
75
|
+
enabled: true,
|
|
76
|
+
audit: {
|
|
77
|
+
enabled: true,
|
|
78
|
+
signer: new Hs256AuditSigner(secret, 'dev'),
|
|
79
|
+
store: new MemoryAuditStore(),
|
|
80
|
+
subjectMode: 'hash', // 'plain' | 'hash' | 'omit'
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
})
|
|
84
|
+
class Server {}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`setSkillAuditFactory(...)` injects the audit module into the SDK at boot. The SDK does **not** statically depend on `@frontmcp/adapters/skills` — this keeps the static dependency graph clean and works in Edge / CSP runtimes.
|
|
88
|
+
|
|
89
|
+
| `skillsConfig.audit` field | Type | Default |
|
|
90
|
+
| -------------------------- | ----------------------------- | --------- |
|
|
91
|
+
| `enabled` | `boolean` | `false` |
|
|
92
|
+
| `signer` | `SkillAuditSigner` | dev HS256 |
|
|
93
|
+
| `store` | `SkillAuditStore` | memory |
|
|
94
|
+
| `subjectMode` | `'plain' \| 'hash' \| 'omit'` | `'hash'` |
|
|
95
|
+
| `headAnchorIntervalMs` | `number \| undefined` | unset |
|
|
96
|
+
|
|
97
|
+
## Built-in Signers
|
|
98
|
+
|
|
99
|
+
| Signer | Key | When to use |
|
|
100
|
+
| ------------------ | --------------------------------------------------- | ------------------------------------------------------------------------------------ |
|
|
101
|
+
| `Hs256AuditSigner` | Symmetric HMAC-SHA-256 | Dev / tests only. Refuses to fire when `NODE_ENV === 'production'` with a random key |
|
|
102
|
+
| `Rs256AuditSigner` | Asymmetric RSA (RS256, RSASSA-PKCS1-v1_5 + SHA-256) | **Production.** Reuse the bundle-signing keypair so the same trust root covers both |
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { Rs256AuditSigner } from '@frontmcp/adapters/skills';
|
|
106
|
+
|
|
107
|
+
// Constructor signature: new Rs256AuditSigner(privateJwk, keyId)
|
|
108
|
+
// The signer accepts a JWK directly so the same key registry that backs
|
|
109
|
+
// bundle signing can be reused. If your key material lives as PEM, convert
|
|
110
|
+
// it to a JWK first (e.g. via `crypto.createPrivateKey(pem).export({ format: 'jwk' })`
|
|
111
|
+
// or `pemToPrivateJwk` from `@frontmcp/utils`).
|
|
112
|
+
const privateJwk = JSON.parse(process.env.BUNDLE_SIGNING_PRIVATE_JWK!) as JsonWebKey;
|
|
113
|
+
const signer = new Rs256AuditSigner(privateJwk, 'bundle-signing-2026-01');
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`Rs256AuditSigner` uses `rsaSignBase64Url` from `@frontmcp/utils` under the hood.
|
|
117
|
+
|
|
118
|
+
## Built-in Stores
|
|
119
|
+
|
|
120
|
+
| Store | Persistence | When to use |
|
|
121
|
+
| -------------------------- | --------------------------------------------------------- | ---------------- |
|
|
122
|
+
| `MemoryAuditStore` | In-process; lost on restart | Tests, local dev |
|
|
123
|
+
| `StorageAdapterAuditStore` | Any `@frontmcp/utils` storage adapter (Redis, KV, SQLite) | Production |
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import { StorageAdapterAuditStore } from '@frontmcp/adapters/skills';
|
|
127
|
+
import { createStorageAdapter } from '@frontmcp/utils';
|
|
128
|
+
|
|
129
|
+
const storage = await createStorageAdapter({ provider: 'redis', host: 'localhost', port: 6379 });
|
|
130
|
+
const store = new StorageAdapterAuditStore(storage);
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
A custom store implements:
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
interface SkillAuditStore {
|
|
137
|
+
/** Allocate the next monotonic sequence atomically. Maps to StorageAdapter.incr in production. */
|
|
138
|
+
nextSequence(): Promise<number>;
|
|
139
|
+
|
|
140
|
+
/** Persist a record at its claimed sequence. MUST refuse / throw if the slot is taken. */
|
|
141
|
+
appendAtSequence(record: SkillAuditRecord): Promise<void>;
|
|
142
|
+
|
|
143
|
+
/** Most recent record (or undefined for empty chain). Drives prevHash for the next write. */
|
|
144
|
+
tail(): Promise<SkillAuditRecord | undefined>;
|
|
145
|
+
|
|
146
|
+
/** Read records in sequence order; supports `{ from, limit }` for incremental verification. */
|
|
147
|
+
read(opts?: { from?: number; limit?: number }): Promise<SkillAuditRecord[]>;
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
See [`custom-store`](../examples/skill-audit-log/custom-store.md) for an S3-backed implementation.
|
|
152
|
+
|
|
153
|
+
## Verifying the Chain
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
import { defaultAuditSignatureVerifier, verifyChain, type AuditTrustedKey } from '@frontmcp/adapters/skills';
|
|
157
|
+
|
|
158
|
+
// `read()` walks the chain in sequence order; pass `{ from, limit }` for
|
|
159
|
+
// incremental verification in CI.
|
|
160
|
+
const records = await store.read();
|
|
161
|
+
|
|
162
|
+
// Trusted keys are passed as an array (per-record `signatureKeyId` selects
|
|
163
|
+
// which entry to use). Supply `publicJwk` or `publicKeyPem` for RS256, or
|
|
164
|
+
// `secret` for HS256.
|
|
165
|
+
const trustedKeys: AuditTrustedKey[] = [
|
|
166
|
+
{ keyId: 'bundle-signing-2026-01', alg: 'RS256', publicKeyPem: PUBLIC_KEY_PEM },
|
|
167
|
+
];
|
|
168
|
+
|
|
169
|
+
const result = verifyChain(records, trustedKeys, defaultAuditSignatureVerifier);
|
|
170
|
+
|
|
171
|
+
if (result.ok) {
|
|
172
|
+
console.log(`Chain verified: ${result.verified} record(s) checked`);
|
|
173
|
+
} else {
|
|
174
|
+
console.error('Chain broken at sequence', result.breakAt, '—', result.reason);
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
`verifyChain` returns `{ ok: true; verified: number } | { ok: false; breakAt: number; reason: string }`. The `verified` count is the number of records whose signature + prevHash checked out — useful for dashboards and CI assertions. `defaultAuditSignatureVerifier` understands HS256 and RS256 records and dispatches based on `record.signatureAlg`.
|
|
179
|
+
|
|
180
|
+
## DI Integration
|
|
181
|
+
|
|
182
|
+
`SkillAuditWriterToken` is the DI token for the active writer. Plugins that need to emit additional audit records (e.g., a custom authority gate) can resolve it:
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
import { SkillAuditWriterToken } from '@frontmcp/adapters/skills';
|
|
186
|
+
|
|
187
|
+
class MyPlugin extends DynamicPlugin {
|
|
188
|
+
@ToolHook.Will('execute')
|
|
189
|
+
willExecute(flowCtx: FlowCtxOf<'tools:call-tool'>): void {
|
|
190
|
+
const writer = flowCtx.scope.tryGet(SkillAuditWriterToken);
|
|
191
|
+
// Use the phase-specific method matching the event you're recording.
|
|
192
|
+
// The writer assembles the canonical record (sequence, prevHash,
|
|
193
|
+
// signature, signatureKeyId, signatureAlg) for you.
|
|
194
|
+
writer?.writeAuthorityPass({
|
|
195
|
+
subject: 'user-id',
|
|
196
|
+
skillId: 'my-skill',
|
|
197
|
+
actionId: 'my-action',
|
|
198
|
+
bundleId: 'bundle:id',
|
|
199
|
+
bundleVersion: '1.0.0',
|
|
200
|
+
input: {},
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Plugins should always go through `SkillAuditWriterToken` rather than rolling their own audit log so the chain stays unified.
|
|
207
|
+
|
|
208
|
+
## Threat Model
|
|
209
|
+
|
|
210
|
+
What the audit log catches:
|
|
211
|
+
|
|
212
|
+
- **Record mutation** — any byte-level change breaks the signature.
|
|
213
|
+
- **Record reordering** — the `prevHash` chain breaks.
|
|
214
|
+
- **Record deletion in the middle** — `prevHash` mismatch on the next record.
|
|
215
|
+
|
|
216
|
+
What it does **not** catch by default:
|
|
217
|
+
|
|
218
|
+
- **Tail truncation** — if an attacker deletes the tail, no record survives to flag it. Mitigation: `headAnchorIntervalMs` writes the latest `(sequence, hash)` pair to an out-of-band notarization channel (queued for v1.3.0 to be wired into the CAS-based atomic head update).
|
|
219
|
+
- **Multi-pod races** — v1.2.0 is **single-writer only**. Multiple pods writing to the same store will produce a loud warning and may interleave sequences. Route writes to a single elected leader pod or use per-pod chains and stitch offline. CAS-based atomic chain head is queued for v1.3.0.
|
|
220
|
+
|
|
221
|
+
## Examples
|
|
222
|
+
|
|
223
|
+
| Example | Level | Description |
|
|
224
|
+
| ------------------------------------------------------------- | ------------ | ------------------------------------------------------------------------------------------- |
|
|
225
|
+
| [`verify-chain`](../examples/skill-audit-log/verify-chain.md) | Intermediate | Verify a stored chain offline using verifyChain and the bundle-signing key registry. |
|
|
226
|
+
| [`custom-store`](../examples/skill-audit-log/custom-store.md) | Advanced | Implement a custom SkillAuditStore that streams records to S3 with one object per sequence. |
|
|
227
|
+
|
|
228
|
+
> See all examples in [`examples/skill-audit-log/`](../examples/skill-audit-log/)
|
|
229
|
+
|
|
230
|
+
## Reference
|
|
231
|
+
|
|
232
|
+
- [Skill Audit Log](https://docs.agentfront.dev/frontmcp/extensibility/skill-audit-log)
|
|
233
|
+
- Related skills: `configure-skills-http`, `create-plugin`
|