> ## 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.

# JobContext

> JobContext is the base class for all job implementations. It extends ExecutionContextBase and provides job-specific features like logging, progress reporting, and retry tracking.

## Class Definition

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
export abstract class JobContext<
  InSchema extends ToolInputType = ToolInputType,
  OutSchema extends ToolOutputType = ToolOutputType,
  In = ToolInputOf<{ inputSchema: InSchema }>,
  Out = ToolOutputOf<{ outputSchema: OutSchema }>,
> extends ExecutionContextBase<Out>
```

## Type Parameters

| Parameter   | Description                   |
| ----------- | ----------------------------- |
| `InSchema`  | Input schema type (Zod shape) |
| `OutSchema` | Output schema type            |
| `In`        | Inferred input type           |
| `Out`       | Inferred output type          |

## Properties

| Property   | Type               | Description                                   |
| ---------- | ------------------ | --------------------------------------------- |
| `metadata` | `JobMetadata`      | Job metadata (name, description, retry, etc.) |
| `input`    | `In`               | Parsed and validated input                    |
| `output`   | `Out \| undefined` | Job output (after execution)                  |
| `jobName`  | `string`           | Job name                                      |
| `jobId`    | `string`           | Job ID (or name if not specified)             |

## Abstract Method

### execute(input)

The main execution method that must be implemented.

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
abstract execute(input: In): Promise<Out>
```

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@Job({
  name: 'process-data',
  inputSchema: { data: z.string() },
  outputSchema: { result: z.string() },
})
class ProcessDataJob extends JobContext {
  async execute(input: { data: string }) {
    return { result: `Processed: ${input.data}` };
  }
}
```

## Methods

### Response Methods

#### respond(value)

Set output and end execution immediately.

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
respond(value: Out): never
```

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
async execute(input) {
  const cached = await this.tryGetCached(input); // e.g. from your cache layer
  if (cached) {
    this.respond(cached); // Ends execution here
  }
  // This code won't run if cached
  return processInput(input);
}
```

### Logging Methods

#### log(message)

Append a timestamped log entry to the job's log buffer.

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
protected log(message: string): void
```

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
async execute(input) {
  this.log('Starting processing');
  await this.doWork(input);
  this.log('Processing complete');
  return { success: true };
}
```

#### getLogs()

Get all log entries recorded during execution.

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
getLogs(): readonly string[]
```

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
async execute(input) {
  this.log('Starting processing');
  await this.doWork(input);
  const logs = this.getLogs();
  // ['[<timestamp>] Starting processing', ...]
  return { success: true, logs };
}
```

### Progress Methods

#### progress(pct, total?, msg?)

Send a progress notification to the client. Returns `false` if no session is available.

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
protected async progress(
  pct: number,
  total?: number,
  msg?: string
): Promise<boolean>
```

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
for (let i = 0; i < items.length; i++) {
  await this.progress(i + 1, items.length, `Processing item ${i + 1}`);
  await this.processItem(items[i]);
}
```

### Retry Tracking

#### attempt

The current retry attempt number (1-based).

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
get attempt(): number
```

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
async execute(input) {
  this.log(`Attempt ${this.attempt}`);
  if (this.attempt > 1) {
    this.log('Retrying after previous failure');
  }
  // ...
}
```

## Inherited Methods

From `ExecutionContextBase`:

| Method                 | Description                                                       |
| ---------------------- | ----------------------------------------------------------------- |
| `get(token)`           | Resolve a required dependency                                     |
| `tryGet(token)`        | Resolve an optional dependency (returns `undefined` if not found) |
| `scope`                | Access the current scope                                          |
| `logger`               | Scoped logger instance                                            |
| `getAuthInfo()`        | Get authentication context                                        |
| `fail(error)`          | Fail execution with an error                                      |
| `mark(stage)`          | Mark current execution stage                                      |
| `fetch(url, options?)` | Context-aware HTTP fetch                                          |

## Full Example

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

const inputSchema = {
  urls: z.array(z.string().url()).describe('URLs to fetch'),
  timeout: z.number().default(5000).describe('Per-URL timeout in ms'),
};

const outputSchema = {
  results: z.array(z.object({
    url: z.string(),
    status: z.number(),
    size: z.number(),
  })),
  totalSize: z.number(),
};

@Job({
  name: 'batch-fetch',
  description: 'Fetch multiple URLs and report their status',
  inputSchema,
  outputSchema,
  retry: { maxAttempts: 3, backoffMs: 1000 },
  timeout: 60000,
})
class BatchFetchJob extends JobContext<typeof inputSchema, typeof outputSchema> {
  async execute(input) {
    this.log(`Fetching ${input.urls.length} URLs (attempt ${this.attempt})`);

    const results: Array<{ url: string; status: number; size: number }> = [];
    for (let i = 0; i < input.urls.length; i++) {
      const url = input.urls[i];
      await this.progress(i + 1, input.urls.length, `Fetching ${url}`);

      try {
        const response = await this.fetch(url, {
          signal: AbortSignal.timeout(input.timeout),
        });
        const body = await response.text();
        results.push({ url, status: response.status, size: body.length });
        this.log(`${url}: ${response.status} (${body.length} bytes)`);
      } catch (err: unknown) {
        results.push({ url, status: 0, size: 0 });
        this.log(`${url}: failed - ${err instanceof Error ? err.message : String(err)}`);
      }
    }

    const totalSize = results.reduce((sum, r) => sum + r.size, 0);
    return { results, totalSize };
  }
}
```

## Related

<CardGroup cols={2}>
  <Card title="@Job" icon="at" href="/frontmcp/sdk-reference/decorators/job">
    Job decorator
  </Card>

  <Card title="ExecutionContextBase" icon="code" href="/frontmcp/sdk-reference/contexts/execution-context-base">
    Base class
  </Card>

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

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