@frontmcp/skills 1.2.1 → 1.3.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.
Files changed (30) hide show
  1. package/catalog/frontmcp-config/SKILL.md +5 -5
  2. package/catalog/frontmcp-config/references/configure-deployment-targets.md +84 -1
  3. package/catalog/frontmcp-config/references/configure-http.md +57 -2
  4. package/catalog/frontmcp-config/references/configure-session.md +14 -7
  5. package/catalog/frontmcp-deployment/SKILL.md +3 -3
  6. package/catalog/frontmcp-deployment/references/build-for-mcpb.md +1 -1
  7. package/catalog/frontmcp-deployment/references/mcp-client-integration.md +107 -0
  8. package/catalog/frontmcp-development/SKILL.md +7 -7
  9. package/catalog/frontmcp-development/examples/create-provider/basic-database-provider.md +14 -0
  10. package/catalog/frontmcp-development/examples/create-provider/config-and-api-providers.md +85 -9
  11. package/catalog/frontmcp-development/examples/create-tool/basic-class-tool.md +30 -11
  12. package/catalog/frontmcp-development/examples/create-tool/tool-with-di-and-errors.md +62 -14
  13. package/catalog/frontmcp-development/examples/create-tool/tool-with-rate-limiting-and-progress.md +30 -12
  14. package/catalog/frontmcp-development/references/create-job.md +45 -11
  15. package/catalog/frontmcp-development/references/create-provider.md +80 -8
  16. package/catalog/frontmcp-development/references/create-skill-with-tools.md +31 -0
  17. package/catalog/frontmcp-development/references/create-skill.md +45 -0
  18. package/catalog/frontmcp-development/references/create-tool.md +124 -46
  19. package/catalog/frontmcp-guides/SKILL.md +7 -7
  20. package/catalog/frontmcp-observability/SKILL.md +15 -7
  21. package/catalog/frontmcp-observability/examples/metrics-endpoint/enable-metrics-endpoint.md +77 -0
  22. package/catalog/frontmcp-observability/references/metrics-endpoint.md +161 -0
  23. package/catalog/frontmcp-setup/SKILL.md +11 -11
  24. package/catalog/frontmcp-setup/examples/frontmcp-skills-usage/install-and-search-skills.md +19 -1
  25. package/catalog/frontmcp-setup/references/frontmcp-skills-usage.md +260 -19
  26. package/catalog/frontmcp-setup/references/setup-project.md +29 -0
  27. package/catalog/frontmcp-setup/references/setup-sqlite.md +68 -9
  28. package/catalog/frontmcp-testing/SKILL.md +17 -17
  29. package/catalog/skills-manifest.json +32 -6
  30. package/package.json +1 -1
@@ -2,18 +2,40 @@
2
2
  name: config-and-api-providers
3
3
  reference: create-provider
4
4
  level: intermediate
5
- description: 'A configuration provider with readonly environment settings and an HTTP API client provider.'
5
+ description: 'A configuration provider and an HTTP API client provider, organized as one folder per provider with co-located specs and barrels.'
6
6
  tags: [development, provider, config, api, providers]
7
7
  features:
8
8
  - 'A configuration provider using `readonly` properties from environment variables (sync construction)'
9
9
  - 'An API client provider that reads credentials in the constructor (no `onInit` — `@Provider` has no lifecycle hooks)'
10
+ - 'Folder-per-provider layout (`src/apps/main/providers/<slug>/`) with a barrel `index.ts` and a co-located `.provider.spec.ts`'
11
+ - 'Top-level `src/apps/main/providers/index.ts` barrel re-exporting each provider folder'
10
12
  - 'Registering providers at `@FrontMcp` level for server-wide sharing across all apps'
11
13
  - 'Separating token definitions from provider implementations for clean dependency boundaries'
12
14
  ---
13
15
 
14
16
  # Configuration and API Client Providers
15
17
 
