> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agentfront.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# @Job

> The @Job decorator defines an executable job with validated input/output schemas, retry configuration, and permission checks.

## Basic Usage

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
import { Job, JobContext } from '@frontmcp/sdk';
import { z } from '@frontmcp/sdk';

@Job({
  name: 'send-email',
  description: 'Send an email to a recipient',
  inputSchema: {
    to: z.string().email().describe('Recipient email'),
    subject: z.string().describe('Email subject'),
    body: z.string().describe('Email body'),
  },
  outputSchema: {
    messageId: z.string(),
    sent: z.boolean(),
  },
})
class SendEmailJob extends JobContext {
  async execute(input: { to: string; subject: string; body: string }) {
    const mailer = this.get(MailerToken);
    const result = await mailer.send(input);
    return { messageId: result.id, sent: true };
  }
}
```

## Signature

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
function Job<I extends ZodRawShape, O extends OutputSchema>(
  opts: JobMetadata<I, O> & { outputSchema: O }
): TypedClassDecorator
```

## Type Safety

The `@Job` decorator enforces at compile time:

* The decorated class must extend `JobContext`
* The `execute()` parameter must exactly match the `inputSchema` type
* The `execute()` return type must match the `outputSchema` type
* Both `inputSchema` and `outputSchema` are required for Jobs

## Configuration Options

### Required Properties

| Property | Type | Description |
| - | - | - |
| `name` | `string` | Unique job identifier |
| `inputSchema` | `ZodShape` | Zod object shape for input validation |
| `outputSchema` | `ZodShape` | Zod object shape for output validation |

### Optional Properties

| Property | Type | Default | Description |
| - | - | - | - |
| `description` | `string` | — | Job description |
| `id` | `string` | `name` | Stable identifier for tracking |
| `timeout` | `number` | `300000` | Max execution time in ms (5 min) |
| `retry` | `JobRetryConfig` | — | Retry configuration |
| `tags` | `string[]` | — | Categorization tags |
| `labels` | `Record<string, string>` | — | Key-value labels |
| `hideFromDiscovery` | `boolean` | `false` | Hide from `list_jobs` |
| `permissions` | `JobPermission[]` | — | RBAC permission rules |

### RetryConfig

| Property | Type | Default | Description |
| - | - | - | - |
| `maxAttempts` | `number` | `3` | Maximum retry attempts |
| `backoffMs` | `number` | `1000` | Initial backoff delay in ms |
| `backoffMultiplier` | `number` | `2` | Backoff multiplier per attempt |
| `maxBackoffMs` | `number` | `60000` | Maximum backoff delay in ms |

### Permission

| Property | Type | Description |
| - | - | - |
| `action` | `'create' \| 'read' \| 'update' \| 'delete' \| 'execute' \| 'list'` | Permission action |
| `roles` | `string[]` | Required roles (at least one must match) |
| `scopes` | `string[]` | Required OAuth scopes |
| `custom` | `(authInfo) => boolean \| Promise<boolean>` | Custom guard function |

## Examples

### Simple Job

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@Job({
  name: 'greet',
  inputSchema: { name: z.string() },
  outputSchema: { message: z.string() },
})
class GreetJob extends JobContext {
  async execute(input: { name: string }) {
    return { message: `Hello, ${input.name}!` };
  }
}
```

### Job with Retry

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@Job({
  name: 'fetch-api',
  description: 'Fetch data from external API with retries',
  inputSchema: { endpoint: z.string().url() },
  outputSchema: { data: z.unknown(), statusCode: z.number() },
  retry: {
    maxAttempts: 5,
    backoffMs: 2000,
    backoffMultiplier: 2,
    maxBackoffMs: 30000,
  },
  timeout: 60000,
})
class FetchApiJob extends JobContext {
  async execute(input: { endpoint: string }) {
    this.log(`Attempt ${this.attempt}: fetching ${input.endpoint}`);
    const response = await this.fetch(input.endpoint);
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    return { data: await response.json(), statusCode: response.status };
  }
}
```

### Job with Permissions

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@Job({
  name: 'delete-records',
  description: 'Delete records from database',
  inputSchema: {
    table: z.string(),
    ids: z.array(z.string()),
  },
  outputSchema: {
    deletedCount: z.number(),
  },
  permissions: [
    { action: 'execute', roles: ['admin'] },
    { action: 'execute', scopes: ['data:delete'] },
  ],
  tags: ['destructive', 'admin'],
})
class DeleteRecordsJob extends JobContext {
  async execute(input: { table: string; ids: string[] }) {
    const db = this.get(DatabaseToken);
    const count = await db.deleteMany(input.table, input.ids);
    return { deletedCount: count };
  }
}
```

## Function-Based Alternative

For simpler jobs, use the `job()` function:

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
import { job } from '@frontmcp/sdk';
import { z } from '@frontmcp/sdk';

const GreetJob = job({
  name: 'greet',
  description: 'Generate a greeting',
  inputSchema: { name: z.string() },
  outputSchema: { message: z.string() },
})((input, ctx) => {
  ctx.log(`Greeting ${input.name}`);
  return { message: `Hello, ${input.name}!` };
});
```

## Context Methods

The `JobContext` base class provides:

### Dependency Injection

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
async execute(input) {
  const service = this.get(ServiceToken);      // Required dependency
  const optional = this.tryGet(OptionalToken); // Optional dependency
}
```

### Logging and Progress

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
async execute(input) {
  this.log('Starting processing');
  await this.progress(50, 100, 'Halfway done');
}
```

### Error Handling

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
async execute(input) {
  // Fail with an error when validation fails
  if (!input.data) {
    this.fail(new Error('Missing required data'));
  }

  // Early return with a previously computed output
  const cachedResult = this.tryGet(CacheToken)?.get(input.data);
  if (cachedResult) {
    this.respond(cachedResult); // Ends execution
  }
}
```

## Related

<CardGroup cols={2}>
  <Card title="JobContext" icon="code" href="/frontmcp/sdk-reference/contexts/job-context">
    Context class details
  </Card>

  <Card title="JobRegistry" icon="database" href="/frontmcp/sdk-reference/registries/job-registry">
    Job registry API
  </Card>

  <Card title="Jobs Guide" icon="book" href="/frontmcp/servers/jobs">
    Jobs documentation
  </Card>

  <Card title="@Workflow" icon="diagram-project" href="/frontmcp/sdk-reference/decorators/workflow">
    Define workflows
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.