Skip to main content

Class Definition

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

ParameterDescription
InSchemaInput schema type (Zod shape)
OutSchemaOutput schema type
InInferred input type
OutInferred output type

Properties

PropertyTypeDescription
metadataJobMetadataJob metadata (name, description, retry, etc.)
inputInParsed and validated input
outputOut | undefinedJob output (after execution)
jobNamestringJob name
jobIdstringJob ID (or name if not specified)

Abstract Method

execute(input)

The main execution method that must be implemented.
abstract execute(input: In): Promise<Out>
@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.
respond(value: Out): never
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.
protected log(message: string): void
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.
getLogs(): readonly string[]
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.
protected async progress(
  pct: number,
  total?: number,
  msg?: string
): Promise<boolean>
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).
get attempt(): number
async execute(input) {
  this.log(`Attempt ${this.attempt}`);
  if (this.attempt > 1) {
    this.log('Retrying after previous failure');
  }
  // ...
}

Inherited Methods

From ExecutionContextBase:
MethodDescription
get(token)Resolve a required dependency
tryGet(token)Resolve an optional dependency (returns undefined if not found)
scopeAccess the current scope
loggerScoped 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

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 };
  }
}

@Job

Job decorator

ExecutionContextBase

Base class

JobRegistry

Job registry

Jobs Guide

Jobs documentation