@frontmcp/skills 0.0.1
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/LICENSE +201 -0
- package/README.md +135 -0
- package/catalog/TEMPLATE.md +49 -0
- package/catalog/adapters/create-adapter/SKILL.md +127 -0
- package/catalog/adapters/official-adapters/SKILL.md +136 -0
- package/catalog/auth/configure-auth/SKILL.md +250 -0
- package/catalog/auth/configure-auth/references/auth-modes.md +77 -0
- package/catalog/auth/configure-session/SKILL.md +201 -0
- package/catalog/config/configure-elicitation/SKILL.md +136 -0
- package/catalog/config/configure-http/SKILL.md +167 -0
- package/catalog/config/configure-throttle/SKILL.md +189 -0
- package/catalog/config/configure-throttle/references/guard-config.md +68 -0
- package/catalog/config/configure-transport/SKILL.md +151 -0
- package/catalog/config/configure-transport/references/protocol-presets.md +57 -0
- package/catalog/deployment/build-for-browser/SKILL.md +95 -0
- package/catalog/deployment/build-for-cli/SKILL.md +100 -0
- package/catalog/deployment/build-for-sdk/SKILL.md +218 -0
- package/catalog/deployment/deploy-to-cloudflare/SKILL.md +192 -0
- package/catalog/deployment/deploy-to-lambda/SKILL.md +304 -0
- package/catalog/deployment/deploy-to-node/SKILL.md +229 -0
- package/catalog/deployment/deploy-to-node/references/Dockerfile.example +45 -0
- package/catalog/deployment/deploy-to-vercel/SKILL.md +196 -0
- package/catalog/deployment/deploy-to-vercel/references/vercel.json.example +60 -0
- package/catalog/development/create-agent/SKILL.md +563 -0
- package/catalog/development/create-agent/references/llm-config.md +46 -0
- package/catalog/development/create-job/SKILL.md +566 -0
- package/catalog/development/create-prompt/SKILL.md +400 -0
- package/catalog/development/create-provider/SKILL.md +233 -0
- package/catalog/development/create-resource/SKILL.md +437 -0
- package/catalog/development/create-skill/SKILL.md +526 -0
- package/catalog/development/create-skill-with-tools/SKILL.md +579 -0
- package/catalog/development/create-tool/SKILL.md +418 -0
- package/catalog/development/create-tool/references/output-schema-types.md +56 -0
- package/catalog/development/create-tool/references/tool-annotations.md +34 -0
- package/catalog/development/create-workflow/SKILL.md +709 -0
- package/catalog/development/decorators-guide/SKILL.md +598 -0
- package/catalog/plugins/create-plugin/SKILL.md +336 -0
- package/catalog/plugins/create-plugin-hooks/SKILL.md +282 -0
- package/catalog/plugins/official-plugins/SKILL.md +667 -0
- package/catalog/setup/frontmcp-skills-usage/SKILL.md +200 -0
- package/catalog/setup/multi-app-composition/SKILL.md +358 -0
- package/catalog/setup/nx-workflow/SKILL.md +357 -0
- package/catalog/setup/project-structure-nx/SKILL.md +186 -0
- package/catalog/setup/project-structure-standalone/SKILL.md +153 -0
- package/catalog/setup/setup-project/SKILL.md +493 -0
- package/catalog/setup/setup-redis/SKILL.md +385 -0
- package/catalog/setup/setup-sqlite/SKILL.md +359 -0
- package/catalog/skills-manifest.json +414 -0
- package/catalog/testing/setup-testing/SKILL.md +539 -0
- package/catalog/testing/setup-testing/references/test-auth.md +88 -0
- package/catalog/testing/setup-testing/references/test-browser-build.md +57 -0
- package/catalog/testing/setup-testing/references/test-cli-binary.md +48 -0
- package/catalog/testing/setup-testing/references/test-direct-client.md +62 -0
- package/catalog/testing/setup-testing/references/test-e2e-handler.md +51 -0
- package/catalog/testing/setup-testing/references/test-tool-unit.md +41 -0
- package/package.json +34 -0
- package/src/index.d.ts +3 -0
- package/src/index.js +16 -0
- package/src/index.js.map +1 -0
- package/src/loader.d.ts +46 -0
- package/src/loader.js +75 -0
- package/src/loader.js.map +1 -0
- package/src/manifest.d.ts +81 -0
- package/src/manifest.js +26 -0
- package/src/manifest.js.map +1 -0
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-prompt
|
|
3
|
+
description: Create MCP prompts for reusable AI interaction patterns. Use when building prompts, defining prompt arguments, or creating conversation templates.
|
|
4
|
+
tags: [prompts, mcp, templates, messages, decorator]
|
|
5
|
+
tools:
|
|
6
|
+
- name: create_prompt
|
|
7
|
+
purpose: Scaffold a new prompt class
|
|
8
|
+
parameters:
|
|
9
|
+
- name: name
|
|
10
|
+
description: Prompt name in kebab-case
|
|
11
|
+
type: string
|
|
12
|
+
required: true
|
|
13
|
+
examples:
|
|
14
|
+
- scenario: Create a code review prompt with language argument
|
|
15
|
+
expected-outcome: Prompt registered and callable via MCP
|
|
16
|
+
- scenario: Create a multi-turn debugging prompt with assistant priming
|
|
17
|
+
expected-outcome: Prompt producing structured message sequences
|
|
18
|
+
priority: 10
|
|
19
|
+
visibility: both
|
|
20
|
+
license: Apache-2.0
|
|
21
|
+
metadata:
|
|
22
|
+
docs: https://docs.agentfront.dev/frontmcp/servers/prompts
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
# Creating MCP Prompts
|
|
26
|
+
|
|
27
|
+
Prompts define reusable AI interaction patterns in the MCP protocol. They produce structured message sequences that clients use to guide LLM conversations. In FrontMCP, prompts are classes extending `PromptContext`, decorated with `@Prompt`, that return `GetPromptResult` objects.
|
|
28
|
+
|
|
29
|
+
## When to Use @Prompt
|
|
30
|
+
|
|
31
|
+
Use `@Prompt` when you need to expose a reusable conversation template that an AI client can invoke with arguments. Prompts are ideal for code review patterns, debugging sessions, RAG queries, report generation, translation workflows, and any scenario where you want a standardized message structure.
|
|
32
|
+
|
|
33
|
+
## Class-Based Pattern
|
|
34
|
+
|
|
35
|
+
Create a class extending `PromptContext` and implement `execute(args)`. The `@Prompt` decorator accepts `name`, optional `description`, and `arguments` (the prompt's input parameters).
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import { Prompt, PromptContext } from '@frontmcp/sdk';
|
|
39
|
+
import { GetPromptResult } from '@frontmcp/protocol';
|
|
40
|
+
|
|
41
|
+
@Prompt({
|
|
42
|
+
name: 'code-review',
|
|
43
|
+
description: 'Generate a structured code review for the given code',
|
|
44
|
+
arguments: [
|
|
45
|
+
{ name: 'code', description: 'The code to review', required: true },
|
|
46
|
+
{ name: 'language', description: 'Programming language', required: false },
|
|
47
|
+
],
|
|
48
|
+
})
|
|
49
|
+
class CodeReviewPrompt extends PromptContext {
|
|
50
|
+
async execute(args: Record<string, string>): Promise<GetPromptResult> {
|
|
51
|
+
const language = args.language ?? 'unknown language';
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
messages: [
|
|
55
|
+
{
|
|
56
|
+
role: 'user',
|
|
57
|
+
content: {
|
|
58
|
+
type: 'text',
|
|
59
|
+
text: `Please review the following ${language} code. Focus on correctness, performance, and maintainability.\n\n\`\`\`${language}\n${args.code}\n\`\`\``,
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Decorator Options
|
|
69
|
+
|
|
70
|
+
The `@Prompt` decorator accepts:
|
|
71
|
+
|
|
72
|
+
- `name` (required) -- unique prompt name
|
|
73
|
+
- `description` (optional) -- human-readable description
|
|
74
|
+
- `arguments` (optional) -- array of `PromptArgument` objects
|
|
75
|
+
|
|
76
|
+
### PromptArgument Structure
|
|
77
|
+
|
|
78
|
+
Each entry in the `arguments` array has this shape:
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
interface PromptArgument {
|
|
82
|
+
name: string; // argument name
|
|
83
|
+
description?: string; // human-readable description
|
|
84
|
+
required?: boolean; // whether the argument must be provided (default: false)
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Required arguments are validated before `execute()` runs. Missing required arguments throw `MissingPromptArgumentError`.
|
|
89
|
+
|
|
90
|
+
### GetPromptResult Structure
|
|
91
|
+
|
|
92
|
+
The `execute()` method must return a `GetPromptResult`:
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
interface GetPromptResult {
|
|
96
|
+
messages: Array<{
|
|
97
|
+
role: 'user' | 'assistant';
|
|
98
|
+
content: {
|
|
99
|
+
type: 'text';
|
|
100
|
+
text: string;
|
|
101
|
+
};
|
|
102
|
+
}>;
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Messages use two roles:
|
|
107
|
+
|
|
108
|
+
- `user` -- represents the human side of the conversation
|
|
109
|
+
- `assistant` -- primes the conversation with expected response patterns
|
|
110
|
+
|
|
111
|
+
### Available Context Methods and Properties
|
|
112
|
+
|
|
113
|
+
`PromptContext` extends `ExecutionContextBase`, providing:
|
|
114
|
+
|
|
115
|
+
**Methods:**
|
|
116
|
+
|
|
117
|
+
- `execute(args)` -- the main method you implement, receives `Record<string, string>`
|
|
118
|
+
- `this.get(token)` -- resolve a dependency from DI (throws if not found)
|
|
119
|
+
- `this.tryGet(token)` -- resolve a dependency from DI (returns `undefined` if not found)
|
|
120
|
+
- `this.fail(err)` -- abort execution, triggers error flow (never returns)
|
|
121
|
+
- `this.mark(stage)` -- set active execution stage for debugging/tracking
|
|
122
|
+
- `this.fetch(input, init?)` -- HTTP fetch with context propagation
|
|
123
|
+
|
|
124
|
+
**Properties:**
|
|
125
|
+
|
|
126
|
+
- `this.metadata` -- prompt metadata from the decorator
|
|
127
|
+
- `this.scope` -- the current scope instance
|
|
128
|
+
- `this.context` -- the execution context
|
|
129
|
+
|
|
130
|
+
## Multi-Turn Conversations
|
|
131
|
+
|
|
132
|
+
Use `assistant` role messages to prime the conversation with expected response patterns:
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
@Prompt({
|
|
136
|
+
name: 'debug-session',
|
|
137
|
+
description: 'Start a structured debugging session',
|
|
138
|
+
arguments: [
|
|
139
|
+
{ name: 'error', description: 'The error message or stack trace', required: true },
|
|
140
|
+
{ name: 'context', description: 'Additional context about what was happening', required: false },
|
|
141
|
+
],
|
|
142
|
+
})
|
|
143
|
+
class DebugSessionPrompt extends PromptContext {
|
|
144
|
+
async execute(args: Record<string, string>): Promise<GetPromptResult> {
|
|
145
|
+
const contextNote = args.context ? `\n\nAdditional context: ${args.context}` : '';
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
messages: [
|
|
149
|
+
{
|
|
150
|
+
role: 'user',
|
|
151
|
+
content: {
|
|
152
|
+
type: 'text',
|
|
153
|
+
text: `I encountered an error and need help debugging it.\n\nError:\n\`\`\`\n${args.error}\n\`\`\`${contextNote}`,
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
role: 'assistant',
|
|
158
|
+
content: {
|
|
159
|
+
type: 'text',
|
|
160
|
+
text: "I'll help you debug this. Let me analyze the error systematically.\n\n**Step 1: Error Classification**\nLet me first identify what type of error this is and its likely root cause.\n\n",
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
role: 'user',
|
|
165
|
+
content: {
|
|
166
|
+
type: 'text',
|
|
167
|
+
text: 'Please continue with your analysis and suggest specific fixes.',
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Dynamic Prompt Generation
|
|
177
|
+
|
|
178
|
+
Prompts can perform async operations to generate context-aware messages. Use `this.get()` for dependency injection and `this.fetch()` for HTTP requests.
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
import type { Token } from '@frontmcp/di';
|
|
182
|
+
|
|
183
|
+
interface KnowledgeBase {
|
|
184
|
+
search(query: string, limit: number): Promise<Array<{ title: string; content: string }>>;
|
|
185
|
+
}
|
|
186
|
+
const KNOWLEDGE_BASE: Token<KnowledgeBase> = Symbol('knowledge-base');
|
|
187
|
+
|
|
188
|
+
@Prompt({
|
|
189
|
+
name: 'rag-query',
|
|
190
|
+
description: 'Answer a question using knowledge base context',
|
|
191
|
+
arguments: [
|
|
192
|
+
{ name: 'question', description: 'The question to answer', required: true },
|
|
193
|
+
{ name: 'maxSources', description: 'Maximum number of sources to include', required: false },
|
|
194
|
+
],
|
|
195
|
+
})
|
|
196
|
+
class RagQueryPrompt extends PromptContext {
|
|
197
|
+
async execute(args: Record<string, string>): Promise<GetPromptResult> {
|
|
198
|
+
const kb = this.get(KNOWLEDGE_BASE);
|
|
199
|
+
const maxSources = parseInt(args.maxSources ?? '3', 10);
|
|
200
|
+
const sources = await kb.search(args.question, maxSources);
|
|
201
|
+
|
|
202
|
+
const contextBlock = sources.map((s, i) => `### Source ${i + 1}: ${s.title}\n${s.content}`).join('\n\n');
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
messages: [
|
|
206
|
+
{
|
|
207
|
+
role: 'user',
|
|
208
|
+
content: {
|
|
209
|
+
type: 'text',
|
|
210
|
+
text: `Answer the following question using only the provided sources. If the sources do not contain enough information, say so clearly.\n\n**Question:** ${args.question}\n\n---\n\n${contextBlock}`,
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Embedding Resources in Prompts
|
|
220
|
+
|
|
221
|
+
Include MCP resource content directly in prompt messages using the `resource` content type:
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
@Prompt({
|
|
225
|
+
name: 'analyze-config',
|
|
226
|
+
description: 'Analyze application configuration and suggest improvements',
|
|
227
|
+
arguments: [{ name: 'configUri', description: 'URI of the config resource to analyze', required: true }],
|
|
228
|
+
})
|
|
229
|
+
class AnalyzeConfigPrompt extends PromptContext {
|
|
230
|
+
async execute(args: Record<string, string>): Promise<GetPromptResult> {
|
|
231
|
+
return {
|
|
232
|
+
messages: [
|
|
233
|
+
{
|
|
234
|
+
role: 'user',
|
|
235
|
+
content: {
|
|
236
|
+
type: 'resource',
|
|
237
|
+
resource: {
|
|
238
|
+
uri: args.configUri,
|
|
239
|
+
mimeType: 'application/json',
|
|
240
|
+
text: '(resource content will be resolved by the client)',
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
role: 'user',
|
|
246
|
+
content: {
|
|
247
|
+
type: 'text',
|
|
248
|
+
text: 'Analyze the configuration above. Identify potential issues, security concerns, and suggest improvements.',
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
],
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## Function-Style Builder
|
|
258
|
+
|
|
259
|
+
For simple prompts that do not need a class, use the `prompt()` function builder:
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
import { prompt } from '@frontmcp/sdk';
|
|
263
|
+
import { GetPromptResult } from '@frontmcp/protocol';
|
|
264
|
+
|
|
265
|
+
const TranslatePrompt = prompt({
|
|
266
|
+
name: 'translate',
|
|
267
|
+
description: 'Translate text between languages',
|
|
268
|
+
arguments: [
|
|
269
|
+
{ name: 'text', description: 'Text to translate', required: true },
|
|
270
|
+
{ name: 'from', description: 'Source language', required: true },
|
|
271
|
+
{ name: 'to', description: 'Target language', required: true },
|
|
272
|
+
],
|
|
273
|
+
})(
|
|
274
|
+
(args): GetPromptResult => ({
|
|
275
|
+
messages: [
|
|
276
|
+
{
|
|
277
|
+
role: 'user',
|
|
278
|
+
content: {
|
|
279
|
+
type: 'text',
|
|
280
|
+
text: `Translate the following text from ${args?.from} to ${args?.to}. Provide only the translation, no explanations.\n\nText: ${args?.text}`,
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
],
|
|
284
|
+
}),
|
|
285
|
+
);
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Register it the same way as a class prompt: `prompts: [TranslatePrompt]`.
|
|
289
|
+
|
|
290
|
+
## Error Handling
|
|
291
|
+
|
|
292
|
+
Use `this.fail()` to abort prompt execution. Missing required arguments are caught automatically before `execute()` runs.
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
@Prompt({
|
|
296
|
+
name: 'generate-tests',
|
|
297
|
+
description: 'Generate test cases for a function',
|
|
298
|
+
arguments: [
|
|
299
|
+
{ name: 'functionCode', description: 'The function to test', required: true },
|
|
300
|
+
{ name: 'framework', description: 'Test framework (jest, mocha, vitest)', required: true },
|
|
301
|
+
],
|
|
302
|
+
})
|
|
303
|
+
class GenerateTestsPrompt extends PromptContext {
|
|
304
|
+
async execute(args: Record<string, string>): Promise<GetPromptResult> {
|
|
305
|
+
const validFrameworks = ['jest', 'mocha', 'vitest'];
|
|
306
|
+
if (!validFrameworks.includes(args.framework)) {
|
|
307
|
+
this.fail(new Error(`Unsupported test framework: "${args.framework}". Supported: ${validFrameworks.join(', ')}`));
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return {
|
|
311
|
+
messages: [
|
|
312
|
+
{
|
|
313
|
+
role: 'user',
|
|
314
|
+
content: {
|
|
315
|
+
type: 'text',
|
|
316
|
+
text: `Write comprehensive ${args.framework} test cases for the following function. Include edge cases, error handling, and boundary conditions.\n\n\`\`\`\n${args.functionCode}\n\`\`\``,
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
],
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
## Stage Tracking
|
|
326
|
+
|
|
327
|
+
Use `this.mark()` for debugging and observability in complex prompts:
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
@Prompt({
|
|
331
|
+
name: 'research-report',
|
|
332
|
+
description: 'Generate a structured research report prompt',
|
|
333
|
+
arguments: [
|
|
334
|
+
{ name: 'topic', description: 'Research topic', required: true },
|
|
335
|
+
{ name: 'depth', description: 'Report depth: brief, standard, or comprehensive', required: false },
|
|
336
|
+
],
|
|
337
|
+
})
|
|
338
|
+
class ResearchReportPrompt extends PromptContext {
|
|
339
|
+
async execute(args: Record<string, string>): Promise<GetPromptResult> {
|
|
340
|
+
this.mark('build-outline');
|
|
341
|
+
const depth = args.depth ?? 'standard';
|
|
342
|
+
const outline = this.buildOutline(depth);
|
|
343
|
+
|
|
344
|
+
this.mark('compose-messages');
|
|
345
|
+
return {
|
|
346
|
+
messages: [
|
|
347
|
+
{
|
|
348
|
+
role: 'user',
|
|
349
|
+
content: {
|
|
350
|
+
type: 'text',
|
|
351
|
+
text: `Write a ${depth} research report on "${args.topic}".\n\nFollow this structure:\n${outline}\n\nInclude citations where possible and maintain an objective, academic tone.`,
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
],
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
private buildOutline(depth: string): string {
|
|
359
|
+
const sections = ['Introduction', 'Background', 'Key Findings'];
|
|
360
|
+
if (depth === 'standard' || depth === 'comprehensive') {
|
|
361
|
+
sections.push('Analysis', 'Discussion');
|
|
362
|
+
}
|
|
363
|
+
if (depth === 'comprehensive') {
|
|
364
|
+
sections.push('Methodology', 'Limitations', 'Future Research');
|
|
365
|
+
}
|
|
366
|
+
sections.push('Conclusion');
|
|
367
|
+
return sections.map((s, i) => `${i + 1}. ${s}`).join('\n');
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
## Registration
|
|
373
|
+
|
|
374
|
+
Add prompt classes (or function-style prompts) to the `prompts` array in `@App`.
|
|
375
|
+
|
|
376
|
+
```typescript
|
|
377
|
+
import { FrontMcp, App } from '@frontmcp/sdk';
|
|
378
|
+
|
|
379
|
+
@App({
|
|
380
|
+
name: 'my-app',
|
|
381
|
+
prompts: [CodeReviewPrompt, DebugSessionPrompt, TranslatePrompt],
|
|
382
|
+
})
|
|
383
|
+
class MyApp {}
|
|
384
|
+
|
|
385
|
+
@FrontMcp({
|
|
386
|
+
info: { name: 'my-server', version: '1.0.0' },
|
|
387
|
+
apps: [MyApp],
|
|
388
|
+
})
|
|
389
|
+
class MyServer {}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
## Nx Generator
|
|
393
|
+
|
|
394
|
+
Scaffold a new prompt using the Nx generator:
|
|
395
|
+
|
|
396
|
+
```bash
|
|
397
|
+
nx generate @frontmcp/nx:prompt
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
This creates the prompt file, spec file, and updates barrel exports.
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-provider
|
|
3
|
+
description: Create dependency injection providers for database connections, API clients, and singleton services. Use when tools and resources need shared services, DB pools, or configuration objects.
|
|
4
|
+
tags: [provider, di, dependency-injection, singleton, database, service]
|
|
5
|
+
parameters:
|
|
6
|
+
- name: name
|
|
7
|
+
description: Provider name
|
|
8
|
+
type: string
|
|
9
|
+
required: true
|
|
10
|
+
examples:
|
|
11
|
+
- scenario: Create a database connection pool provider
|
|
12
|
+
expected-outcome: Singleton DB pool injectable into all tools via this.get()
|
|
13
|
+
- scenario: Create a config provider from environment variables
|
|
14
|
+
expected-outcome: Type-safe config object available in any context
|
|
15
|
+
priority: 8
|
|
16
|
+
visibility: both
|
|
17
|
+
license: Apache-2.0
|
|
18
|
+
metadata:
|
|
19
|
+
docs: https://docs.agentfront.dev/frontmcp/extensibility/providers
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
# Creating Providers (Dependency Injection)
|
|
23
|
+
|
|
24
|
+
Providers are singleton services — database pools, API clients, config objects — that tools, resources, prompts, and agents can access via `this.get(token)`.
|
|
25
|
+
|
|
26
|
+
## When to Use
|
|
27
|
+
|
|
28
|
+
Create a provider when:
|
|
29
|
+
|
|
30
|
+
- Multiple tools need the same database connection pool
|
|
31
|
+
- You have API clients that should be shared (not recreated per request)
|
|
32
|
+
- Configuration values should be centralized and type-safe
|
|
33
|
+
- You need lifecycle management (initialize on startup, cleanup on shutdown)
|
|
34
|
+
|
|
35
|
+
## Step 1: Define a Token
|
|
36
|
+
|
|
37
|
+
Tokens identify providers in the DI container:
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import type { Token } from '@frontmcp/di';
|
|
41
|
+
|
|
42
|
+
// Define a typed token
|
|
43
|
+
interface DatabaseService {
|
|
44
|
+
query(sql: string, params?: unknown[]): Promise<unknown[]>;
|
|
45
|
+
close(): Promise<void>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const DB_TOKEN: Token<DatabaseService> = Symbol('DatabaseService');
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Step 2: Create the Provider
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { Provider } from '@frontmcp/sdk';
|
|
55
|
+
import { createPool, Pool } from 'your-db-driver';
|
|
56
|
+
|
|
57
|
+
@Provider({ name: 'DatabaseProvider' })
|
|
58
|
+
class DatabaseProvider implements DatabaseService {
|
|
59
|
+
private pool!: Pool;
|
|
60
|
+
|
|
61
|
+
async onInit() {
|
|
62
|
+
// Called once when server starts
|
|
63
|
+
this.pool = await createPool({
|
|
64
|
+
connectionString: process.env.DATABASE_URL,
|
|
65
|
+
max: 20,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async query(sql: string, params?: unknown[]) {
|
|
70
|
+
return this.pool.query(sql, params);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async onDestroy() {
|
|
74
|
+
// Called when server shuts down
|
|
75
|
+
await this.pool.end();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Step 3: Register in @App or @FrontMcp
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
@App({
|
|
84
|
+
name: 'MyApp',
|
|
85
|
+
providers: [DatabaseProvider], // App-scoped provider
|
|
86
|
+
tools: [QueryTool, InsertTool],
|
|
87
|
+
})
|
|
88
|
+
class MyApp {}
|
|
89
|
+
|
|
90
|
+
// OR at server level (shared across all apps)
|
|
91
|
+
@FrontMcp({
|
|
92
|
+
info: { name: 'my-server', version: '1.0.0' },
|
|
93
|
+
apps: [MyApp],
|
|
94
|
+
providers: [DatabaseProvider], // Server-scoped provider
|
|
95
|
+
})
|
|
96
|
+
class Server {}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Step 4: Use in Tools
|
|
100
|
+
|
|
101
|
+
Access providers via `this.get(token)` in any context (ToolContext, ResourceContext, PromptContext, AgentContext):
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
@Tool({
|
|
105
|
+
name: 'query_users',
|
|
106
|
+
description: 'Query users from the database',
|
|
107
|
+
inputSchema: {
|
|
108
|
+
filter: z.string().optional(),
|
|
109
|
+
limit: z.number().default(10),
|
|
110
|
+
},
|
|
111
|
+
outputSchema: {
|
|
112
|
+
users: z.array(z.object({ id: z.string(), name: z.string(), email: z.string() })),
|
|
113
|
+
},
|
|
114
|
+
})
|
|
115
|
+
class QueryUsersTool extends ToolContext {
|
|
116
|
+
async execute(input: { filter?: string; limit: number }) {
|
|
117
|
+
const db = this.get(DB_TOKEN); // Get the database provider
|
|
118
|
+
const users = await db.query('SELECT id, name, email FROM users WHERE name LIKE $1 LIMIT $2', [
|
|
119
|
+
`%${input.filter ?? ''}%`,
|
|
120
|
+
input.limit,
|
|
121
|
+
]);
|
|
122
|
+
return { users };
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Safe Access
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
// Throws if not registered
|
|
131
|
+
const db = this.get(DB_TOKEN);
|
|
132
|
+
|
|
133
|
+
// Returns undefined if not registered
|
|
134
|
+
const db = this.tryGet(DB_TOKEN);
|
|
135
|
+
if (!db) {
|
|
136
|
+
this.fail(new Error('Database not configured'));
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Common Provider Patterns
|
|
141
|
+
|
|
142
|
+
### Configuration Provider
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
interface AppConfig {
|
|
146
|
+
apiBaseUrl: string;
|
|
147
|
+
maxRetries: number;
|
|
148
|
+
debug: boolean;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const CONFIG_TOKEN: Token<AppConfig> = Symbol('AppConfig');
|
|
152
|
+
|
|
153
|
+
@Provider({ name: 'ConfigProvider' })
|
|
154
|
+
class ConfigProvider implements AppConfig {
|
|
155
|
+
readonly apiBaseUrl = process.env.API_BASE_URL ?? 'https://api.example.com';
|
|
156
|
+
readonly maxRetries = Number(process.env.MAX_RETRIES ?? 3);
|
|
157
|
+
readonly debug = process.env.DEBUG === 'true';
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### HTTP API Client Provider
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
interface ApiClient {
|
|
165
|
+
get(path: string): Promise<unknown>;
|
|
166
|
+
post(path: string, body: unknown): Promise<unknown>;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const API_TOKEN: Token<ApiClient> = Symbol('ApiClient');
|
|
170
|
+
|
|
171
|
+
@Provider({ name: 'ApiClientProvider' })
|
|
172
|
+
class ApiClientProvider implements ApiClient {
|
|
173
|
+
private baseUrl!: string;
|
|
174
|
+
private apiKey!: string;
|
|
175
|
+
|
|
176
|
+
async onInit() {
|
|
177
|
+
this.baseUrl = process.env.API_URL!;
|
|
178
|
+
this.apiKey = process.env.API_KEY!;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async get(path: string) {
|
|
182
|
+
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
183
|
+
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
184
|
+
});
|
|
185
|
+
return res.json();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async post(path: string, body: unknown) {
|
|
189
|
+
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
190
|
+
method: 'POST',
|
|
191
|
+
headers: { Authorization: `Bearer ${this.apiKey}`, 'Content-Type': 'application/json' },
|
|
192
|
+
body: JSON.stringify(body),
|
|
193
|
+
});
|
|
194
|
+
return res.json();
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Cache Provider
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
const CACHE_TOKEN: Token<Map<string, unknown>> = Symbol('Cache');
|
|
203
|
+
|
|
204
|
+
@Provider({ name: 'CacheProvider' })
|
|
205
|
+
class CacheProvider extends Map<string, unknown> {
|
|
206
|
+
// Map is already a valid provider - no lifecycle needed
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Provider Lifecycle
|
|
211
|
+
|
|
212
|
+
| Method | When Called | Use For |
|
|
213
|
+
| ------------- | ----------------------- | -------------------------------- |
|
|
214
|
+
| `onInit()` | Server startup (async) | Open connections, load config |
|
|
215
|
+
| `onDestroy()` | Server shutdown (async) | Close connections, flush buffers |
|
|
216
|
+
|
|
217
|
+
Providers are initialized in dependency order — if Provider A depends on Provider B, B initializes first.
|
|
218
|
+
|
|
219
|
+
## Nx Generator
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
nx generate @frontmcp/nx:provider my-provider --project=my-app
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Verification
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
# Start server — providers initialize on startup
|
|
229
|
+
frontmcp dev
|
|
230
|
+
|
|
231
|
+
# Call a tool that uses the provider
|
|
232
|
+
# If provider fails to init, you'll see an error at startup
|
|
233
|
+
```
|