@frontmcp/skills 0.0.1 → 1.0.0-beta.11
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/README.md +2 -2
- package/catalog/TEMPLATE.md +58 -13
- package/catalog/frontmcp-config/SKILL.md +156 -0
- package/catalog/{auth/configure-auth/references/auth-modes.md → frontmcp-config/references/configure-auth-modes.md} +5 -0
- package/catalog/frontmcp-config/references/configure-auth.md +243 -0
- package/catalog/frontmcp-config/references/configure-elicitation.md +183 -0
- package/catalog/frontmcp-config/references/configure-http.md +210 -0
- package/catalog/frontmcp-config/references/configure-session.md +210 -0
- package/catalog/{config/configure-throttle/references/guard-config.md → frontmcp-config/references/configure-throttle-guard-config.md} +5 -0
- package/catalog/frontmcp-config/references/configure-throttle.md +234 -0
- package/catalog/{config/configure-transport/references/protocol-presets.md → frontmcp-config/references/configure-transport-protocol-presets.md} +5 -0
- package/catalog/frontmcp-config/references/configure-transport.md +200 -0
- package/catalog/frontmcp-config/references/setup-redis.md +9 -0
- package/catalog/frontmcp-config/references/setup-sqlite.md +9 -0
- package/catalog/frontmcp-deployment/SKILL.md +152 -0
- package/catalog/frontmcp-deployment/references/build-for-browser.md +143 -0
- package/catalog/frontmcp-deployment/references/build-for-cli.md +191 -0
- package/catalog/{deployment/build-for-sdk/SKILL.md → frontmcp-deployment/references/build-for-sdk.md} +66 -20
- package/catalog/frontmcp-deployment/references/deploy-to-cloudflare.md +218 -0
- package/catalog/{deployment/deploy-to-lambda/SKILL.md → frontmcp-deployment/references/deploy-to-lambda.md} +77 -59
- package/catalog/{deployment/deploy-to-node/references/Dockerfile.example → frontmcp-deployment/references/deploy-to-node-dockerfile.md} +18 -4
- package/catalog/{deployment/deploy-to-node/SKILL.md → frontmcp-deployment/references/deploy-to-node.md} +69 -36
- package/catalog/frontmcp-deployment/references/deploy-to-vercel-config.md +65 -0
- package/catalog/frontmcp-deployment/references/deploy-to-vercel.md +229 -0
- package/catalog/frontmcp-development/SKILL.md +126 -0
- package/catalog/frontmcp-development/references/create-adapter.md +170 -0
- package/catalog/{development/create-agent/references/llm-config.md → frontmcp-development/references/create-agent-llm-config.md} +10 -5
- package/catalog/{development/create-agent/SKILL.md → frontmcp-development/references/create-agent.md} +83 -40
- package/catalog/{development/create-job/SKILL.md → frontmcp-development/references/create-job.md} +62 -15
- package/catalog/{plugins/create-plugin-hooks/SKILL.md → frontmcp-development/references/create-plugin-hooks.md} +100 -7
- package/catalog/frontmcp-development/references/create-plugin.md +506 -0
- package/catalog/{development/create-prompt/SKILL.md → frontmcp-development/references/create-prompt.md} +65 -22
- package/catalog/{development/create-provider/SKILL.md → frontmcp-development/references/create-provider.md} +63 -23
- package/catalog/{development/create-resource/SKILL.md → frontmcp-development/references/create-resource.md} +148 -26
- package/catalog/{development/create-skill-with-tools/SKILL.md → frontmcp-development/references/create-skill-with-tools.md} +174 -20
- package/catalog/{development/create-skill/SKILL.md → frontmcp-development/references/create-skill.md} +114 -28
- package/catalog/{development/create-tool/references/tool-annotations.md → frontmcp-development/references/create-tool-annotations.md} +5 -0
- package/catalog/{development/create-tool/references/output-schema-types.md → frontmcp-development/references/create-tool-output-schema-types.md} +5 -0
- package/catalog/{development/create-tool/SKILL.md → frontmcp-development/references/create-tool.md} +172 -23
- package/catalog/{development/create-workflow/SKILL.md → frontmcp-development/references/create-workflow.md} +61 -14
- package/catalog/frontmcp-development/references/decorators-guide.md +754 -0
- package/catalog/frontmcp-development/references/official-adapters.md +199 -0
- package/catalog/{plugins/official-plugins/SKILL.md → frontmcp-development/references/official-plugins.md} +97 -27
- package/catalog/frontmcp-extensibility/SKILL.md +103 -0
- package/catalog/frontmcp-extensibility/references/vectoriadb.md +289 -0
- package/catalog/frontmcp-guides/SKILL.md +420 -0
- package/catalog/frontmcp-guides/references/example-knowledge-base.md +641 -0
- package/catalog/frontmcp-guides/references/example-task-manager.md +517 -0
- package/catalog/frontmcp-guides/references/example-weather-api.md +297 -0
- package/catalog/frontmcp-production-readiness/SKILL.md +98 -0
- package/catalog/frontmcp-production-readiness/references/common-checklist.md +156 -0
- package/catalog/frontmcp-production-readiness/references/production-browser.md +46 -0
- package/catalog/frontmcp-production-readiness/references/production-cli-binary.md +62 -0
- package/catalog/frontmcp-production-readiness/references/production-cli-daemon.md +61 -0
- package/catalog/frontmcp-production-readiness/references/production-cloudflare.md +52 -0
- package/catalog/frontmcp-production-readiness/references/production-lambda.md +53 -0
- package/catalog/frontmcp-production-readiness/references/production-node-sdk.md +66 -0
- package/catalog/frontmcp-production-readiness/references/production-node-server.md +61 -0
- package/catalog/frontmcp-production-readiness/references/production-vercel.md +52 -0
- package/catalog/frontmcp-setup/SKILL.md +132 -0
- package/catalog/frontmcp-setup/references/frontmcp-skills-usage.md +280 -0
- package/catalog/{setup/multi-app-composition/SKILL.md → frontmcp-setup/references/multi-app-composition.md} +66 -19
- package/catalog/{setup/nx-workflow/SKILL.md → frontmcp-setup/references/nx-workflow.md} +79 -17
- package/catalog/frontmcp-setup/references/project-structure-nx.md +251 -0
- package/catalog/frontmcp-setup/references/project-structure-standalone.md +217 -0
- package/catalog/frontmcp-setup/references/readme-guide.md +226 -0
- package/catalog/{setup/setup-project/SKILL.md → frontmcp-setup/references/setup-project.md} +63 -58
- package/catalog/{setup/setup-redis/SKILL.md → frontmcp-setup/references/setup-redis.md} +60 -82
- package/catalog/{setup/setup-sqlite/SKILL.md → frontmcp-setup/references/setup-sqlite.md} +65 -72
- package/catalog/frontmcp-testing/SKILL.md +135 -0
- package/catalog/{testing/setup-testing/SKILL.md → frontmcp-testing/references/setup-testing.md} +79 -63
- package/catalog/{testing/setup-testing → frontmcp-testing}/references/test-auth.md +5 -0
- package/catalog/{testing/setup-testing → frontmcp-testing}/references/test-browser-build.md +5 -0
- package/catalog/{testing/setup-testing → frontmcp-testing}/references/test-cli-binary.md +5 -0
- package/catalog/{testing/setup-testing → frontmcp-testing}/references/test-direct-client.md +5 -0
- package/catalog/{testing/setup-testing → frontmcp-testing}/references/test-e2e-handler.md +5 -0
- package/catalog/{testing/setup-testing → frontmcp-testing}/references/test-tool-unit.md +6 -0
- package/catalog/skills-manifest.json +337 -382
- package/package.json +2 -2
- package/src/index.d.ts +1 -1
- package/src/index.js.map +1 -1
- package/src/loader.js +0 -1
- package/src/loader.js.map +1 -1
- package/src/manifest.d.ts +15 -3
- package/src/manifest.js +3 -3
- package/src/manifest.js.map +1 -1
- package/catalog/adapters/create-adapter/SKILL.md +0 -127
- package/catalog/adapters/official-adapters/SKILL.md +0 -136
- package/catalog/auth/configure-auth/SKILL.md +0 -250
- package/catalog/auth/configure-session/SKILL.md +0 -201
- package/catalog/config/configure-elicitation/SKILL.md +0 -136
- package/catalog/config/configure-http/SKILL.md +0 -167
- package/catalog/config/configure-throttle/SKILL.md +0 -189
- package/catalog/config/configure-transport/SKILL.md +0 -151
- package/catalog/deployment/build-for-browser/SKILL.md +0 -95
- package/catalog/deployment/build-for-cli/SKILL.md +0 -100
- package/catalog/deployment/deploy-to-cloudflare/SKILL.md +0 -192
- package/catalog/deployment/deploy-to-vercel/SKILL.md +0 -196
- package/catalog/deployment/deploy-to-vercel/references/vercel.json.example +0 -60
- package/catalog/development/decorators-guide/SKILL.md +0 -598
- package/catalog/plugins/create-plugin/SKILL.md +0 -336
- package/catalog/setup/frontmcp-skills-usage/SKILL.md +0 -200
- package/catalog/setup/project-structure-nx/SKILL.md +0 -186
- package/catalog/setup/project-structure-standalone/SKILL.md +0 -153
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-plugin
|
|
3
|
+
description: Build plugins with providers, context extensions, lifecycle hooks, and contributed tools
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Create a FrontMCP Plugin
|
|
7
|
+
|
|
8
|
+
This skill covers building custom plugins for FrontMCP and using all 6 official plugins. Plugins are modular units that extend server behavior through providers, context extensions, lifecycle hooks, and contributed tools/resources/prompts.
|
|
9
|
+
|
|
10
|
+
## When to Use This Skill
|
|
11
|
+
|
|
12
|
+
### Must Use
|
|
13
|
+
|
|
14
|
+
- Adding cross-cutting behavior (logging, caching, auth) that applies across multiple tools
|
|
15
|
+
- Extending `ExecutionContextBase` with new properties accessible via `this.propertyName` in tools
|
|
16
|
+
- Contributing injectable providers that tools or other plugins depend on
|
|
17
|
+
|
|
18
|
+
### Recommended
|
|
19
|
+
|
|
20
|
+
- Building a configurable module with runtime options using the `DynamicPlugin` pattern
|
|
21
|
+
- Extending the `@Tool` decorator metadata with custom fields (e.g., audit, approval)
|
|
22
|
+
- Composing multiple related providers, hooks, and tools into a single installable unit
|
|
23
|
+
|
|
24
|
+
### Skip When
|
|
25
|
+
|
|
26
|
+
- You only need lifecycle hooks without providers or context extensions (see `create-plugin-hooks`)
|
|
27
|
+
- You want to use an existing official plugin (see `official-plugins`)
|
|
28
|
+
- You need to generate tools from an external API spec (see `create-adapter`)
|
|
29
|
+
|
|
30
|
+
> **Decision:** Use this skill when you need a reusable module that bundles providers, context extensions, or contributed entries and registers them via `@Plugin`.
|
|
31
|
+
|
|
32
|
+
## Plugin Decorator Signature
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
function Plugin(metadata: PluginMetadata): ClassDecorator;
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The `PluginMetadata` interface:
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
interface PluginMetadata {
|
|
42
|
+
name: string;
|
|
43
|
+
id?: string;
|
|
44
|
+
description?: string;
|
|
45
|
+
providers?: ProviderType[];
|
|
46
|
+
exports?: ProviderType[];
|
|
47
|
+
plugins?: PluginType[];
|
|
48
|
+
adapters?: AdapterType[];
|
|
49
|
+
tools?: ToolType[];
|
|
50
|
+
resources?: ResourceType[];
|
|
51
|
+
prompts?: PromptType[];
|
|
52
|
+
skills?: SkillType[];
|
|
53
|
+
scope?: 'app' | 'server'; // default: 'app'
|
|
54
|
+
contextExtensions?: ContextExtension[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
interface ContextExtension {
|
|
58
|
+
property: string;
|
|
59
|
+
token: Token<unknown>;
|
|
60
|
+
errorMessage?: string;
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## DynamicPlugin Base Class
|
|
65
|
+
|
|
66
|
+
For plugins that accept runtime configuration, extend `DynamicPlugin<TOptions, TInput>`:
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
abstract class DynamicPlugin<TOptions extends object, TInput extends object = TOptions> {
|
|
70
|
+
static dynamicProviders?(options: any): readonly ProviderType[];
|
|
71
|
+
static init<TThis>(options: InitOptions<TInput>): PluginReturn<TOptions>;
|
|
72
|
+
get<T>(token: Reference<T>): T;
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
- `TOptions` -- the resolved options type (after parsing/defaults)
|
|
77
|
+
- `TInput` -- the input type users provide to `init()` (may have optional fields)
|
|
78
|
+
- `init()` creates a provider entry for use in `plugins: [...]` arrays
|
|
79
|
+
- `dynamicProviders()` returns providers computed from the input options
|
|
80
|
+
|
|
81
|
+
## Quick Start: Minimal DynamicPlugin
|
|
82
|
+
|
|
83
|
+
The simplest working plugin needs three files: a provider, the plugin class, and registration.
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
// my-greeter.provider.ts
|
|
87
|
+
import { Provider } from '@frontmcp/sdk';
|
|
88
|
+
|
|
89
|
+
@Provider()
|
|
90
|
+
export class GreeterService {
|
|
91
|
+
greet(name: string): string {
|
|
92
|
+
return `Hello, ${name}`;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
// my-greeter.plugin.ts
|
|
99
|
+
import { Plugin, DynamicPlugin, ProviderType } from '@frontmcp/sdk';
|
|
100
|
+
import { GreeterService } from './providers/my-greeter.provider';
|
|
101
|
+
|
|
102
|
+
@Plugin({ name: 'greeter', exports: [GreeterService] })
|
|
103
|
+
export default class GreeterPlugin extends DynamicPlugin<{ prefix: string }> {
|
|
104
|
+
static override dynamicProviders(opts: { prefix: string }): ProviderType[] {
|
|
105
|
+
return [{ provide: GreeterService, useFactory: () => new GreeterService() }];
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
// server.ts
|
|
112
|
+
import { FrontMcp } from '@frontmcp/sdk';
|
|
113
|
+
import GreeterPlugin from './plugins/my-greeter.plugin';
|
|
114
|
+
|
|
115
|
+
@FrontMcp({
|
|
116
|
+
info: { name: 'my-server', version: '1.0.0' },
|
|
117
|
+
plugins: [GreeterPlugin.init({ prefix: 'Hi' })],
|
|
118
|
+
})
|
|
119
|
+
class MyServer {}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Step 1: Create a Simple Plugin
|
|
123
|
+
|
|
124
|
+
The minimal plugin only needs a name:
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
import { Plugin } from '@frontmcp/sdk';
|
|
128
|
+
|
|
129
|
+
@Plugin({
|
|
130
|
+
name: 'audit-log',
|
|
131
|
+
description: 'Logs tool executions for audit compliance',
|
|
132
|
+
})
|
|
133
|
+
export default class AuditLogPlugin {}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Register it in your server:
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
import { FrontMcp, App } from '@frontmcp/sdk';
|
|
140
|
+
import AuditLogPlugin from './plugins/audit-log.plugin';
|
|
141
|
+
|
|
142
|
+
@App({ name: 'MyApp' })
|
|
143
|
+
class MyApp {}
|
|
144
|
+
|
|
145
|
+
@FrontMcp({
|
|
146
|
+
info: { name: 'my-server', version: '1.0.0' },
|
|
147
|
+
apps: [MyApp],
|
|
148
|
+
plugins: [AuditLogPlugin],
|
|
149
|
+
tools: [
|
|
150
|
+
/* your tools */
|
|
151
|
+
],
|
|
152
|
+
})
|
|
153
|
+
class MyServer {}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Step 2: Add Providers
|
|
157
|
+
|
|
158
|
+
Plugins contribute injectable services via `providers`:
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
import { Plugin, Provider } from '@frontmcp/sdk';
|
|
162
|
+
import type { Token } from '@frontmcp/sdk';
|
|
163
|
+
|
|
164
|
+
export const AuditLoggerToken: Token<AuditLogger> = Symbol('AuditLogger');
|
|
165
|
+
|
|
166
|
+
@Provider()
|
|
167
|
+
class AuditLogger {
|
|
168
|
+
async logToolCall(toolName: string, userId: string, input: unknown): Promise<void> {
|
|
169
|
+
console.log(`[AUDIT] ${userId} called ${toolName}`, input);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
@Plugin({
|
|
174
|
+
name: 'audit-log',
|
|
175
|
+
description: 'Logs tool executions for audit compliance',
|
|
176
|
+
providers: [{ provide: AuditLoggerToken, useClass: AuditLogger }],
|
|
177
|
+
exports: [AuditLogger],
|
|
178
|
+
})
|
|
179
|
+
export default class AuditLogPlugin {}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Step 3: Add Context Extensions
|
|
183
|
+
|
|
184
|
+
Context extensions add properties to `ExecutionContextBase` so tools access plugin services via `this.propertyName`. Two parts are required:
|
|
185
|
+
|
|
186
|
+
### Part A: TypeScript Type Declaration (Module Augmentation)
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
// audit-log.context-extension.ts
|
|
190
|
+
import type { AuditLogger } from './audit-logger';
|
|
191
|
+
|
|
192
|
+
declare module '@frontmcp/sdk' {
|
|
193
|
+
interface ExecutionContextBase {
|
|
194
|
+
/** Audit logger provided by AuditLogPlugin */
|
|
195
|
+
readonly auditLog: AuditLogger;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Part B: Register via Plugin Metadata
|
|
201
|
+
|
|
202
|
+
The SDK handles runtime installation when you declare `contextExtensions` in plugin metadata. Do not modify `ExecutionContextBase.prototype` directly.
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
import { Plugin } from '@frontmcp/sdk';
|
|
206
|
+
import type { Token } from '@frontmcp/sdk';
|
|
207
|
+
import './audit-log.context-extension'; // Import for type augmentation side effect
|
|
208
|
+
|
|
209
|
+
export const AuditLoggerToken: Token<AuditLogger> = Symbol('AuditLogger');
|
|
210
|
+
|
|
211
|
+
@Plugin({
|
|
212
|
+
name: 'audit-log',
|
|
213
|
+
description: 'Logs tool executions for audit compliance',
|
|
214
|
+
providers: [{ provide: AuditLoggerToken, useClass: AuditLogger }],
|
|
215
|
+
contextExtensions: [
|
|
216
|
+
{
|
|
217
|
+
property: 'auditLog',
|
|
218
|
+
token: AuditLoggerToken,
|
|
219
|
+
errorMessage: 'AuditLogPlugin is not installed. Add it to your @FrontMcp plugins array.',
|
|
220
|
+
},
|
|
221
|
+
],
|
|
222
|
+
})
|
|
223
|
+
export default class AuditLogPlugin {}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Now tools can use `this.auditLog`:
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
import { Tool, ToolContext } from '@frontmcp/sdk';
|
|
230
|
+
|
|
231
|
+
@Tool({ name: 'delete_record' })
|
|
232
|
+
class DeleteRecordTool extends ToolContext {
|
|
233
|
+
async execute(input: { recordId: string }) {
|
|
234
|
+
await this.auditLog.logToolCall('delete_record', this.scope.userId, input);
|
|
235
|
+
return { deleted: true };
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Step 4: Create a Configurable Plugin with DynamicPlugin
|
|
241
|
+
|
|
242
|
+
For plugins that accept runtime options, extend `DynamicPlugin`:
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
import { Plugin, DynamicPlugin, ProviderType } from '@frontmcp/sdk';
|
|
246
|
+
import type { Token } from '@frontmcp/sdk';
|
|
247
|
+
|
|
248
|
+
export interface MyPluginOptions {
|
|
249
|
+
endpoint: string;
|
|
250
|
+
refreshIntervalMs: number;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export type MyPluginOptionsInput = Omit<MyPluginOptions, 'refreshIntervalMs'> & {
|
|
254
|
+
refreshIntervalMs?: number;
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
export const MyServiceToken: Token<MyService> = Symbol('MyService');
|
|
258
|
+
|
|
259
|
+
@Plugin({
|
|
260
|
+
name: 'my-plugin',
|
|
261
|
+
description: 'A configurable plugin',
|
|
262
|
+
contextExtensions: [
|
|
263
|
+
{
|
|
264
|
+
property: 'myService',
|
|
265
|
+
token: MyServiceToken,
|
|
266
|
+
errorMessage: 'MyPlugin is not installed.',
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
})
|
|
270
|
+
export default class MyPlugin extends DynamicPlugin<MyPluginOptions, MyPluginOptionsInput> {
|
|
271
|
+
options: MyPluginOptions;
|
|
272
|
+
|
|
273
|
+
constructor(options: MyPluginOptionsInput = { endpoint: '' }) {
|
|
274
|
+
super();
|
|
275
|
+
this.options = { refreshIntervalMs: 30_000, ...options };
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
static override dynamicProviders(options: MyPluginOptionsInput): ProviderType[] {
|
|
279
|
+
return [
|
|
280
|
+
{
|
|
281
|
+
provide: MyServiceToken,
|
|
282
|
+
useFactory: () =>
|
|
283
|
+
new MyService({
|
|
284
|
+
refreshIntervalMs: 30_000,
|
|
285
|
+
...options,
|
|
286
|
+
}),
|
|
287
|
+
},
|
|
288
|
+
];
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Register with `init()`:
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
@FrontMcp({
|
|
297
|
+
info: { name: 'my-server', version: '1.0.0' },
|
|
298
|
+
apps: [MyApp],
|
|
299
|
+
plugins: [
|
|
300
|
+
MyPlugin.init({
|
|
301
|
+
endpoint: 'https://api.example.com',
|
|
302
|
+
refreshIntervalMs: 60_000,
|
|
303
|
+
}),
|
|
304
|
+
],
|
|
305
|
+
})
|
|
306
|
+
class MyServer {}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
## Step 5: Extend Metadata and Execution Context
|
|
310
|
+
|
|
311
|
+
FrontMCP provides two extension mechanisms for plugins: **metadata augmentation** (add fields to decorators) and **context extensions** (add properties to `this` in tools/resources/prompts).
|
|
312
|
+
|
|
313
|
+
### All Extensible Metadata Interfaces
|
|
314
|
+
|
|
315
|
+
Plugins can extend these `declare global` interfaces to add custom fields to any decorator:
|
|
316
|
+
|
|
317
|
+
| Interface | Decorator | Example Field |
|
|
318
|
+
| ---------------------------------------- | -------------------------- | ---------------------------- |
|
|
319
|
+
| `ExtendFrontMcpToolMetadata` | `@Tool({...})` | `audit: { enabled: true }` |
|
|
320
|
+
| `ExtendFrontMcpAgentMetadata` | `@Agent({...})` | Inherits from ToolMetadata |
|
|
321
|
+
| `ExtendFrontMcpResourceMetadata` | `@Resource({...})` | `cache: { ttl: 3600 }` |
|
|
322
|
+
| `ExtendFrontMcpResourceTemplateMetadata` | `@ResourceTemplate({...})` | `rateLimit: { max: 100 }` |
|
|
323
|
+
| `ExtendFrontMcpPromptMetadata` | `@Prompt({...})` | `category: 'onboarding'` |
|
|
324
|
+
| `ExtendFrontMcpJobMetadata` | `@Job({...})` | `priority: 'high'` |
|
|
325
|
+
| `ExtendFrontMcpWorkflowMetadata` | `@Workflow({...})` | `retryPolicy: 'exponential'` |
|
|
326
|
+
| `ExtendFrontMcpSkillMetadata` | `@Skill({...})` | `complexity: 'advanced'` |
|
|
327
|
+
| `ExtendFrontMcpLoggerMetadata` | Logger transports | `destination: 'sentry'` |
|
|
328
|
+
|
|
329
|
+
### Metadata Extension Pattern
|
|
330
|
+
|
|
331
|
+
Add custom fields to any decorator via `declare global`:
|
|
332
|
+
|
|
333
|
+
```typescript
|
|
334
|
+
// my-plugin.types.ts
|
|
335
|
+
declare global {
|
|
336
|
+
interface ExtendFrontMcpToolMetadata {
|
|
337
|
+
audit?: {
|
|
338
|
+
enabled: boolean;
|
|
339
|
+
level: 'info' | 'warn' | 'critical';
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Tools then use the custom field directly in the decorator:
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
@Tool({
|
|
349
|
+
name: 'delete_user',
|
|
350
|
+
audit: { enabled: true, level: 'critical' },
|
|
351
|
+
})
|
|
352
|
+
class DeleteUserTool extends ToolContext {
|
|
353
|
+
/* ... */
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
The same pattern works for any of the 9 interfaces above — replace `ExtendFrontMcpToolMetadata` with the target interface.
|
|
358
|
+
|
|
359
|
+
### Context Extension Pattern
|
|
360
|
+
|
|
361
|
+
Add properties like `this.myService` to execution contexts. This requires both TypeScript augmentation and runtime registration.
|
|
362
|
+
|
|
363
|
+
**Part A: TypeScript type declaration** (in a separate `.context-extension.ts` file):
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
// my-plugin.context-extension.ts
|
|
367
|
+
import type { MyService } from './providers/my-service.provider';
|
|
368
|
+
|
|
369
|
+
declare module '@frontmcp/sdk' {
|
|
370
|
+
interface ExecutionContextBase {
|
|
371
|
+
readonly myService: MyService;
|
|
372
|
+
}
|
|
373
|
+
// PromptContext has a separate prototype chain — augment it too
|
|
374
|
+
interface PromptContext {
|
|
375
|
+
readonly myService: MyService;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
**Part B: Runtime registration** (in the `@Plugin` metadata):
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
@Plugin({
|
|
384
|
+
name: 'my-plugin',
|
|
385
|
+
providers: [MyServiceProvider],
|
|
386
|
+
contextExtensions: [
|
|
387
|
+
{
|
|
388
|
+
property: 'myService',
|
|
389
|
+
token: MyServiceToken,
|
|
390
|
+
errorMessage: 'MyPlugin is not installed. Add it to your app plugins.',
|
|
391
|
+
},
|
|
392
|
+
],
|
|
393
|
+
})
|
|
394
|
+
export class MyPlugin extends DynamicPlugin<MyPluginOptions> {
|
|
395
|
+
/* ... */
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
The SDK installs lazy getters on both `ExecutionContextBase.prototype` and `PromptContext.prototype` that resolve the DI token on first access.
|
|
400
|
+
|
|
401
|
+
### ContextExtension Interface
|
|
402
|
+
|
|
403
|
+
Each entry in the `contextExtensions` array has these fields:
|
|
404
|
+
|
|
405
|
+
| Field | Type | Required | Description |
|
|
406
|
+
| -------------- | ---------------- | -------- | --------------------------------------------- |
|
|
407
|
+
| `property` | `string` | Yes | Property name accessible as `this.{property}` |
|
|
408
|
+
| `token` | `Token<unknown>` | Yes | DI token to resolve when property is accessed |
|
|
409
|
+
| `errorMessage` | `string` | No | Custom error when plugin is not installed |
|
|
410
|
+
|
|
411
|
+
### Side-Effect Import
|
|
412
|
+
|
|
413
|
+
The TypeScript augmentation file must be imported somewhere in your plugin's barrel export so the type declarations take effect:
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
// index.ts
|
|
417
|
+
import './my-plugin.context-extension'; // side-effect import for type augmentation
|
|
418
|
+
export { MyPlugin } from './my-plugin.plugin';
|
|
419
|
+
export { MyServiceToken } from './my-plugin.symbols';
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
## Official Plugins
|
|
425
|
+
|
|
426
|
+
For official plugin installation, configuration, and examples, see the **official-plugins** skill. FrontMCP provides 6 official plugins: CodeCall, Remember, Approval, Cache, Feature Flags, and Dashboard. Install individually or via `@frontmcp/plugins` (meta-package).
|
|
427
|
+
|
|
428
|
+
## Recommended Folder Structure
|
|
429
|
+
|
|
430
|
+
```text
|
|
431
|
+
plugins/
|
|
432
|
+
my-plugin/
|
|
433
|
+
index.ts # Barrel exports: plugin, tokens, types, side-effect import
|
|
434
|
+
my-plugin.plugin.ts # Plugin class extending DynamicPlugin
|
|
435
|
+
my-plugin.types.ts # Options Zod schema, TypeScript types, interfaces
|
|
436
|
+
my-plugin.symbols.ts # DI tokens: export const MY_TOKEN: Token<T> = Symbol('...')
|
|
437
|
+
my-plugin.context-extension.ts # Module augmentation (declare module '@frontmcp/sdk')
|
|
438
|
+
providers/
|
|
439
|
+
index.ts # Barrel for providers
|
|
440
|
+
my-service.provider.ts # @Provider class with business logic
|
|
441
|
+
my-store-memory.provider.ts # In-memory store implementation
|
|
442
|
+
my-store-redis.provider.ts # Redis store implementation (optional)
|
|
443
|
+
tools/ # Optional — only if plugin provides tools
|
|
444
|
+
index.ts
|
|
445
|
+
my-action.tool.ts # @Tool class registered via @Plugin({ tools: [...] })
|
|
446
|
+
__tests__/
|
|
447
|
+
my-plugin.spec.ts # Plugin tests
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
**Key files explained:**
|
|
451
|
+
|
|
452
|
+
- `index.ts` — Must import the context extension file as a side effect: `import './my-plugin.context-extension'`
|
|
453
|
+
- `symbols.ts` — All DI tokens in one place. Other files import from here, not from the plugin class
|
|
454
|
+
- `context-extension.ts` — `declare module '@frontmcp/sdk' { interface ExecutionContextBase { readonly myProp: T } }`
|
|
455
|
+
- `plugin.ts` — The `@Plugin()` decorated class. Lists `providers`, `exports`, `contextExtensions`, `tools`
|
|
456
|
+
|
|
457
|
+
## Common Patterns
|
|
458
|
+
|
|
459
|
+
| Pattern | Correct | Incorrect | Why |
|
|
460
|
+
| ------------------------------ | ---------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
|
|
461
|
+
| Context extension registration | `contextExtensions: [{ property: 'auditLog', token: AuditLoggerToken }]` in metadata | `Object.defineProperty(ExecutionContextBase.prototype, ...)` manually | SDK handles runtime installation; manual modification causes ordering issues |
|
|
462
|
+
| Type augmentation | `declare module '@frontmcp/sdk' { interface ExecutionContextBase { ... } }` in a separate file | Skipping the augmentation and casting `this` in tools | Without augmentation, TypeScript cannot type-check `this.auditLog` |
|
|
463
|
+
| Provider types | `Token<AuditLogger> = Symbol('AuditLogger')` with typed token | `provide: Symbol('AuditLogger')` without type annotation | Typed tokens enable compile-time DI resolution checking |
|
|
464
|
+
| Plugin scope | `scope: 'app'` (default) for app-scoped behavior | `scope: 'server'` when hooks should only apply to one app | Server scope fires hooks for all apps in a gateway; default to app |
|
|
465
|
+
| Dynamic options | Extend `DynamicPlugin<TOptions, TInput>` with `static dynamicProviders()` | Constructing providers in the constructor body | `dynamicProviders` runs before instantiation, enabling proper DI wiring |
|
|
466
|
+
|
|
467
|
+
## Verification Checklist
|
|
468
|
+
|
|
469
|
+
### Configuration
|
|
470
|
+
|
|
471
|
+
- [ ] `@Plugin` decorator has `name` and `description`
|
|
472
|
+
- [ ] Providers are listed in `providers` array with typed tokens
|
|
473
|
+
- [ ] Exported providers are listed in `exports` array
|
|
474
|
+
- [ ] Context extensions have `property`, `token`, and `errorMessage` fields
|
|
475
|
+
|
|
476
|
+
### Type Safety
|
|
477
|
+
|
|
478
|
+
- [ ] Module augmentation file exists with `declare module '@frontmcp/sdk'` block
|
|
479
|
+
- [ ] Augmented properties are `readonly` on `ExecutionContextBase`
|
|
480
|
+
- [ ] `PromptContext` is augmented alongside `ExecutionContextBase` for context extensions
|
|
481
|
+
- [ ] `declare global` block exists for each metadata extension interface used
|
|
482
|
+
- [ ] Augmentation file is imported (side-effect import) in the plugin barrel export
|
|
483
|
+
|
|
484
|
+
### Runtime
|
|
485
|
+
|
|
486
|
+
- [ ] Plugin is registered in `plugins` array of `@FrontMcp` or `@App`
|
|
487
|
+
- [ ] `this.propertyName` resolves correctly in tool contexts
|
|
488
|
+
- [ ] Missing plugin produces a clear error message (from `errorMessage`)
|
|
489
|
+
- [ ] Dynamic plugin options are validated in `dynamicProviders()`
|
|
490
|
+
|
|
491
|
+
## Troubleshooting
|
|
492
|
+
|
|
493
|
+
| Problem | Cause | Solution |
|
|
494
|
+
| ---------------------------------------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
495
|
+
| `this.auditLog` has type `any` or is unrecognized | Module augmentation file not imported | Add side-effect import: `import './audit-log.context-extension'` in plugin file |
|
|
496
|
+
| Circular dependency error at startup | Calling `installExtension()` at module top level | Remove manual installation; use `contextExtensions` metadata array instead |
|
|
497
|
+
| Provider not found in tool context | Provider not listed in plugin `exports` | Add the provider to both `providers` and `exports` arrays |
|
|
498
|
+
| Hooks fire for unrelated apps in gateway | Plugin `scope` set to `'server'` | Change to `scope: 'app'` (default) unless server-wide behavior is intended |
|
|
499
|
+
| `DynamicPlugin.init()` options ignored | Overriding constructor without calling `super()` | Ensure constructor calls `super()` and merges defaults properly |
|
|
500
|
+
| `ProviderNotRegisteredError` for context extension | Token in `contextExtensions` not in `providers` | Ensure the token used in `contextExtensions[].token` is registered in the plugin's `providers` array. Use `{ provide: MyToken, useClass: MyService }` or list the class directly. If using `dynamicProviders()`, return the provider there |
|
|
501
|
+
| Provider works in tools but not in context extension | Using class reference instead of Symbol token | Create a typed `Token<T> = Symbol('name')` in `symbols.ts`, use it in both `providers` and `contextExtensions`. Direct class references can fail if not constructable without dependencies |
|
|
502
|
+
|
|
503
|
+
## Reference
|
|
504
|
+
|
|
505
|
+
- [Plugin System Documentation](https://docs.agentfront.dev/frontmcp/plugins/creating-plugins)
|
|
506
|
+
- Related skills: `create-plugin-hooks`, `official-plugins`, `create-adapter`, `create-provider`
|
|
@@ -1,34 +1,33 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: create-prompt
|
|
3
|
-
description:
|
|
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
|
|
3
|
+
description: Define reusable AI interaction patterns that produce structured message sequences
|
|
23
4
|
---
|
|
24
5
|
|
|
25
6
|
# Creating MCP Prompts
|
|
26
7
|
|
|
27
8
|
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
9
|
|
|
29
|
-
## When to Use
|
|
10
|
+
## When to Use This Skill
|
|
30
11
|
|
|
31
|
-
|
|
12
|
+
### Must Use
|
|
13
|
+
|
|
14
|
+
- Building a reusable conversation template that AI clients invoke with arguments
|
|
15
|
+
- Defining structured multi-turn message sequences (user/assistant patterns)
|
|
16
|
+
- Creating domain-specific prompt patterns (code review, debugging, RAG queries)
|
|
17
|
+
|
|
18
|
+
### Recommended
|
|
19
|
+
|
|
20
|
+
- Standardizing message formats across multiple tools or agents
|
|
21
|
+
- Embedding MCP resource content into prompt messages for context
|
|
22
|
+
- Generating dynamic prompts that perform async lookups (knowledge base, APIs)
|
|
23
|
+
|
|
24
|
+
### Skip When
|
|
25
|
+
|
|
26
|
+
- You need an executable action that performs work and returns results (see `create-tool`)
|
|
27
|
+
- You need to expose read-only data at a URI (see `create-resource`)
|
|
28
|
+
- The task requires autonomous multi-step reasoning with inner tools (see `create-agent`)
|
|
29
|
+
|
|
30
|
+
> **Decision:** Use this skill when you need a reusable, parameterized conversation template that produces structured `GetPromptResult` messages.
|
|
32
31
|
|
|
33
32
|
## Class-Based Pattern
|
|
34
33
|
|
|
@@ -70,8 +69,10 @@ class CodeReviewPrompt extends PromptContext {
|
|
|
70
69
|
The `@Prompt` decorator accepts:
|
|
71
70
|
|
|
72
71
|
- `name` (required) -- unique prompt name
|
|
72
|
+
- `title` (optional) -- human-readable display title for UIs (if omitted, `name` is used)
|
|
73
73
|
- `description` (optional) -- human-readable description
|
|
74
74
|
- `arguments` (optional) -- array of `PromptArgument` objects
|
|
75
|
+
- `icons` (optional) -- array of Icon objects for UI representation (per MCP spec)
|
|
75
76
|
|
|
76
77
|
### PromptArgument Structure
|
|
77
78
|
|
|
@@ -398,3 +399,45 @@ nx generate @frontmcp/nx:prompt
|
|
|
398
399
|
```
|
|
399
400
|
|
|
400
401
|
This creates the prompt file, spec file, and updates barrel exports.
|
|
402
|
+
|
|
403
|
+
## Common Patterns
|
|
404
|
+
|
|
405
|
+
| Pattern | Correct | Incorrect | Why |
|
|
406
|
+
| ------------------- | ----------------------------------------------------------------- | --------------------------------------------------- | --------------------------------------------------------------------- |
|
|
407
|
+
| Return type | `execute()` returns `Promise<GetPromptResult>` | Returning a plain string or array of strings | MCP protocol requires `{ messages: [...] }` structure |
|
|
408
|
+
| Argument validation | Mark arguments as `required: true` in `arguments` array | Manually checking `args.field` inside `execute()` | Framework validates required arguments before `execute()` runs |
|
|
409
|
+
| Multi-turn priming | Use `assistant` role messages to prime expected response patterns | Putting all instructions in a single `user` message | Alternating roles guides the LLM toward structured output |
|
|
410
|
+
| Resource embedding | Use `type: 'resource'` content with a resource URI | Inlining resource data as raw text in the prompt | Resource references let clients resolve content dynamically |
|
|
411
|
+
| Error handling | Use `this.fail(err)` for validation failures in execute | `throw new Error(...)` directly | `this.fail` triggers the error flow with proper MCP error propagation |
|
|
412
|
+
|
|
413
|
+
## Verification Checklist
|
|
414
|
+
|
|
415
|
+
### Configuration
|
|
416
|
+
|
|
417
|
+
- [ ] Prompt class extends `PromptContext` and implements `execute(args)`
|
|
418
|
+
- [ ] `@Prompt` decorator has `name` and `arguments` array with correct `required` flags
|
|
419
|
+
- [ ] Prompt is registered in `prompts` array of `@App` or `@FrontMcp`
|
|
420
|
+
- [ ] All required arguments have `required: true`
|
|
421
|
+
|
|
422
|
+
### Runtime
|
|
423
|
+
|
|
424
|
+
- [ ] Prompt appears in `prompts/list` MCP response
|
|
425
|
+
- [ ] Calling prompt with valid arguments returns well-formed `GetPromptResult`
|
|
426
|
+
- [ ] Missing required arguments trigger `MissingPromptArgumentError`
|
|
427
|
+
- [ ] Multi-turn messages have correct `user`/`assistant` role alternation
|
|
428
|
+
- [ ] DI dependencies resolve correctly via `this.get()`
|
|
429
|
+
|
|
430
|
+
## Troubleshooting
|
|
431
|
+
|
|
432
|
+
| Problem | Cause | Solution |
|
|
433
|
+
| ------------------------------------------------- | --------------------------------------------------- | ----------------------------------------------------------------------------------------- |
|
|
434
|
+
| Prompt not appearing in `prompts/list` | Not registered in `prompts` array | Add prompt class to `@App` or `@FrontMcp` `prompts` array |
|
|
435
|
+
| `MissingPromptArgumentError` on optional argument | Argument marked `required: true` incorrectly | Set `required: false` for optional arguments in the `arguments` array |
|
|
436
|
+
| LLM ignores priming messages | Only using `user` role messages | Add `assistant` role messages to prime the conversation pattern |
|
|
437
|
+
| Type error on `execute()` return | Returning plain string instead of `GetPromptResult` | Wrap return in `{ messages: [{ role: 'user', content: { type: 'text', text: '...' } }] }` |
|
|
438
|
+
| `this.get(TOKEN)` throws DependencyNotFoundError | Provider not registered in scope | Register provider in `providers` array of `@App` or `@FrontMcp` |
|
|
439
|
+
|
|
440
|
+
## Reference
|
|
441
|
+
|
|
442
|
+
- [Prompts Documentation](https://docs.agentfront.dev/frontmcp/servers/prompts)
|
|
443
|
+
- Related skills: `create-tool`, `create-resource`, `create-agent`, `create-provider`
|