@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
package/catalog/frontmcp-guides/examples/example-knowledge-base/vector-search-and-resources.md
CHANGED
|
@@ -2,14 +2,20 @@
|
|
|
2
2
|
name: vector-search-and-resources
|
|
3
3
|
reference: example-knowledge-base
|
|
4
4
|
level: intermediate
|
|
5
|
-
description:
|
|
6
|
-
tags:
|
|
5
|
+
description: Shows a semantic search tool with embedding generation and a resource template for retrieving documents by ID using URI parameters.
|
|
6
|
+
tags:
|
|
7
|
+
- guides
|
|
8
|
+
- vectoriadb
|
|
9
|
+
- semantic-search
|
|
10
|
+
- knowledge-base
|
|
11
|
+
- knowledge
|
|
12
|
+
- base
|
|
7
13
|
features:
|
|
8
|
-
-
|
|
9
|
-
-
|
|
14
|
+
- Semantic search tool that delegates embedding generation to VectoriaDB via `store.search(query, topK)`
|
|
15
|
+
- Using `this.mark()` for execution phase tracing
|
|
10
16
|
- "Resource template with `uriTemplate: 'kb://documents/{documentId}'` for parameterized URIs"
|
|
11
17
|
- 'Typed params via `ResourceContext<{ documentId: string }>` for type-safe URI parameters'
|
|
12
|
-
-
|
|
18
|
+
- Returning `ReadResourceResult` with proper MCP protocol structure
|
|
13
19
|
---
|
|
14
20
|
|
|
15
21
|
# Knowledge Base: Semantic Search Tool and Resource Template
|
|
@@ -22,7 +28,7 @@ Shows a semantic search tool with embedding generation and a resource template f
|
|
|
22
28
|
// src/search/tools/search-docs.tool.ts
|
|
23
29
|
import { Tool, ToolContext, z } from '@frontmcp/sdk';
|
|
24
30
|
|
|
25
|
-
import {
|
|
31
|
+
import { VectorStoreProvider } from '../../ingestion/providers/vector-store.provider';
|
|
26
32
|
|
|
27
33
|
@Tool({
|
|
28
34
|
name: 'search_docs',
|
|
@@ -45,46 +51,29 @@ import { VECTOR_STORE } from '../../ingestion/providers/vector-store.provider';
|
|
|
45
51
|
})
|
|
46
52
|
export class SearchDocsTool extends ToolContext {
|
|
47
53
|
async execute(input: { query: string; topK: number }) {
|
|
48
|
-
const store = this.get(
|
|
49
|
-
|
|
50
|
-
// Mark execution phases for observability
|
|
51
|
-
this.mark('embedding-query');
|
|
52
|
-
const queryEmbedding = await this.generateQueryEmbedding(input.query);
|
|
54
|
+
const store = this.get(VectorStoreProvider);
|
|
53
55
|
|
|
56
|
+
// VectoriaDB handles embedding generation internally — pass the raw query.
|
|
54
57
|
this.mark('searching');
|
|
55
|
-
const
|
|
58
|
+
const matches = await store.search(input.query, input.topK);
|
|
56
59
|
|
|
57
|
-
const results =
|
|
58
|
-
documentId:
|
|
59
|
-
content:
|
|
60
|
-
score:
|
|
61
|
-
title:
|
|
60
|
+
const results = matches.map((m) => ({
|
|
61
|
+
documentId: m.metadata.documentId,
|
|
62
|
+
content: m.metadata.content,
|
|
63
|
+
score: m.score,
|
|
64
|
+
title: m.metadata.title ?? 'Untitled',
|
|
62
65
|
}));
|
|
63
66
|
|
|
64
67
|
return { results, total: results.length };
|
|
65
68
|
}
|
|
66
|
-
|
|
67
|
-
private async generateQueryEmbedding(query: string): Promise<number[]> {
|
|
68
|
-
const response = await this.fetch('https://api.openai.com/v1/embeddings', {
|
|
69
|
-
method: 'POST',
|
|
70
|
-
headers: {
|
|
71
|
-
'Content-Type': 'application/json',
|
|
72
|
-
Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
|
|
73
|
-
},
|
|
74
|
-
body: JSON.stringify({ input: query, model: 'text-embedding-3-small' }),
|
|
75
|
-
});
|
|
76
|
-
const data = await response.json();
|
|
77
|
-
return data.data[0].embedding;
|
|
78
|
-
}
|
|
79
69
|
}
|
|
80
70
|
```
|
|
81
71
|
|
|
82
72
|
```typescript
|
|
83
73
|
// src/search/resources/doc.resource.ts
|
|
84
|
-
import
|
|
85
|
-
import { ResourceContext, ResourceTemplate } from '@frontmcp/sdk';
|
|
74
|
+
import { ReadResourceResult, ResourceContext, ResourceNotFoundError, ResourceTemplate } from '@frontmcp/sdk';
|
|
86
75
|
|
|
87
|
-
import {
|
|
76
|
+
import { VectorStoreProvider } from '../../ingestion/providers/vector-store.provider';
|
|
88
77
|
|
|
89
78
|
@ResourceTemplate({
|
|
90
79
|
name: 'document',
|
|
@@ -94,20 +83,20 @@ import { VECTOR_STORE } from '../../ingestion/providers/vector-store.provider';
|
|
|
94
83
|
})
|
|
95
84
|
export class DocResource extends ResourceContext<{ documentId: string }> {
|
|
96
85
|
async execute(uri: string, params: { documentId: string }): Promise<ReadResourceResult> {
|
|
97
|
-
const store = this.get(
|
|
98
|
-
const
|
|
86
|
+
const store = this.get(VectorStoreProvider);
|
|
87
|
+
const matches = await store.getByDocumentId(params.documentId);
|
|
99
88
|
|
|
100
|
-
if (
|
|
101
|
-
|
|
89
|
+
if (matches.length === 0) {
|
|
90
|
+
// Use a typed MCP error so the protocol response carries the correct JSON-RPC code.
|
|
91
|
+
throw new ResourceNotFoundError(uri);
|
|
102
92
|
}
|
|
103
93
|
|
|
104
94
|
const document = {
|
|
105
95
|
documentId: params.documentId,
|
|
106
|
-
title:
|
|
107
|
-
chunks:
|
|
108
|
-
chunkIndex:
|
|
109
|
-
|
|
110
|
-
})),
|
|
96
|
+
title: matches[0].metadata.title ?? 'Untitled',
|
|
97
|
+
chunks: matches
|
|
98
|
+
.map((m) => ({ chunkIndex: m.metadata.chunkIndex, content: m.metadata.content }))
|
|
99
|
+
.sort((a, b) => a.chunkIndex - b.chunkIndex),
|
|
111
100
|
};
|
|
112
101
|
|
|
113
102
|
return {
|
|
@@ -125,7 +114,7 @@ export class DocResource extends ResourceContext<{ documentId: string }> {
|
|
|
125
114
|
|
|
126
115
|
## What This Demonstrates
|
|
127
116
|
|
|
128
|
-
- Semantic search tool that
|
|
117
|
+
- Semantic search tool that delegates embedding generation to VectoriaDB via `store.search(query, topK)`
|
|
129
118
|
- Using `this.mark()` for execution phase tracing
|
|
130
119
|
- Resource template with `uriTemplate: 'kb://documents/{documentId}'` for parameterized URIs
|
|
131
120
|
- Typed params via `ResourceContext<{ documentId: string }>` for type-safe URI parameters
|
|
@@ -2,19 +2,25 @@
|
|
|
2
2
|
name: auth-and-crud-tools
|
|
3
3
|
reference: example-task-manager
|
|
4
4
|
level: basic
|
|
5
|
-
description:
|
|
6
|
-
tags:
|
|
5
|
+
description: Shows how to create CRUD tools with authentication, using `this.auth?.user.sub` (the FrontMcpAuthContext exposed on every execution context) for user isolation and `this.get()` for dependency injection.
|
|
6
|
+
tags:
|
|
7
|
+
- guides
|
|
8
|
+
- auth
|
|
9
|
+
- session
|
|
10
|
+
- task-manager
|
|
11
|
+
- task
|
|
12
|
+
- manager
|
|
7
13
|
features:
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
14
|
+
- Using `this.auth?.user.sub` (FrontMcpAuthContext) for per-user data isolation
|
|
15
|
+
- Using `this.get(TaskStoreProvider)` for dependency injection of providers (class-as-token)
|
|
16
|
+
- Enforcing authentication with `this.fail()` when no authenticated user is present in `this.auth`
|
|
17
|
+
- Optional input fields with `.optional()` for filtering
|
|
12
18
|
- '`outputSchema` with nested `z.array(z.object(...))` for structured responses'
|
|
13
19
|
---
|
|
14
20
|
|
|
15
21
|
# Task Manager: Authenticated CRUD Tools
|
|
16
22
|
|
|
17
|
-
Shows how to create CRUD tools with authentication, using `this.
|
|
23
|
+
Shows how to create CRUD tools with authentication, using `this.auth?.user.sub` (the FrontMcpAuthContext exposed on every execution context) for user isolation and `this.get()` for dependency injection.
|
|
18
24
|
|
|
19
25
|
## Code
|
|
20
26
|
|
|
@@ -22,7 +28,7 @@ Shows how to create CRUD tools with authentication, using `this.context.session`
|
|
|
22
28
|
// src/tools/create-task.tool.ts
|
|
23
29
|
import { Tool, ToolContext, z } from '@frontmcp/sdk';
|
|
24
30
|
|
|
25
|
-
import {
|
|
31
|
+
import { TaskStoreProvider } from '../providers/task-store.provider';
|
|
26
32
|
|
|
27
33
|
@Tool({
|
|
28
34
|
name: 'create_task',
|
|
@@ -42,10 +48,11 @@ import { TASK_STORE } from '../providers/task-store.provider';
|
|
|
42
48
|
export class CreateTaskTool extends ToolContext {
|
|
43
49
|
async execute(input: { title: string; priority: 'low' | 'medium' | 'high' }) {
|
|
44
50
|
// Inject the task store via DI
|
|
45
|
-
|
|
51
|
+
// Inject the task store via DI (the class itself is the token)
|
|
52
|
+
const store = this.get(TaskStoreProvider);
|
|
46
53
|
|
|
47
|
-
// Get the authenticated user's
|
|
48
|
-
const userId = this.
|
|
54
|
+
// Get the authenticated user's id from FrontMcpAuthContext (`this.auth.user.sub`)
|
|
55
|
+
const userId = this.auth?.user.sub;
|
|
49
56
|
if (!userId) {
|
|
50
57
|
this.fail(new Error('Authentication required'));
|
|
51
58
|
}
|
|
@@ -72,7 +79,7 @@ export class CreateTaskTool extends ToolContext {
|
|
|
72
79
|
// src/tools/list-tasks.tool.ts
|
|
73
80
|
import { Tool, ToolContext, z } from '@frontmcp/sdk';
|
|
74
81
|
|
|
75
|
-
import {
|
|
82
|
+
import { TaskStoreProvider } from '../providers/task-store.provider';
|
|
76
83
|
|
|
77
84
|
@Tool({
|
|
78
85
|
name: 'list_tasks',
|
|
@@ -95,8 +102,8 @@ import { TASK_STORE } from '../providers/task-store.provider';
|
|
|
95
102
|
})
|
|
96
103
|
export class ListTasksTool extends ToolContext {
|
|
97
104
|
async execute(input: { status?: 'pending' | 'in_progress' | 'done' }) {
|
|
98
|
-
const store = this.get(
|
|
99
|
-
const userId = this.
|
|
105
|
+
const store = this.get(TaskStoreProvider);
|
|
106
|
+
const userId = this.auth?.user.sub;
|
|
100
107
|
|
|
101
108
|
if (!userId) {
|
|
102
109
|
this.fail(new Error('Authentication required'));
|
|
@@ -124,9 +131,9 @@ export class ListTasksTool extends ToolContext {
|
|
|
124
131
|
|
|
125
132
|
## What This Demonstrates
|
|
126
133
|
|
|
127
|
-
- Using `this.
|
|
128
|
-
- Using `this.get(
|
|
129
|
-
- Enforcing authentication with `this.fail()` when no
|
|
134
|
+
- Using `this.auth?.user.sub` (FrontMcpAuthContext) for per-user data isolation
|
|
135
|
+
- Using `this.get(TaskStoreProvider)` for dependency injection of providers (class-as-token)
|
|
136
|
+
- Enforcing authentication with `this.fail()` when no authenticated user is present in `this.auth`
|
|
130
137
|
- Optional input fields with `.optional()` for filtering
|
|
131
138
|
- `outputSchema` with nested `z.array(z.object(...))` for structured responses
|
|
132
139
|
|
|
@@ -7,9 +7,9 @@ tags: [guides, auth, session, e2e, unit-test, task-manager]
|
|
|
7
7
|
features:
|
|
8
8
|
- 'Using `TestTokenFactory` to create JWT tokens for authenticated E2E tests'
|
|
9
9
|
- 'Chaining `.withToken(token).buildAndConnect()` for authenticated clients'
|
|
10
|
-
- 'Unit testing
|
|
11
|
-
- 'Mocking
|
|
12
|
-
- 'Testing the unauthenticated error path (no
|
|
10
|
+
- 'Unit testing tools by mocking `this.get(TaskStoreProvider)` and the `auth` getter'
|
|
11
|
+
- 'Mocking `this.auth = { user: { sub } }` (FrontMcpAuthContext) for auth-dependent tools'
|
|
12
|
+
- 'Testing the unauthenticated error path (no `auth` / anonymous user)'
|
|
13
13
|
---
|
|
14
14
|
|
|
15
15
|
# Task Manager: Authenticated E2E Tests
|
|
@@ -43,7 +43,7 @@ describe('Task Manager E2E', () => {
|
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
it('should list all CRUD tools', async () => {
|
|
46
|
-
const
|
|
46
|
+
const tools = await client.tools.list();
|
|
47
47
|
const names = tools.map((t) => t.name);
|
|
48
48
|
|
|
49
49
|
expect(names).toContain('create_task');
|
|
@@ -53,16 +53,16 @@ describe('Task Manager E2E', () => {
|
|
|
53
53
|
});
|
|
54
54
|
|
|
55
55
|
it('should create and list a task', async () => {
|
|
56
|
-
const createResult = await client.
|
|
56
|
+
const createResult = await client.tools.call('create_task', {
|
|
57
57
|
title: 'E2E test task',
|
|
58
58
|
priority: 'high',
|
|
59
59
|
});
|
|
60
|
-
expect(createResult).
|
|
60
|
+
expect(createResult.isError).toBeFalsy();
|
|
61
61
|
|
|
62
|
-
const listResult = await client.
|
|
63
|
-
expect(listResult).
|
|
62
|
+
const listResult = await client.tools.call('list_tasks', {});
|
|
63
|
+
expect(listResult.isError).toBeFalsy();
|
|
64
64
|
|
|
65
|
-
const parsed =
|
|
65
|
+
const parsed = listResult.json<{ tasks: Array<{ title: string }> }>();
|
|
66
66
|
expect(parsed.tasks.length).toBeGreaterThan(0);
|
|
67
67
|
expect(parsed.tasks.some((t: { title: string }) => t.title === 'E2E test task')).toBe(true);
|
|
68
68
|
});
|
|
@@ -70,15 +70,16 @@ describe('Task Manager E2E', () => {
|
|
|
70
70
|
```
|
|
71
71
|
|
|
72
72
|
```typescript
|
|
73
|
-
// test/create-task.tool.spec.ts — Unit test with mocked
|
|
73
|
+
// test/create-task.tool.spec.ts — Unit test with mocked auth context
|
|
74
74
|
import { ToolContext } from '@frontmcp/sdk';
|
|
75
|
+
|
|
76
|
+
import { TaskStoreProvider } from '../src/providers/task-store.provider';
|
|
75
77
|
import { CreateTaskTool } from '../src/tools/create-task.tool';
|
|
76
|
-
import { TASK_STORE, type TaskStore } from '../src/providers/task-store.provider';
|
|
77
78
|
import type { Task } from '../src/types/task';
|
|
78
79
|
|
|
79
80
|
describe('CreateTaskTool', () => {
|
|
80
81
|
let tool: CreateTaskTool;
|
|
81
|
-
let mockStore: jest.Mocked<
|
|
82
|
+
let mockStore: jest.Mocked<TaskStoreProvider>;
|
|
82
83
|
|
|
83
84
|
beforeEach(() => {
|
|
84
85
|
tool = new CreateTaskTool();
|
|
@@ -87,13 +88,13 @@ describe('CreateTaskTool', () => {
|
|
|
87
88
|
list: jest.fn(),
|
|
88
89
|
update: jest.fn(),
|
|
89
90
|
delete: jest.fn(),
|
|
90
|
-
}
|
|
91
|
+
} as unknown as jest.Mocked<TaskStoreProvider>;
|
|
91
92
|
});
|
|
92
93
|
|
|
93
94
|
function applyContext(userId: string | undefined): void {
|
|
94
95
|
const ctx = {
|
|
95
|
-
get: jest.fn((token:
|
|
96
|
-
if (token ===
|
|
96
|
+
get: jest.fn((token: unknown) => {
|
|
97
|
+
if (token === TaskStoreProvider) return mockStore;
|
|
97
98
|
throw new Error(`Unknown token: ${String(token)}`);
|
|
98
99
|
}),
|
|
99
100
|
tryGet: jest.fn(),
|
|
@@ -101,9 +102,10 @@ describe('CreateTaskTool', () => {
|
|
|
101
102
|
throw err;
|
|
102
103
|
}),
|
|
103
104
|
mark: jest.fn(),
|
|
104
|
-
notify: jest.fn(),
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
notify: jest.fn().mockResolvedValue(true),
|
|
106
|
+
progress: jest.fn().mockResolvedValue(true),
|
|
107
|
+
// Stub the FrontMcpAuthContext exposed via the `auth` getter on the real SDK.
|
|
108
|
+
auth: userId ? { user: { sub: userId }, isAnonymous: false } : undefined,
|
|
107
109
|
} as unknown as ToolContext;
|
|
108
110
|
Object.assign(tool, ctx);
|
|
109
111
|
}
|
|
@@ -139,9 +141,9 @@ describe('CreateTaskTool', () => {
|
|
|
139
141
|
|
|
140
142
|
- Using `TestTokenFactory` to create JWT tokens for authenticated E2E tests
|
|
141
143
|
- Chaining `.withToken(token).buildAndConnect()` for authenticated clients
|
|
142
|
-
- Unit testing
|
|
143
|
-
- Mocking
|
|
144
|
-
- Testing the unauthenticated error path (no
|
|
144
|
+
- Unit testing tools by mocking `this.get(TaskStoreProvider)` and the `auth` getter
|
|
145
|
+
- Mocking `this.auth = { user: { sub } }` (FrontMcpAuthContext) for auth-dependent tools
|
|
146
|
+
- Testing the unauthenticated error path (no `auth` / anonymous user)
|
|
145
147
|
|
|
146
148
|
## Related
|
|
147
149
|
|
|
@@ -2,20 +2,25 @@
|
|
|
2
2
|
name: redis-provider-with-di
|
|
3
3
|
reference: example-task-manager
|
|
4
4
|
level: intermediate
|
|
5
|
-
description:
|
|
6
|
-
tags:
|
|
5
|
+
description: Shows how to create a Redis-backed provider using the class-as-token DI pattern (`@Provider({ name, scope })`) plus an `AsyncProvider` factory that runs the async Redis setup before any tool is invoked.
|
|
6
|
+
tags:
|
|
7
|
+
- guides
|
|
8
|
+
- redis
|
|
9
|
+
- node
|
|
10
|
+
- task-manager
|
|
11
|
+
- task
|
|
12
|
+
- manager
|
|
7
13
|
features:
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
- '
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
- 'Per-user data isolation using Redis hash keys (`tasks:${userId}`)'
|
|
14
|
+
- 'Class-as-token DI: `@Provider({ name, scope })` and inject via `this.get(TaskStoreProvider)`'
|
|
15
|
+
- Building the singleton with `AsyncProvider({ provide, name, scope, useFactory })` for async setup
|
|
16
|
+
- 'Cleanup: explicit `disconnect()` method (called from the host before `server.dispose()`) — `@Provider` has no `onDestroy` hook'
|
|
17
|
+
- Using `@frontmcp/utils` for `randomUUID()` instead of `node:crypto`
|
|
18
|
+
- Per-user data isolation using Redis hash keys (`tasks:${userId}`)
|
|
14
19
|
---
|
|
15
20
|
|
|
16
21
|
# Task Manager: Redis Provider with Dependency Injection
|
|
17
22
|
|
|
18
|
-
Shows how to create a Redis-backed provider
|
|
23
|
+
Shows how to create a Redis-backed provider using the class-as-token DI pattern (`@Provider({ name, scope })`) plus an `AsyncProvider` factory that runs the async Redis setup before any tool is invoked.
|
|
19
24
|
|
|
20
25
|
## Code
|
|
21
26
|
|
|
@@ -33,32 +38,18 @@ export interface Task {
|
|
|
33
38
|
|
|
34
39
|
```typescript
|
|
35
40
|
// src/providers/task-store.provider.ts
|
|
36
|
-
import {
|
|
37
|
-
import type { Token } from '@frontmcp/di';
|
|
38
|
-
import type { Task } from '../types/task';
|
|
39
|
-
|
|
40
|
-
export interface TaskStore {
|
|
41
|
-
create(task: Omit<Task, 'id' | 'createdAt'>): Promise<Task>;
|
|
42
|
-
list(userId: string): Promise<Task[]>;
|
|
43
|
-
update(id: string, userId: string, data: Partial<Pick<Task, 'title' | 'priority' | 'status'>>): Promise<Task>;
|
|
44
|
-
delete(id: string, userId: string): Promise<void>;
|
|
45
|
-
}
|
|
41
|
+
import Redis, { Redis as RedisClient } from 'ioredis';
|
|
46
42
|
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
import { AsyncProvider, Provider, ProviderScope } from '@frontmcp/sdk';
|
|
44
|
+
import { randomUUID } from '@frontmcp/utils';
|
|
49
45
|
|
|
50
|
-
|
|
51
|
-
export class RedisTaskStoreProvider implements TaskStore {
|
|
52
|
-
private redis!: import('ioredis').default;
|
|
46
|
+
import type { Task } from '../types/task';
|
|
53
47
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
this.redis = new Redis(process.env.REDIS_URL ?? 'redis://localhost:6379');
|
|
58
|
-
}
|
|
48
|
+
@Provider({ name: 'task-store', scope: ProviderScope.GLOBAL })
|
|
49
|
+
export class TaskStoreProvider {
|
|
50
|
+
constructor(private readonly redis: RedisClient) {}
|
|
59
51
|
|
|
60
52
|
async create(input: Omit<Task, 'id' | 'createdAt'>): Promise<Task> {
|
|
61
|
-
const { randomUUID } = await import('@frontmcp/utils');
|
|
62
53
|
const task: Task = {
|
|
63
54
|
...input,
|
|
64
55
|
id: randomUUID(),
|
|
@@ -90,26 +81,44 @@ export class RedisTaskStoreProvider implements TaskStore {
|
|
|
90
81
|
}
|
|
91
82
|
}
|
|
92
83
|
|
|
93
|
-
//
|
|
94
|
-
|
|
84
|
+
// `@Provider` has no `onDestroy` hook — expose explicit cleanup that the
|
|
85
|
+
// host calls before `server.dispose()` (e.g., from the SIGTERM/process
|
|
86
|
+
// shutdown side-effect handler).
|
|
87
|
+
async disconnect(): Promise<void> {
|
|
95
88
|
await this.redis.quit();
|
|
96
89
|
}
|
|
97
90
|
}
|
|
91
|
+
|
|
92
|
+
// AsyncProvider factory: builds the TaskStoreProvider singleton with a connected Redis client.
|
|
93
|
+
// Tools still inject the class itself: `this.get(TaskStoreProvider)`.
|
|
94
|
+
export const createTaskStoreProvider = AsyncProvider({
|
|
95
|
+
provide: TaskStoreProvider,
|
|
96
|
+
name: 'task-store-factory',
|
|
97
|
+
scope: ProviderScope.GLOBAL,
|
|
98
|
+
inject: () => [] as const,
|
|
99
|
+
useFactory: async () => {
|
|
100
|
+
const redis = new Redis(process.env.REDIS_URL ?? 'redis://localhost:6379');
|
|
101
|
+
return new TaskStoreProvider(redis);
|
|
102
|
+
},
|
|
103
|
+
});
|
|
98
104
|
```
|
|
99
105
|
|
|
100
106
|
```typescript
|
|
101
107
|
// src/tasks.app.ts
|
|
102
108
|
import { App } from '@frontmcp/sdk';
|
|
103
|
-
|
|
109
|
+
|
|
110
|
+
import { createTaskStoreProvider } from './providers/task-store.provider';
|
|
104
111
|
import { CreateTaskTool } from './tools/create-task.tool';
|
|
112
|
+
import { DeleteTaskTool } from './tools/delete-task.tool';
|
|
105
113
|
import { ListTasksTool } from './tools/list-tasks.tool';
|
|
106
114
|
import { UpdateTaskTool } from './tools/update-task.tool';
|
|
107
|
-
import { DeleteTaskTool } from './tools/delete-task.tool';
|
|
108
115
|
|
|
109
116
|
@App({
|
|
110
117
|
name: 'Tasks',
|
|
111
118
|
description: 'Task management with CRUD operations',
|
|
112
|
-
|
|
119
|
+
// The AsyncProvider factory binds the `TaskStoreProvider` class as the DI token.
|
|
120
|
+
// Tools inject the same class via `this.get(TaskStoreProvider)`.
|
|
121
|
+
providers: [createTaskStoreProvider],
|
|
113
122
|
tools: [CreateTaskTool, ListTasksTool, UpdateTaskTool, DeleteTaskTool],
|
|
114
123
|
})
|
|
115
124
|
export class TasksApp {}
|
|
@@ -117,10 +126,9 @@ export class TasksApp {}
|
|
|
117
126
|
|
|
118
127
|
## What This Demonstrates
|
|
119
128
|
|
|
120
|
-
-
|
|
121
|
-
-
|
|
122
|
-
-
|
|
123
|
-
- Lazy-loading `ioredis` via dynamic `import()` in `onInit()`
|
|
129
|
+
- Class-as-token DI: `@Provider({ name, scope })` and inject via `this.get(TaskStoreProvider)`
|
|
130
|
+
- Building the singleton with `AsyncProvider({ provide, name, scope, useFactory })` for async setup
|
|
131
|
+
- Cleanup: explicit `disconnect()` method (called from the host before `server.dispose()`) — `@Provider` has no `onDestroy` hook
|
|
124
132
|
- Using `@frontmcp/utils` for `randomUUID()` instead of `node:crypto`
|
|
125
133
|
- Per-user data isolation using Redis hash keys (`tasks:${userId}`)
|
|
126
134
|
|
|
@@ -7,7 +7,7 @@ tags: [guides, weather, api, app, setup]
|
|
|
7
7
|
features:
|
|
8
8
|
- 'Server entry point with `@FrontMcp` decorator and `info` configuration'
|
|
9
9
|
- 'App registration with `@App` grouping tools and resources together'
|
|
10
|
-
- 'Static resource that returns JSON data via `
|
|
10
|
+
- 'Static resource that returns JSON data via `execute(uri)` and `ReadResourceResult`'
|
|
11
11
|
- 'Clean separation between server, app, tools, and resources'
|
|
12
12
|
---
|
|
13
13
|
|
|
@@ -20,6 +20,7 @@ Shows the server entry point, app registration, and static resource for a beginn
|
|
|
20
20
|
```typescript
|
|
21
21
|
// src/main.ts
|
|
22
22
|
import { FrontMcp } from '@frontmcp/sdk';
|
|
23
|
+
|
|
23
24
|
import { WeatherApp } from './weather.app';
|
|
24
25
|
|
|
25
26
|
@FrontMcp({
|
|
@@ -32,8 +33,9 @@ export default class WeatherServer {}
|
|
|
32
33
|
```typescript
|
|
33
34
|
// src/weather.app.ts
|
|
34
35
|
import { App } from '@frontmcp/sdk';
|
|
35
|
-
|
|
36
|
+
|
|
36
37
|
import { CitiesResource } from './resources/cities.resource';
|
|
38
|
+
import { GetWeatherTool } from './tools/get-weather.tool';
|
|
37
39
|
|
|
38
40
|
@App({
|
|
39
41
|
name: 'Weather',
|
|
@@ -46,7 +48,7 @@ export class WeatherApp {}
|
|
|
46
48
|
|
|
47
49
|
```typescript
|
|
48
50
|
// src/resources/cities.resource.ts
|
|
49
|
-
import { Resource, ResourceContext } from '@frontmcp/sdk';
|
|
51
|
+
import { ReadResourceResult, Resource, ResourceContext } from '@frontmcp/sdk';
|
|
50
52
|
|
|
51
53
|
const SUPPORTED_CITIES = ['London', 'Tokyo', 'New York', 'Paris', 'Sydney', 'Berlin', 'Toronto', 'Mumbai'];
|
|
52
54
|
|
|
@@ -57,8 +59,16 @@ const SUPPORTED_CITIES = ['London', 'Tokyo', 'New York', 'Paris', 'Sydney', 'Ber
|
|
|
57
59
|
mimeType: 'application/json',
|
|
58
60
|
})
|
|
59
61
|
export class CitiesResource extends ResourceContext {
|
|
60
|
-
async
|
|
61
|
-
return
|
|
62
|
+
async execute(uri: string): Promise<ReadResourceResult> {
|
|
63
|
+
return {
|
|
64
|
+
contents: [
|
|
65
|
+
{
|
|
66
|
+
uri,
|
|
67
|
+
mimeType: 'application/json',
|
|
68
|
+
text: JSON.stringify(SUPPORTED_CITIES),
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
};
|
|
62
72
|
}
|
|
63
73
|
}
|
|
64
74
|
```
|
|
@@ -67,7 +77,7 @@ export class CitiesResource extends ResourceContext {
|
|
|
67
77
|
|
|
68
78
|
- Server entry point with `@FrontMcp` decorator and `info` configuration
|
|
69
79
|
- App registration with `@App` grouping tools and resources together
|
|
70
|
-
- Static resource that returns JSON data via `
|
|
80
|
+
- Static resource that returns JSON data via `execute(uri)` and `ReadResourceResult`
|
|
71
81
|
- Clean separation between server, app, tools, and resources
|
|
72
82
|
|
|
73
83
|
## Related
|
|
@@ -8,7 +8,7 @@ features:
|
|
|
8
8
|
- 'Unit testing tools by mocking `this.fetch()`, `this.fail()`, and other context methods'
|
|
9
9
|
- 'Using `Object.assign(tool, ctx)` to inject mock context into the tool instance'
|
|
10
10
|
- 'E2E testing with `TestServer.start()` and `McpTestClient.create()`'
|
|
11
|
-
- '
|
|
11
|
+
- 'Asserting tool presence via `expect(tools.map((t) => t.name)).toContain(...)` and parsing resource JSON via `result.json<T>()`'
|
|
12
12
|
- 'Proper cleanup with `client.disconnect()` and `server.stop()` in `afterAll`'
|
|
13
13
|
---
|
|
14
14
|
|
|
@@ -21,6 +21,7 @@ Shows how to write unit tests for tools by mocking context methods, and E2E test
|
|
|
21
21
|
```typescript
|
|
22
22
|
// test/get-weather.tool.spec.ts
|
|
23
23
|
import { ToolContext } from '@frontmcp/sdk';
|
|
24
|
+
|
|
24
25
|
import { GetWeatherTool } from '../src/tools/get-weather.tool';
|
|
25
26
|
|
|
26
27
|
describe('GetWeatherTool', () => {
|
|
@@ -49,7 +50,7 @@ describe('GetWeatherTool', () => {
|
|
|
49
50
|
get: jest.fn(),
|
|
50
51
|
tryGet: jest.fn(),
|
|
51
52
|
notify: jest.fn(),
|
|
52
|
-
|
|
53
|
+
progress: jest.fn(),
|
|
53
54
|
} as unknown as ToolContext;
|
|
54
55
|
Object.assign(tool, ctx);
|
|
55
56
|
|
|
@@ -81,7 +82,7 @@ describe('GetWeatherTool', () => {
|
|
|
81
82
|
get: jest.fn(),
|
|
82
83
|
tryGet: jest.fn(),
|
|
83
84
|
notify: jest.fn(),
|
|
84
|
-
|
|
85
|
+
progress: jest.fn(),
|
|
85
86
|
} as unknown as ToolContext;
|
|
86
87
|
Object.assign(tool, ctx);
|
|
87
88
|
|
|
@@ -112,15 +113,15 @@ describe('Weather Server E2E', () => {
|
|
|
112
113
|
});
|
|
113
114
|
|
|
114
115
|
it('should list tools including get_weather', async () => {
|
|
115
|
-
const
|
|
116
|
+
const tools = await client.tools.list();
|
|
116
117
|
|
|
117
118
|
expect(tools.length).toBeGreaterThan(0);
|
|
118
|
-
expect(tools).
|
|
119
|
+
expect(tools.map((t) => t.name)).toContain('get_weather');
|
|
119
120
|
});
|
|
120
121
|
|
|
121
122
|
it('should read the cities resource', async () => {
|
|
122
|
-
const result = await client.
|
|
123
|
-
const cities =
|
|
123
|
+
const result = await client.resources.read('weather://cities');
|
|
124
|
+
const cities = result.json<string[]>();
|
|
124
125
|
|
|
125
126
|
expect(Array.isArray(cities)).toBe(true);
|
|
126
127
|
expect(cities).toContain('London');
|
|
@@ -134,7 +135,7 @@ describe('Weather Server E2E', () => {
|
|
|
134
135
|
- Unit testing tools by mocking `this.fetch()`, `this.fail()`, and other context methods
|
|
135
136
|
- Using `Object.assign(tool, ctx)` to inject mock context into the tool instance
|
|
136
137
|
- E2E testing with `TestServer.start()` and `McpTestClient.create()`
|
|
137
|
-
-
|
|
138
|
+
- Asserting tool presence via `expect(tools.map((t) => t.name)).toContain(...)` and parsing resource JSON via `result.json<T>()`
|
|
138
139
|
- Proper cleanup with `client.disconnect()` and `server.stop()` in `afterAll`
|
|
139
140
|
|
|
140
141
|
## Related
|