16
- A configuration provider with readonly environment settings and an HTTP API client provider.
18
+ A configuration provider and an HTTP API client provider, organized as one folder per provider with co-located specs and barrels.
19
+
20
+ ## File layout
21
+
22
+ ```text
23
+ src/apps/main/
24
+ ├── tokens.ts # shared token + interface definitions
25
+ ├── index.ts # @App / @FrontMcp registration
26
+ └── providers/
27
+ ├── index.ts # top-level barrel
28
+ ├── config/
29
+ │ ├── index.ts # barrel: ConfigProvider
30
+ │ ├── config.provider.ts # @Provider class
31
+ │ └── config.provider.spec.ts # tests
32
+ └── api-client/
33
+ ├── index.ts # barrel: ApiClientProvider
34
+ ├── api-client.provider.ts # @Provider class
35
+ └── api-client.provider.spec.ts # tests
36
+ ```
37
+
38
+ Each provider lives in its own subfolder with a barrel — cross-provider imports go through the barrel (`from '../config'`), never reaching into another provider's implementation file.
17
39
 
18
40
  ## Code
19
41
 
@@ -38,13 +60,13 @@ export const API_TOKEN: Token<ApiClient> = Symbol('ApiClient');
38
60
  ```
39
61
 
40
62
  ```typescript
41
- // src/apps/main/providers/config.provider.ts
63
+ // src/apps/main/providers/config/config.provider.ts
42
64
  import { Provider } from '@frontmcp/sdk';
43
65
 
44
- import type { AppConfig } from '../tokens';
66
+ import type { AppConfig } from '../../tokens';
45
67
 
46
68
  @Provider({ name: 'ConfigProvider' })
47
- class ConfigProvider implements AppConfig {
69
+ export class ConfigProvider implements AppConfig {
48
70
  readonly apiBaseUrl = process.env.API_BASE_URL ?? 'https://api.example.com';
49
71
  readonly maxRetries = Number(process.env.MAX_RETRIES ?? 3);
50
72
  readonly debug = process.env.DEBUG === 'true';
@@ -52,13 +74,31 @@ class ConfigProvider implements AppConfig {
52
74
  ```
53
75
 
54
76
  ```typescript
55
- // src/apps/main/providers/api-client.provider.ts
77
+ // src/apps/main/providers/config/index.ts
78
+ export { ConfigProvider } from './config.provider';
79
+ ```
80
+
81
+ ```typescript
82
+ // src/apps/main/providers/config/config.provider.spec.ts
83
+ import { ConfigProvider } from './config.provider';
84
+
85
+ describe('ConfigProvider', () => {
86
+ it('reads apiBaseUrl from env with a default fallback', () => {
87
+ const provider = new ConfigProvider();
88
+ expect(provider.apiBaseUrl).toBeDefined();
89
+ expect(typeof provider.apiBaseUrl).toBe('string');
90
+ });
91
+ });
92
+ ```
93
+
94
+ ```typescript
95
+ // src/apps/main/providers/api-client/api-client.provider.ts
56
96
  import { Provider } from '@frontmcp/sdk';
57
97
 
58
- import type { ApiClient } from '../tokens';
98
+ import type { ApiClient } from '../../tokens';
59
99
 
60
100
  @Provider({ name: 'ApiClientProvider' })
61
- class ApiClientProvider implements ApiClient {
101
+ export class ApiClientProvider implements ApiClient {
62
102
  // `@Provider` has no `onInit` lifecycle hook — read env in the constructor.
63
103
  // First instantiation throws synchronously on missing config (fail fast).
64
104
  private readonly baseUrl: string;
@@ -92,10 +132,44 @@ class ApiClientProvider implements ApiClient {
92
132
  }
93
133
  ```
94
134
 
135
+ ```typescript
136
+ // src/apps/main/providers/api-client/index.ts
137
+ export { ApiClientProvider } from './api-client.provider';
138
+ ```
139
+
140
+ ```typescript
141
+ // src/apps/main/providers/api-client/api-client.provider.spec.ts
142
+ import { ApiClientProvider } from './api-client.provider';
143
+
144
+ describe('ApiClientProvider', () => {
145
+ const origEnv = { ...process.env };
146
+ afterEach(() => {
147
+ process.env = { ...origEnv };
148
+ });
149
+
150
+ it('throws fast when API_URL or API_KEY is missing', () => {
151
+ delete process.env.API_URL;
152
+ delete process.env.API_KEY;
153
+ expect(() => new ApiClientProvider()).toThrow(/API_URL and API_KEY/);
154
+ });
155
+ });
156
+ ```
157
+
158
+ ```typescript
159
+ // src/apps/main/providers/index.ts
160
+ // Top-level barrel — re-exports each provider folder so importers can do
161
+ // `import { ConfigProvider, ApiClientProvider } from './providers'`.
162
+ export * from './config';
163
+ export * from './api-client';
164
+ ```
165
+
95
166
  ```typescript
96
167
  // src/index.ts
97
168
  import { FrontMcp } from '@frontmcp/sdk';
98
169
 
170
+ import { MainApp } from './apps/main';
171
+ import { ApiClientProvider, ConfigProvider } from './apps/main/providers';
172
+
99
173
  @FrontMcp({
100
174
  info: { name: 'my-server', version: '1.0.0' },
101
175
  apps: [MainApp],
@@ -108,9 +182,11 @@ class MyServer {}
108
182
 
109
183
  - A configuration provider using `readonly` properties from environment variables (sync construction)
110
184
  - An API client provider that reads credentials in the constructor (no `onInit` — `@Provider` has no lifecycle hooks)
185
+ - Folder-per-provider layout (`src/apps/main/providers/<slug>/`) with a barrel `index.ts` and a co-located `.provider.spec.ts`
186
+ - Top-level `src/apps/main/providers/index.ts` barrel re-exporting each provider folder
111
187
  - Registering providers at `@FrontMcp` level for server-wide sharing across all apps
112
188
  - Separating token definitions from provider implementations for clean dependency boundaries
113
189
 
114
190
  ## Related
115
191
 
116
- - See `create-provider` for cache providers, lifecycle details, and the `tryGet()` safe access pattern
192
+ - See `create-provider` for the [File Layout](../../references/create-provider.md#file-layout) section, cache providers, lifecycle details, and the `tryGet()` safe access pattern
@@ -2,37 +2,54 @@
2
2
  name: basic-class-tool
3
3
  reference: create-tool
4
4
  level: basic
5
- description: 'A minimal tool using the class-based pattern with Zod input validation and output schema.'
5
+ description: 'A minimal tool using the class-based pattern with Zod input validation, output schema, and types derived from the schemas.'
6
6
  tags: [development, tool, class]
7
7
  features:
8
8
  - 'Extending `ToolContext` and implementing the `execute()` method'
9
9
  - 'Using a Zod raw shape for `inputSchema` (not wrapped in `z.object()`)'
10
10
  - 'Defining `outputSchema` to validate and restrict output fields'
11
+ - 'Deriving `execute()` input/output types from the schemas via `ToolInputOf<>` / `ToolOutputOf<>` (no duplicated annotation)'
12
+ - 'Co-locating schema and tool in sibling files (`<name>.schema.ts` / `<name>.tool.ts`)'
11
13
  - 'Registering the tool in an `@App` via the `tools` array'
12
14
  ---
13
15
 
14
16
  # Basic Class-Based Tool
15
17
 
16
- A minimal tool using the class-based pattern with Zod input validation and output schema.
18
+ A minimal tool using the class-based pattern with Zod input validation, output schema, and types derived from the schemas.
17
19
 
18
20
  ## Code
19
21
 
22
+ ```typescript
23
+ // src/apps/main/tools/greet-user.schema.ts
24
+ import { ToolInputOf, ToolOutputOf, z } from '@frontmcp/sdk';
25
+
26
+ // Hoist only the schemas — keep `@Tool({…})` self-contained in the tool file.
27
+ export const inputSchema = {
28
+ name: z.string().describe('The name of the user to greet'),
29
+ };
30
+
31
+ export const outputSchema = {
32
+ greeting: z.string(),
33
+ };
34
+
35
+ export type GreetUserInput = ToolInputOf<{ inputSchema: typeof inputSchema }>;
36
+ export type GreetUserOutput = ToolOutputOf<{ outputSchema: typeof outputSchema }>;
37
+ ```
38
+
20
39
  ```typescript
21
40
  // src/apps/main/tools/greet-user.tool.ts
22
- import { Tool, ToolContext, z } from '@frontmcp/sdk';
41
+ import { Tool, ToolContext } from '@frontmcp/sdk';
42
+
43
+ import { inputSchema, outputSchema, type GreetUserInput, type GreetUserOutput } from './greet-user.schema';
23
44
 
24
45
  @Tool({
25
46
  name: 'greet_user',
26
47
  description: 'Greet a user by name',
27
- inputSchema: {
28
- name: z.string().describe('The name of the user to greet'),
29
- },
30
- outputSchema: {
31
- greeting: z.string(),
32
- },
48
+ inputSchema,
49
+ outputSchema,
33
50
  })
34
51
  class GreetUserTool extends ToolContext {
35
- async execute(input: { name: string }): Promise<{ greeting: string }> {
52
+ async execute(input: GreetUserInput): Promise<GreetUserOutput> {
36
53
  return { greeting: `Hello, ${input.name}!` };
37
54
  }
38
55
  }
@@ -54,8 +71,10 @@ class MainApp {}
54
71
  - Extending `ToolContext` and implementing the `execute()` method
55
72
  - Using a Zod raw shape for `inputSchema` (not wrapped in `z.object()`)
56
73
  - Defining `outputSchema` to validate and restrict output fields
74
+ - Deriving `execute()` input/output types from the schemas via `ToolInputOf<>` / `ToolOutputOf<>` (no duplicated annotation)
75
+ - Co-locating schema and tool in sibling files (`<name>.schema.ts` / `<name>.tool.ts`)
57
76
  - Registering the tool in an `@App` via the `tools` array
58
77
 
59
78
  ## Related
60
79
 
61
- - See `create-tool` for the full API reference including annotations, rate limiting, and elicitation
80
+ - See `create-tool` for the full API reference including the derive-types pattern, file layouts, annotations, rate limiting, and elicitation
@@ -2,18 +2,32 @@
2
2
  name: tool-with-di-and-errors
3
3
  reference: create-tool
4
4
  level: intermediate
5
- description: 'A tool that resolves a database service via DI and uses `this.fail()` for business-logic errors.'
5
+ description: 'A tool that resolves a database service via DI and uses `this.fail()` for business-logic errors, with `execute()` types derived from the schemas.'
6
6
  tags: [development, database, tool, di, errors]
7
7
  features:
8
8
  - 'Defining a typed DI token with `Token<T>` and resolving it via `this.get()`'
9
9
  - 'Using `this.fail()` with `ResourceNotFoundError` for MCP-compliant error responses'
10
10
  - 'Letting infrastructure errors (database failures) propagate naturally to the framework'
11
+ - 'Deriving `execute()` types from `inputSchema` / `outputSchema` via `ToolInputOf<>` / `ToolOutputOf<>`'
12
+ - 'Folder-per-tool layout (`tools/delete-record/{schema,tool,index}.ts`) for tools with local helpers or error types'
11
13
  - 'Registering both the provider and tool in the same `@App`'
12
14
  ---
13
15
 
14
16
  # Tool with Dependency Injection and Error Handling
15
17
 
16
- A tool that resolves a database service via DI and uses `this.fail()` for business-logic errors.
18
+ A tool that resolves a database service via DI and uses `this.fail()` for business-logic errors, with `execute()` types derived from the schemas.
19
+
20
+ ## File layout
21
+
22
+ ```text
23
+ src/apps/main/
24
+ ├── tokens.ts # shared DI tokens
25
+ └── tools/
26
+ └── delete-record/
27
+ ├── delete-record.schema.ts # input/output schemas + derived types
28
+ ├── delete-record.tool.ts # @Tool class, execute()
29
+ └── index.ts # barrel re-export
30
+ ```
17
31
 
18
32
  ## Code
19
33
 
@@ -29,23 +43,38 @@ export const DATABASE: Token<DatabaseService> = Symbol('database');
29
43
  ```
30
44
 
31
45
  ```typescript
32
- // src/apps/main/tools/delete-record.tool.ts
33
- import { ResourceNotFoundError, Tool, ToolContext, z } from '@frontmcp/sdk';
46
+ // src/apps/main/tools/delete-record/delete-record.schema.ts
47
+ import { ToolInputOf, ToolOutputOf, z } from '@frontmcp/sdk';
48
+
49
+ // Only the schemas live here — decorator config (`name`, `description`, …)
50
+ // stays inside @Tool({…}) in the tool file.
51
+ export const inputSchema = {
52
+ id: z.string().uuid().describe('Record UUID'),
53
+ };
34
54
 
35
- import { DATABASE } from '../tokens';
55
+ export const outputSchema = {
56
+ message: z.string(),
57
+ };
58
+
59
+ export type DeleteRecordInput = ToolInputOf<{ inputSchema: typeof inputSchema }>;
60
+ export type DeleteRecordOutput = ToolOutputOf<{ outputSchema: typeof outputSchema }>;
61
+ ```
62
+
63
+ ```typescript
64
+ // src/apps/main/tools/delete-record/delete-record.tool.ts
65
+ import { ResourceNotFoundError, Tool, ToolContext } from '@frontmcp/sdk';
66
+
67
+ import { DATABASE } from '../../tokens';
68
+ import { inputSchema, outputSchema, type DeleteRecordInput, type DeleteRecordOutput } from './delete-record.schema';
36
69
 
37
70
  @Tool({
38
71
  name: 'delete_record',
39
72
  description: 'Delete a record by ID',
40
- inputSchema: {
41
- id: z.string().uuid().describe('Record UUID'),
42
- },
43
- outputSchema: {
44
- message: z.string(),
45
- },
73
+ inputSchema,
74
+ outputSchema,
46
75
  })
47
- class DeleteRecordTool extends ToolContext {
48
- async execute(input: { id: string }): Promise<{ message: string }> {
76
+ export class DeleteRecordTool extends ToolContext {
77
+ async execute(input: DeleteRecordInput): Promise<DeleteRecordOutput> {
49
78
  const db = this.get(DATABASE);
50
79
  const rows = await db.query('SELECT * FROM records WHERE id = $1', [input.id]);
51
80
 
@@ -59,10 +88,27 @@ class DeleteRecordTool extends ToolContext {
59
88
  }
60
89
  ```
61
90
 
91
+ ```typescript
92
+ // src/apps/main/tools/delete-record/index.ts
93
+ export { DeleteRecordTool } from './delete-record.tool';
94
+ export {
95
+ inputSchema as deleteRecordInputSchema,
96
+ outputSchema as deleteRecordOutputSchema,
97
+ type DeleteRecordInput,
98
+ type DeleteRecordOutput,
99
+ } from './delete-record.schema';
100
+ ```
101
+
62
102
  ```typescript
63
103
  // src/apps/main/index.ts
64
104
  import { App } from '@frontmcp/sdk';
65
105
 
106
+ // `DatabaseProvider` is the singleton that backs `DATABASE` — see the
107
+ // `create-provider` skill for an `AsyncProvider({ useFactory })` reference
108
+ // implementation that opens a connection pool at startup.
109
+ import { DatabaseProvider } from './providers/database';
110
+ import { DeleteRecordTool } from './tools/delete-record';
111
+
66
112
  @App({
67
113
  name: 'main',
68
114
  providers: [DatabaseProvider],
@@ -76,9 +122,11 @@ class MainApp {}
76
122
  - Defining a typed DI token with `Token<T>` and resolving it via `this.get()`
77
123
  - Using `this.fail()` with `ResourceNotFoundError` for MCP-compliant error responses
78
124
  - Letting infrastructure errors (database failures) propagate naturally to the framework
125
+ - Deriving `execute()` types from `inputSchema` / `outputSchema` via `ToolInputOf<>` / `ToolOutputOf<>`
126
+ - Folder-per-tool layout (`tools/delete-record/{schema,tool,index}.ts`) for tools with local helpers or error types
79
127
  - Registering both the provider and tool in the same `@App`
80
128
 
81
129
  ## Related
82
130
 
83
- - See `create-tool` for all context methods and error handling patterns
131
+ - See `create-tool` for all context methods, the derive-types pattern, and error handling
84
132
  - See `create-provider` for how to implement the `DatabaseProvider` class
@@ -2,7 +2,7 @@
2
2
  name: tool-with-rate-limiting-and-progress
3
3
  reference: create-tool
4
4
  level: advanced
5
- description: 'A batch processing tool that uses rate limiting, concurrency control, progress notifications, and annotations.'
5
+ description: 'A batch processing tool that uses rate limiting, concurrency control, progress notifications, and annotations, with `execute()` types derived from the schemas.'
6
6
  tags: [development, throttle, tool, rate, limiting, progress]
7
7
  features:
8
8
  - 'Configuring `rateLimit`, `concurrency`, and `timeout` for throttling protection'
@@ -10,28 +10,45 @@ features:
10
10
  - 'Using `this.mark(stage)` for execution stage tracking and debugging'
11
11
  - 'Sending log-level notifications with `this.notify(message, level)`'
12
12
  - 'Setting tool `annotations` to communicate behavioral hints to clients'
13
+ - 'Deriving `execute()` types from the schemas via `ToolInputOf<>` / `ToolOutputOf<>`'
13
14
  ---
14
15
 
15
16
  # Tool with Rate Limiting, Progress, and Annotations
16
17
 
17
- A batch processing tool that uses rate limiting, concurrency control, progress notifications, and annotations.
18
+ A batch processing tool that uses rate limiting, concurrency control, progress notifications, and annotations, with `execute()` types derived from the schemas.
18
19
 
19
20
  ## Code
20
21
 
22
+ ```typescript
23
+ // src/apps/main/tools/batch-process.schema.ts
24
+ import { ToolInputOf, ToolOutputOf, z } from '@frontmcp/sdk';
25
+
26
+ // Schemas only — `annotations`, `rateLimit`, `concurrency`, `timeout` etc.
27
+ // stay inside @Tool({…}) in the tool file.
28
+ export const inputSchema = {
29
+ items: z.array(z.string()).min(1).describe('Items to process'),
30
+ };
31
+
32
+ export const outputSchema = {
33
+ processed: z.number(),
34
+ results: z.array(z.string()),
35
+ };
36
+
37
+ export type BatchProcessInput = ToolInputOf<{ inputSchema: typeof inputSchema }>;
38
+ export type BatchProcessOutput = ToolOutputOf<{ outputSchema: typeof outputSchema }>;
39
+ ```
40
+
21
41
  ```typescript
22
42
  // src/apps/main/tools/batch-process.tool.ts
23
- import { Tool, ToolContext, z } from '@frontmcp/sdk';
43
+ import { Tool, ToolContext } from '@frontmcp/sdk';
44
+
45
+ import { inputSchema, outputSchema, type BatchProcessInput, type BatchProcessOutput } from './batch-process.schema';
24
46
 
25
47
  @Tool({
26
48
  name: 'batch_process',
27
49
  description: 'Process a batch of items with progress tracking',
28
- inputSchema: {
29
- items: z.array(z.string()).min(1).describe('Items to process'),
30
- },
31
- outputSchema: {
32
- processed: z.number(),
33
- results: z.array(z.string()),
34
- },
50
+ inputSchema,
51
+ outputSchema,
35
52
  annotations: {
36
53
  title: 'Batch Processor',
37
54
  readOnlyHint: false,
@@ -43,7 +60,7 @@ import { Tool, ToolContext, z } from '@frontmcp/sdk';
43
60
  timeout: { executeMs: 30_000 },
44
61
  })
45
62
  class BatchProcessTool extends ToolContext {
46
- async execute(input: { items: string[] }): Promise<{ processed: number; results: string[] }> {
63
+ async execute(input: BatchProcessInput): Promise<BatchProcessOutput> {
47
64
  this.mark('validation');
48
65
  if (input.items.some((item) => item.trim() === '')) {
49
66
  this.fail(new Error('Items must not be empty strings'));
@@ -86,7 +103,8 @@ class MainApp {}
86
103
  - Using `this.mark(stage)` for execution stage tracking and debugging
87
104
  - Sending log-level notifications with `this.notify(message, level)`
88
105
  - Setting tool `annotations` to communicate behavioral hints to clients
106
+ - Deriving `execute()` types from the schemas via `ToolInputOf<>` / `ToolOutputOf<>`
89
107
 
90
108
  ## Related
91
109
 
92
- - See `create-tool` for all annotation fields, elicitation, and auth provider patterns
110
+ - See `create-tool` for the full derive-types pattern, annotation fields, elicitation, and auth provider patterns
@@ -418,7 +418,9 @@ class DataApp {}
418
418
 
419
419
  ### Enabling the Jobs System
420
420
 
421
- Jobs require a persistent store for state management. Enable the jobs system in `@FrontMcp` configuration.
421
+ **Auto-enable (issue #408):** declaring any `@App({ jobs: [...] })` (or `workflows: [...]`) is enough — the jobs subsystem comes up with in-memory stores by default and the management tools (`execute_job`, `list_jobs`, `get_job_status`, `register_job`, `remove_job`) are registered automatically so agents can invoke them. No `@FrontMcp({ jobs: { enabled: true } })` is required for the happy path.
422
+
423
+ **When to configure `@FrontMcp({ jobs })` explicitly:** override the in-memory default with persistent storage (Redis recommended for multi-replica HA) so job state, progress, logs, and outputs survive retries and server restarts.
422
424
 
423
425
  ```typescript
424
426
  import { FrontMcp } from '@frontmcp/sdk';
@@ -426,8 +428,9 @@ import { FrontMcp } from '@frontmcp/sdk';
426
428
  @FrontMcp({
427
429
  info: { name: 'my-server', version: '1.0.0' },
428
430
  apps: [DataApp],
431
+ // `enabled: true` is implied by `@App({ jobs })`. Set explicitly only to
432
+ // configure store, or set to `false` to opt out and suppress the tools.
429
433
  jobs: {
430
- enabled: true,
431
434
  store: {
432
435
  redis: {
433
436
  provider: 'redis',
@@ -441,7 +444,36 @@ import { FrontMcp } from '@frontmcp/sdk';
441
444
  class MyServer {}
442
445
  ```
443
446
 
444
- The store persists job state, progress, logs, and outputs across retries and server restarts. Redis is recommended for production. Without `jobs.enabled: true`, registered jobs will not be activated.
447
+ Setting `jobs: { enabled: false }` is an explicit opt-out declared jobs will NOT be activated and the framework logs a warning so the configuration mismatch is loud.
448
+
449
+ ### Calling Jobs from an Agent
450
+
451
+ Once jobs are registered, the SDK exposes five MCP tools (snake_case per ecosystem convention; hyphen aliases like `execute-job` keep working with a deprecation log line for one release):
452
+
453
+ | Tool | Purpose |
454
+ | ---------------- | ------------------------------------------------------------------------ |
455
+ | `list_jobs` | List registered jobs with optional `tags` / `labels` / `query` filters |
456
+ | `execute_job` | Execute a registered job by name (`{ name, input?, background? }`) |
457
+ | `get_job_status` | Get the run state for a `runId` returned by `execute_job` |
458
+ | `register_job` | Register a dynamic job at runtime (sandboxed; `hideFromDiscovery: true`) |
459
+ | `remove_job` | Remove a dynamic job by name (`hideFromDiscovery: true`) |
460
+
461
+ Workflows expose a parallel set: `list_workflows`, `execute_workflow`, `get_workflow_status`, `register_workflow`, `remove_workflow`.
462
+
463
+ For finer-grained control (e.g. omit `register_job` / `remove_job` in production), opt out of auto-registration by importing the tool classes manually:
464
+
465
+ ```typescript
466
+ import { App, ExecuteJobTool, GetJobStatusTool, ListJobsTool } from '@frontmcp/sdk';
467
+
468
+ @App({
469
+ name: 'data-app',
470
+ jobs: [GenerateReportJob],
471
+ tools: [ExecuteJobTool, ListJobsTool, GetJobStatusTool], // omit register/remove
472
+ })
473
+ class DataApp {}
474
+ ```
475
+
476
+ The same classes are also reachable via the subpath `@frontmcp/sdk/job/tools` and `@frontmcp/sdk/workflow/tools` for bundlers that prefer narrower imports.
445
477
 
446
478
  ## Nx Generator
447
479
 
@@ -603,7 +635,9 @@ class DataServer {}
603
635
 
604
636
  ### Runtime
605
637
 
606
- - [ ] `jobs.enabled: true` is set in `@FrontMcp` configuration with a store
638
+ - [ ] Jobs subsystem is active either implicitly via `@App({ jobs: [...] })`
639
+ (auto-enable, issue #408) or explicitly via `@FrontMcp({ jobs: { store: { ... } } })`
640
+ when overriding the in-memory default
607
641
  - [ ] Job executes and returns output matching `outputSchema`
608
642
  - [ ] Progress is reported and queryable during execution
609
643
  - [ ] Retry fires with correct backoff delays on transient failures
@@ -611,13 +645,13 @@ class DataServer {}
611
645
 
612
646
  ## Troubleshooting
613
647
 
614
- | Problem | Cause | Solution |
615
- | -------------------------- | ----------------------------------------------- | ---------------------------------------------------------------------------- |
616
- | Job not activated | `jobs.enabled` not set to `true` in `@FrontMcp` | Add `jobs: { enabled: true, store: { ... } }` to `@FrontMcp` config |
617
- | Job fails without retrying | No `retry` policy configured | Add `retry: { maxAttempts: 3, backoffMs: 2000 }` to `@Job` options |
618
- | Progress not visible | Not calling `this.progress()` during execution | Add `this.progress(pct, total, message)` calls at each stage |
619
- | Job times out unexpectedly | Default 5-minute timeout too short | Set `timeout` in `@Job` to a higher value (e.g., `600000` for 10 minutes) |
620
- | Permission denied error | User lacks required roles or scopes | Verify user has one of the `roles` and all `scopes` defined in `permissions` |
648
+ | Problem | Cause | Solution |
649
+ | -------------------------- | ------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
650
+ | Job not activated | `@App` doesn't declare `jobs: [...]` AND `@FrontMcp({ jobs: { enabled: true } })` isn't set | Add the job class to `@App({ jobs: [...] })` (auto-enables) OR set `@FrontMcp({ jobs: { enabled: true, store: { ... } } })` |
651
+ | Job fails without retrying | No `retry` policy configured | Add `retry: { maxAttempts: 3, backoffMs: 2000 }` to `@Job` options |
652
+ | Progress not visible | Not calling `this.progress()` during execution | Add `this.progress(pct, total, message)` calls at each stage |
653
+ | Job times out unexpectedly | Default 5-minute timeout too short | Set `timeout` in `@Job` to a higher value (e.g., `600000` for 10 minutes) |
654
+ | Permission denied error | User lacks required roles or scopes | Verify user has one of the `roles` and all `scopes` defined in `permissions` |
621
655
 
622
656
  ## Examples
623
657